精华内容
下载资源
问答
  • 设计模式 | 组合模式及典型应用

    千次阅读 多人点赞 2018-10-05 17:36:55
    介绍组合模式 示例 组合模式总结 源码分析组合模式的典型应用 java.awt中的组合模式 Java集合中的组合模式 Mybatis SqlNode中的组合模式 更多内容可访问我的个人博客:http://laijianfeng.org 推荐阅读 设计...

    本文的主要内容:

    • 介绍组合模式
    • 示例
    • 组合模式总结
    • 源码分析组合模式的典型应用
      • java.awt中的组合模式
      • Java集合中的组合模式
      • Mybatis SqlNode中的组合模式

    组合模式

    树形结构不论在生活中或者是开发中都是一种非常常见的结构,一个容器对象(如文件夹)下可以存放多种不同的叶子对象或者容器对象,容器对象与叶子对象之间属性差别可能非常大。

    由于容器对象和叶子对象在功能上的区别,在使用这些对象的代码中必须有区别地对待容器对象和叶子对象,而实际上大多数情况下我们希望一致地处理它们,因为对于这些对象的区别对待将会使得程序非常复杂。

    一个简化的Linux目录树

    组合模式为解决此类问题而诞生,它可以让叶子对象和容器对象的使用具有一致性

    组合模式(Composite Pattern):组合多个对象形成树形结构以表示具有 “整体—部分” 关系的层次结构。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性,组合模式又可以称为 “整体—部分”(Part-Whole) 模式,它是一种对象结构型模式。

    由于在软件开发中存在大量的树形结构,因此组合模式是一种使用频率较高的结构型设计模式,Java SE中的AWT和Swing包的设计就基于组合模式。

    除此以外,在XML解析、组织结构树处理、文件系统设计等领域,组合模式都得到了广泛应用。

    角色

    Component(抽象构件):它可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。

    Leaf(叶子构件):它在组合结构中表示叶子节点对象,叶子节点没有子节点,它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法,可以通过异常等方式进行处理。

    Composite(容器构件):它在组合结构中表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子节点的业务方法。

    组合模式的关键是定义了一个抽象构件类,它既可以代表叶子,又可以代表容器,而客户端针对该抽象构件类进行编程,无须知道它到底表示的是叶子还是容器,可以对其进行统一处理。同时容器对象与抽象构件类之间还建立一个聚合关联关系,在容器对象中既可以包含叶子,也可以包含容器,以此实现递归组合,形成一个树形结构。

    示例

    我们来实现一个简单的目录树,有文件夹和文件两种类型,首先需要一个抽象构件类,声明了文件夹类和文件类需要的方法

    public abstract class Component {
    
        public String getName() {
            throw new UnsupportedOperationException("不支持获取名称操作");
        }
    
        public void add(Component component) {
            throw new UnsupportedOperationException("不支持添加操作");
        }
    
        public void remove(Component component) {
            throw new UnsupportedOperationException("不支持删除操作");
        }
    
        public void print() {
            throw new UnsupportedOperationException("不支持打印操作");
        }
    
        public String getContent() {
            throw new UnsupportedOperationException("不支持获取内容操作");
        }
    }
    

    实现一个文件夹类 Folder,继承 Component,定义一个 List<Component> 类型的componentList属性,用来存储该文件夹下的文件和子文件夹,并实现 getName、add、remove、print等方法

    public class Folder extends Component {
        private String name;
        private List<Component> componentList = new ArrayList<Component>();
    
        public Folder(String name) {
            this.name = name;
        }
    
        @Override
        public String getName() {
            return this.name;
        }
    
        @Override
        public void add(Component component) {
            this.componentList.add(component);
        }
    
        @Override
        public void remove(Component component) {
            this.componentList.remove(component);
        }
    
        @Override
        public void print() {
            System.out.println(this.getName());
            for (Component component : this.componentList) {
                component.print();
            }
        }
    }
    

    文件类 File,继承Component父类,实现 getName、print、getContent等方法

    public class File extends Component {
        private String name;
        private String content;
    
        public File(String name, String content) {
            this.name = name;
            this.content = content;
        }
    
        @Override
        public String getName() {
            return this.name;
        }
    
        @Override
        public void print() {
            System.out.println(this.getName());
        }
    
        @Override
        public String getContent() {
            return this.content;
        }
    }
    

    我们来测试一下

    public class Test {
        public static void main(String[] args) {
            Folder DSFolder = new Folder("设计模式资料");
            File note1 = new File("组合模式笔记.md", "组合模式组合多个对象形成树形结构以表示具有 \"整体—部分\" 关系的层次结构");
            File note2 = new File("工厂方法模式.md", "工厂方法模式定义一个用于创建对象的接口,让子类决定将哪一个类实例化。");
            DSFolder.add(note1);
            DSFolder.add(note2);
    
            Folder codeFolder = new Folder("样例代码");
            File readme = new File("README.md", "# 设计模式示例代码项目");
            Folder srcFolder = new Folder("src");
            File code1 = new File("组合模式示例.java", "这是组合模式的示例代码");
    
            srcFolder.add(code1);
            codeFolder.add(readme);
            codeFolder.add(srcFolder);
            DSFolder.add(codeFolder);
    
            DSFolder.print();
        }
    }
    

    输出结果

    设计模式资料
    组合模式笔记.md
    工厂方法模式.md
    样例代码
    README.md
    src
    组合模式示例.java
    

    输出正常,不过有个小问题,从输出看不出它们的层级结构,为了体现出它们之间的层级关系,我们需要改造一下 Folder 类,增加一个 level 属性,并修改 print 方法

    public class Folder extends Component {
        private String name;
        private List<Component> componentList = new ArrayList<Component>();
        public Integer level;
    
        public Folder(String name) {
            this.name = name;
        }
    
        @Override
        public String getName() {
            return this.name;
        }
    
        @Override
        public void add(Component component) {
            this.componentList.add(component);
        }
    
        @Override
        public void remove(Component component) {
            this.componentList.remove(component);
        }
    
        @Override
        public void print() {
            System.out.println(this.getName());
            if (this.level == null) {
                this.level = 1;
            }
            String prefix = "";
            for (int i = 0; i < this.level; i++) {
                prefix += "\t- ";
            }
            for (Component component : this.componentList) {
                if (component instanceof Folder){
                    ((Folder)component).level = this.level + 1;
                }
                System.out.print(prefix);
                component.print();
            }
            this.level = null;
        }
    }
    

    现在的输出就有相应的层级结构了

    设计模式资料
    	- 组合模式笔记.md
    	- 工厂方法模式.md
    	- 样例代码
    	- 	- README.md
    	- 	- src
    	- 	- 	- 组合模式示例.java
    

    我们可以画出它们之间的类图

    示例.组合模式类图

    在这里父类 Component 是一个抽象构件类,Folder 类是一个容器构件类,File 是一个叶子构件类,Folder 和 File 继承了 Component,Folder 与 Component 又是聚合关系

    透明与安全

    在使用组合模式时,根据抽象构件类的定义形式,我们可将组合模式分为透明组合模式和安
    全组合模式两种形式。

    透明组合模式

    透明组合模式中,抽象构件角色中声明了所有用于管理成员对象的方法,譬如在示例中 Component 声明了 addremove 方法,这样做的好处是确保所有的构件类都有相同的接口。透明组合模式也是组合模式的标准形式。

    透明组合模式的缺点是不够安全,因为叶子对象和容器对象在本质上是有区别的,叶子对象不可能有下一个层次的对象,即不可能包含成员对象,因此为其提供 add()remove() 等方法是没有意义的,这在编译阶段不会出错,但在运行阶段如果调用这些方法可能会出错(如果没有提供相应的错误处理代码)

    安全组合模式

    在安全组合模式中,在抽象构件角色中没有声明任何用于管理成员对象的方法,而是在容器构件 Composite 类中声明并实现这些方法。

    安全组合模式模式图

    安全组合模式的缺点是不够透明,因为叶子构件和容器构件具有不同的方法,且容器构件中那些用于管理成员对象的方法没有在抽象构件类中定义,因此客户端不能完全针对抽象编程,必须有区别地对待叶子构件和容器构件。

    在实际应用中 java.awtswing 中的组合模式即为安全组合模式。

    组合模式总结

    组合模式的主要优点如下:

    • 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
    • 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。
    • 在组合模式中增加新的容器构件和叶子构件都很方便,无须对现有类库进行任何修改,符合“开闭原则”。
    • 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子对象和容器对象的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。

    组合模式的主要缺点如下:

    • 使得设计更加复杂,客户端需要花更多时间理清类之间的层次关系。
    • 在增加新构件时很难对容器中的构件类型进行限制。

    适用场景

    • 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待它们。
    • 在一个使用面向对象语言开发的系统中需要处理一个树形结构。
    • 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型。

    源码分析组合模式的典型应用

    java.awt中的组合模式

    Java GUI分两种:

    • AWT(Abstract Window Toolkit):抽象窗口工具集,是第一代的Java GUI组件。绘制依赖于底层的操作系统。基本的AWT库处理用户界面元素的方法是把这些元素的创建和行为委托给每个目标平台上(Windows、 Unix、 Macintosh等)的本地GUI工具进行处理。

    • Swing,不依赖于底层细节,是轻量级的组件。现在多是基于Swing来开发。

    我们来看一个AWT的简单示例:

    注意:为了正常显示中文,需要在IDEA中的 Edit Configurations -> VM Options 中设置参数 -Dfile.encoding=GB18030

    import java.awt.*;
    import java.awt.event.WindowAdapter;
    import java.awt.event.WindowEvent;
    
    public class MyFrame extends Frame {
    
        public MyFrame(String title) {
            super(title);
        }
    
        public static void main(String[] args) {
            MyFrame frame = new MyFrame("这是一个 Frame");
    
            // 定义三个构件,添加到Frame中去
            Button button = new Button("按钮 A");
            Label label = new Label("这是一个 AWT Label!");
            TextField textField = new TextField("这是一个 AWT TextField!");
    
            frame.add(button, BorderLayout.EAST);
            frame.add(label, BorderLayout.SOUTH);
            frame.add(textField, BorderLayout.NORTH);
    
            // 定义一个 Panel,在Panel中添加三个构件,然后再把Panel添加到Frame中去
            Panel panel = new Panel();
            panel.setBackground(Color.pink);
    
            Label lable1 = new Label("用户名");
            TextField textField1 = new TextField("请输入用户名:", 20);
            Button button1 = new Button("确定");
            panel.add(lable1);
            panel.add(textField1);
            panel.add(button1);
    
            frame.add(panel, BorderLayout.CENTER);
    
            // 设置Frame的属性
            frame.setSize(500, 300);
            frame.setBackground(Color.orange);
            // 设置点击关闭事件
            frame.addWindowListener(new WindowAdapter() {
                @Override
                public void windowClosing(WindowEvent e) {
                    System.exit(0);
                }
            });
            frame.setVisible(true);
        }
    }
    

    运行后窗体显示如下

    示例.AWT绘制窗体

    我们在Frame容器中添加了三个不同的构件 ButtonLabelTextField,还添加了一个 Panel 容器,Panel 容器中又添加了 ButtonLabelTextField 三个构件,为什么容器 FramePanel 可以添加类型不同的构件和容器呢?

    我们先来看下AWT Component的类图

    AWT Component类图

    GUI组件根据作用可以分为两种:基本组件和容器组件。

    • 基本组件又称构件,诸如按钮、文本框之类的图形界面元素。
    • 容器是一种比较特殊的组件,可以容纳其他组件,容器如窗口、对话框等。所有的容器类都是 java.awt.Container 的直接或间接子类

    容器父类 Container 的部分代码如下

    public class Container extends Component {
        /**
         * The components in this container.
         * @see #add
         * @see #getComponents
         */
        private java.util.List<Component> component = new ArrayList<>();
        
        public Component add(Component comp) {
            addImpl(comp, null, -1);
            return comp;
        }
        // 省略...
    }
    

    容器父类 Container 内部定义了一个集合用于存储 Component 对象,而容器组件 Container 和 基本组件如 ButtonLabelTextField 等都是 Component 的子类,所以可以很清楚的看到这里应用了组合模式

    Component 类中封装了组件通用的方法和属性,如图形的组件对象、大小、显示位置、前景色和背景色、边界、可见性等,因此许多组件类也就继承了 Component 类的成员方法和成员变量,相应的成员方法包括:

       getComponentAt(int x, int y)
       getFont()
       getForeground()
       getName()
       getSize()
       paint(Graphics g)
       repaint()
       update()
       setVisible(boolean b)
       setSize(Dimension d)
       setName(String name)
    

    Java集合中的组合模式

    HashMap 提供 putAll 的方法,可以将另一个 Map 对象放入自己的存储空间中,如果有相同的 key 值则会覆盖之前的 key 值所对应的 value 值

    public class Test {
        public static void main(String[] args) {
            Map<String, Integer> map1 = new HashMap<String, Integer>();
            map1.put("aa", 1);
            map1.put("bb", 2);
            map1.put("cc", 3);
            System.out.println("map1: " + map1);
    
            Map<String, Integer> map2 = new LinkedMap();
            map2.put("cc", 4);
            map2.put("dd", 5);
            System.out.println("map2: " + map2);
    
            map1.putAll(map2);
            System.out.println("map1.putAll(map2): " + map1);
        }
    }
    

    输出结果

    map1: {aa=1, bb=2, cc=3}
    map2: {cc=4, dd=5}
    map1.putAll(map2): {aa=1, bb=2, cc=4, dd=5}
    

    查看 putAll 源码

        public void putAll(Map<? extends K, ? extends V> m) {
            putMapEntries(m, true);
        }
    

    putAll 接收的参数为父类 Map 类型,所以 HashMap 是一个容器类,Map 的子类为叶子类,当然如果 Map 的其他子类也实现了 putAll 方法,那么它们都既是容器类,又都是叶子类

    同理,ArrayList 中的 addAll(Collection<? extends E> c) 方法也是一个组合模式的应用,在此不做探讨

    Mybatis SqlNode中的组合模式

    MyBatis 的强大特性之一便是它的动态SQL,其通过 if, choose, when, otherwise, trim, where, set, foreach 标签,可组合成非常灵活的SQL语句,从而提高开发人员的效率。

    来几个官方示例:

    动态SQL – IF

    <select id="findActiveBlogLike"  resultType="Blog">
      SELECT * FROM BLOG WHERE state = ‘ACTIVE’ 
      <if test="title != null">
        AND title like #{title}
      </if>
      <if test="author != null and author.name != null">
        AND author_name like #{author.name}
      </if>
    </select>
    

    动态SQL – choose, when, otherwise

    <select id="findActiveBlogLike"  resultType="Blog">
      SELECT * FROM BLOG WHERE state = ‘ACTIVE’
      <choose>
        <when test="title != null">
          AND title like #{title}
        </when>
        <when test="author != null and author.name != null">
          AND author_name like #{author.name}
        </when>
        <otherwise>
          AND featured = 1
        </otherwise>
      </choose>
    </select>
    

    动态SQL – where

    <select id="findActiveBlogLike"  resultType="Blog">
      SELECT * FROM BLOG 
      <where> 
        <if test="state != null">
             state = #{state}
        </if> 
        <if test="title != null">
            AND title like #{title}
        </if>
        <if test="author != null and author.name != null">
            AND author_name like #{author.name}
        </if>
      </where>
    </select>
    

    动态SQL – foreach

    <select id="selectPostIn" resultType="domain.blog.Post">
      SELECT * FROM POST P WHERE ID in
      <foreach item="item" index="index" collection="list"
          open="(" separator="," close=")">
            #{item}
      </foreach>
    </select>
    

    Mybatis在处理动态SQL节点时,应用到了组合设计模式,Mybatis会将映射配置文件中定义的动态SQL节点、文本节点等解析成对应的 SqlNode 实现,并形成树形结构。

    SQLNode 的类图如下所示

    Mybatis SqlNode 类图

    需要先了解 DynamicContext 类的作用:主要用于记录解析动态SQL语句之后产生的SQL语句片段,可以认为它是一个用于记录动态SQL语句解析结果的容器

    抽象构件为 SqlNode 接口,源码如下

    public interface SqlNode {
      boolean apply(DynamicContext context);
    }
    

    applySQLNode 接口中定义的唯一方法,该方法会根据用户传入的实参,参数解析该SQLNode所记录的动态SQL节点,并调用 DynamicContext.appendSql() 方法将解析后的SQL片段追加到 DynamicContext.sqlBuilder 中保存,当SQL节点下所有的 SqlNode 完成解析后,我们就可以从 DynamicContext 中获取一条动态生产的、完整的SQL语句

    然后来看 MixedSqlNode 类的源码

    public class MixedSqlNode implements SqlNode {
      private List<SqlNode> contents;
    
      public MixedSqlNode(List<SqlNode> contents) {
        this.contents = contents;
      }
    
      @Override
      public boolean apply(DynamicContext context) {
        for (SqlNode sqlNode : contents) {
          sqlNode.apply(context);
        }
        return true;
      }
    }
    

    MixedSqlNode 维护了一个 List<SqlNode> 类型的列表,用于存储 SqlNode 对象,apply 方法通过 for循环 遍历 contents 并调用其中对象的 apply 方法,这里跟我们的示例中的 Folder 类中的 print 方法非常类似,很明显 MixedSqlNode 扮演了容器构件角色

    对于其他SqlNode子类的功能,稍微概括如下:

    • TextSqlNode:表示包含 ${} 占位符的动态SQL节点,其 apply 方法会使用 GenericTokenParser 解析 ${} 占位符,并直接替换成用户给定的实际参数值
    • IfSqlNode:对应的是动态SQL节点 <If> 节点,其 apply 方法首先通过 ExpressionEvaluator.evaluateBoolean() 方法检测其 test 表达式是否为 true,然后根据 test 表达式的结果,决定是否执行其子节点的 apply() 方法
    • TrimSqlNode :会根据子节点的解析结果,添加或删除相应的前缀或后缀。
    • WhereSqlNodeSetSqlNode 都继承了 TrimSqlNode
    • ForeachSqlNode:对应 <foreach> 标签,对集合进行迭代
    • 动态SQL中的 <choose><when><otherwise> 分别解析成 ChooseSqlNodeIfSqlNodeMixedSqlNode

    综上,SqlNode 接口有多个实现类,每个实现类对应一个动态SQL节点,其中 SqlNode 扮演抽象构件角色,MixedSqlNode 扮演容器构件角色,其它一般是叶子构件角色

    参考:
    刘伟:设计模式Java版
    慕课网java设计模式精讲 Debug 方式+内存分析
    Java AWT基础及布局管理
    【java源码一带一路系列】之HashMap.putAll()
    徐郡明:Mybatis技术内幕 3.2 SqlNode&SqlSource
    Mybatis 3.4.7 文档:动态 SQL

    更多内容可访问我的个人博客:http://laijianfeng.org
    关注【小旋锋】微信公众号

    推荐阅读

    设计模式 | 简单工厂模式及典型应用
    设计模式 | 工厂方法模式及典型应用
    设计模式 | 抽象工厂模式及典型应用
    设计模式 | 建造者模式及典型应用
    设计模式 | 原型模式及典型应用
    设计模式 | 外观模式及典型应用
    设计模式 | 装饰者模式及典型应用
    设计模式 | 适配器模式及典型应用
    设计模式 | 享元模式及典型应用

    展开全文
  • S.M.A.R.T.

    千次阅读 2012-06-26 15:45:02
    S.M.A.R.T.,全称为“Self-Monitoring Analysis and Reporting Technology”,即“自我监测、分析及报告技术”。是一种自动的硬盘状态检测与预警系统和规范。通过在硬盘硬件的检测指令对硬盘的硬件如磁头、盘片、...

    S.M.A.R.T.,全称为“Self-Monitoring Analysis and Reporting Technology”,即“自我监测、分析及报告技术”。是一种自动的硬盘状态检测与预警系统和规范。通过在硬盘硬件内的检测指令对硬盘的硬件如磁头、盘片、马达、电路的运行情况进行监控、记录并与厂商所设定的预设安全值进行比较,若监控情况将或已超出预设安全值的安全范围,就可以通过主机的监控硬件或软件自动向用户作出警告并进行轻微的自动修复,以提前保障硬盘数据的安全。除一些出厂时间极早的硬盘外,现在大部分硬盘均配备该项技术。

    目录

       [隐藏

    [编辑]历史

    最早期的硬盘监控技术起源于1992年IBM在为AS/400计算机的IBM9337硬盘阵列中的IBM 0662 SCSI2代硬盘驱动器之中,后来该技术被命名为Predictive Failure Analysis(故障预警分析技术),它是通过在固件中测量几个重要的硬盘安全参数和评估他们的情况。从物理硬盘发送到监控软件的结果中被限定两种结果:“硬盘安全”和“硬盘不久后会发生故障”。

    不久,由微机制造商Compaq和硬盘制造商SeagateQuantumConner提出了名为IntelliSafe的类似技术。通过该技术,硬盘会测量自身的的健康指标并将参量值传送给操作系统和用户的监控软件中,每个硬盘生产商有权决定哪些指标需要被监控和它们的安全阈值。

    Compaq于1995早期将该项技术方案提交到Small Form Factor委员会进行标准化,该方案得到IBMSeagateQuantumConnerWestern Digital所支持。由于IntelliSafe技术的灵活性,委员会接受了该方案,并正式更名S.M.A.R.T.技术,将其标准化并推广至ATA-3行业标准中。

    [编辑]运作原理

    该技术所需数据被存放在硬盘物理盘面最前面的磁道中,由硬盘制作商将相关管理程序和数据该磁道中,包括加解密程序,自监控程序,自修复程序等,主机的监控软件可以通过“SMART RETURN STATUS”的命令读取S.M.A.R.T.信息,且这些信息不允许被用户修改。

    [编辑]检测属性

    下面将列出一些S.M.A.R.T.的原始检测属性和含义。普遍为检测值越高性能越好。即使所有制造商都必须遵守共同的规则,但由于有些检测值在不同硬盘制造商中用不全相同的定义和计量方法而对于不同制作商来说检测值不全是越高越好,所以下面属性的指标只作一般参考。除外,各制造商也会根据自己需要添加一些自己专有的检测属性

    说明
    Dark Green Arrow Up.svg
    表示数值越高越好
    Dark Green Arrow Down.svg
    表示数值越低越好
    重要项:红色底 当超出安全范围会对性能严重影响
    ID ID十六进制值 英文名 中文译名 最优 说明
    1 0x01 read error rate 底层数据读取错误率
    Dark Green Arrow Down.svg
    存储器从一个硬盘表面读取数据时发生的错误率。原始值由于不同厂商的不同计算方法而有所不同,其十进制值往往无意义的。一般来说有数值意味着磁头已出现问题了。
    2 0x02 Throughput Performance 读写通量性能
    Dark Green Arrow Up.svg
    通常是硬盘读写性能的测量值,如果其值有变动,有可能硬盘出现了问题。
    3 0x03 Spin-Up Time 盘片启动时间
    Dark Green Arrow Down.svg
    盘片由静止启动加速到稳定正常运行速度的平均所需时间。
    4 0x04 Start/Stop Count 电机起停次计数
    Dark Green Arrow Down.svg
    一个盘片启动关闭周期的统计值,只有硬盘从完全断电中启动或从睡眠模式恢复,盘片主轴电机被启动时才会记一次数。
    5 0x05 Reallocated Sector Count 重定位磁区计数
    Dark Green Arrow Down.svg
    记录由于损坏而被映射到无损的后备区的磁區计数。当硬盘出现损坏磁區时,可以通过将其物理空间指向到特定的无损区域进行重映射修复,从而出现坏磁區的硬盘仍可使用。但当高过一定数值后,后磁區消耗殆尽而无法再重映射修复时,这些坏磁區就会显现出来且无法自行修复。除外由于要要求磁头读取这些坏磁區时专门再移动到后备区读写数据,对硬盘读写性能也有影响。
    6 0x06 Read Channel Margin 信道读取余量   读取数据时信道可用的余量,该属性没制定任何功用。
    7 0x07 Seek Error Rate 寻道错误率   (该属性是特定制造商才有的)磁头寻找磁道由于机械问题而出错几率,有多种原因可能引致出错,如:磁头伺服构件,盘体过热,或损坏。于不同厂商的不同计算方法而有所不同,其十进制值往往无意义的。
    8 0x08 Seek Time Performance 寻道性能
    Dark Green Arrow Up.svg
    每次寻道时间的平均值,该值短期内迅速减少,有可能硬盘出现了问题。
    9 0x09 Power-On Hours 硬盘加电时间
    Dark Green Arrow Down.svg
    硬盘自出厂以来加电启动的统计时间,单位为小时(或根据制造商设定为分钟或秒),一般用户以该值判定硬盘是否被使用过。
    10 0x0a Spin Retry Count 电机起转重试
    Dark Green Arrow Down.svg
    主轴电机在一次加速至正常速度失败后尝试重新继续加速到正常运行速度的统计数,该值改变时意味着硬盘的机械部件已经出现问题了。
    11 0x0b Recalibration Retries 磁头校准重试
    Dark Green Arrow Down.svg
    磁头在一次运行失败时尝试校准至正常状态的统计数,该值改变时意味着硬盘的机械部件已经出现问题了。
    12 0x0c Power Cycle Count 设备开关计数   该属性表示硬盘电源充分开/关循环计数。
    13 0x0d Soft Read Error Rate 软件读取错误率
    Dark Green Arrow Down.svg
    操作系统读取数据时的出错率。
    183 0xb7 SATA Downshift Error Count SATA降级运行计数   Western Digital 和 Samsung 特有属性,记录由于兼容问题导致降低SATA传输级别运行的计数。
    184 0xb8 End-to-End error 终端校验出错
    Dark Green Arrow Down.svg
    HP专有S.M.A.R.T.(SMART IV)技术的一个特有属性,记录硬盘从盘片读取数据到高速缓存后再传输到主机时数据校验出错的次数。
    185 0xb9 Head Stability 磁头稳定性   Western Digital特有属性
    186 0xba Induced Op-Vibration Detection     Western Digital特有属性
    187 0xbb Reported Uncorrectable Errors 报告不可纠正错误
    Dark Green Arrow Down.svg
    硬件ECC无法恢复的错误计数。
    188 0xbc Command Timeout 通信超时
    Dark Green Arrow Down.svg
    由于无法连接至硬盘而终止操作的统计数,一般为0,如果远超过0,则可能电源问题,数据线接口氧化或更严重的问题。
    189 0xbd High Fly Writes 磁头写入高度
    Dark Green Arrow Down.svg
    硬盘进行写入时对磁头高度进行监控以提供额外的保障。当磁头处于不正常高度进行写入时,写入操作会被终止,原有数据重写入或者将该扇区重映射到安全区域。该属性是统计值。
    190 0xbe Airflow Temperature 气流温度
    Dark Green Arrow Down.svg
    Western Digital特有属性,计量硬盘内气流温度,和检测项0xc2相似。
    191 0xbf G-sense Error Rate 加速度错误率
    Dark Green Arrow Down.svg
    计量可能对硬盘做成损害的冲击次数。
    192 0xc0 Power-off Retract Count 电源关闭磁头收回计数
    Dark Green Arrow Down.svg
    计量磁头在没有加电时不移进硬盘的值。
    193 0xc1 Load Cycle Count 磁头升降计数
    Dark Green Arrow Down.svg
    计量磁头在加电时移进/移出硬盘周期的值。
    194 0xc2 Temperature 温度
    Dark Green Arrow Down.svg
    计量硬盘的温度
    195 0xc3 Hardware ECC Recovered 硬件ECC恢复   (特定原始值)
    196 0xc4 Reallocation Event Count 重定位事件计数
    Dark Green Arrow Down.svg
    记录已重映射扇区和可能重映射扇区的事件计数。
    197 0xc5 Current Pending Sector Count 等候重定的扇区计数
    Dark Green Arrow Down.svg
    记录了不稳定的扇区的数量。
    198 0xc6 Uncorrectable Sector Count 无法校正的扇区计数
    Dark Green Arrow Down.svg
    记录肯定出错的扇区数量。
    199 0xc7 UltraDMA CRC Error Count UltraDMA通讯CRC错误
    Dark Green Arrow Down.svg
    记录硬盘通讯时发生的CRC错误。
    200 0xc8 Multi-Zone Error Rate 多区域错误率
    Dark Green Arrow Down.svg
    写入一个区域时发现的错误的计数。
    200 0xc8 Write Error Rate 写入错误率
    Dark Green Arrow Down.svg
    Fujitsu的特别属性,写入一个区域时发现的错误的计数。
    201 0xc9 Soft Read Error Rate 逻辑读取错误率
    Dark Green Arrow Down.svg
    记录脱轨错误。
    202 0xca Data Address Mark errors 数据地址标记错误
    Dark Green Arrow Down.svg
    记录数据地址标记错误(或制造商特定的计数)
    203 0xcb Run Out Cancel 用完取消
    Dark Green Arrow Down.svg
    ECC错误计数
    204 0xcc Soft ECC Correction 逻辑ECC纠正
    Dark Green Arrow Down.svg
    记录由软件ECC更正的错误计数。
    205 0xcd Thermal Asperity Rate 热嘈率
    Dark Green Arrow Down.svg
    记录高温导致的出错记数。
    206 0xce Flying Height 飞行高度   记录磁头的飞行高度。飞得太低会增加磁头撞毁的机会,飞得太高增加读写错误的机会。
    207 0xcf Spin High Current 主轴电机浪涌电流计数
    Dark Green Arrow Down.svg
    记录主轴电机运转时浪涌电流的次数。
    208 0xd0 Spin Buzz     记录由于电力不足而启动主轴电机的蜂鸣声次数。
    209 0xd1 Offline Seek Performance 离线寻址效能   在其内部测试硬盘的寻址能力表现。
    210 0xd2   (没定性,出现在Maxtor 6B200M0 200GB 和Maxtor 2R015H1 15GB 的硬盘中)
    211 0xd3 Vibration During Write 写操作震动   记录写入操作的震动数。
    212 0xd4 Shock During Write 写操作冲击   记录写入操作时的冲击数。
    220 0xdc Disk Shift 盘体偏移
    Dark Green Arrow Down.svg
    记录盘体由于冲击或温度导致偏离主轴的相对距离。
    221 0xdd G-Sense Error Rate 加速计出错率
    Dark Green Arrow Down.svg
    从外部诱发的冲击和振动产生的错误计数。
    222 0xde Loaded Hours 数据加载时间   数据读取时所花费的时间。(磁头移动时间)
    223 0xdf Load/Unload Retry Count 加载/卸载重试次数   磁头改变位置时所需时间。
    224 0xe0 Load Friction 负载摩擦
    Dark Green Arrow Down.svg
    读写时由于机械摩擦做成的阻力。
    225 0xe1 Load/Unload Cycle Count 加载/卸载循环计数
    Dark Green Arrow Down.svg
    总负载周期计数。
    226 0xe2 Load 'In'-time 磁头   磁头加载所需总时间(不包括在停泊区的花费)。
    227 0xe3 Torque Amplification Count 扭矩放大计数
    Dark Green Arrow Down.svg
    尝试来补偿盘片的速度变化的计数。
    228 0xe4 Power-Off Retract Cycle 断电缩回周期
    Dark Green Arrow Down.svg
    切断电源后电磁枢自动缩回的时间计数。
    230 0xe6 GMR Head Amplitude GMR磁头振幅   磁头振幅计数(磁头反复正反向运动距离)。
    231 0xe7 Temperature 硬盘温度
    Dark Green Arrow Down.svg
    记录硬盘温度。
    232 0xe8 Endurance Remaining 耐久性剩余   磁盘可使用周期与设计可使用周期的百分比。
    232 0xe8 Available Reserved Space 可用保留空间   Intel固态硬盘报告的可提供的预留空间占作为一支全新的固态硬盘预留空间的百分比。
    233 0xe9 Power-On Hours 加电时间   处于开机状态的小时数。
    233 0xe9 Media Wearout Indicator 介质耗损指标   Intel固态硬盘报告的NAND刷写寿命,全新时值为100,最低值为1,其跌幅随NAND的擦除周期增加而在0到最大额定周期范围减少。
    240 0xf0 Head Flying Hours 磁头飞行时间   磁头处于定位中的时间。
    240 0xf0 Transfer Error Rate 传输错误率   在数据传输时连接被重置的次数计数。(Fujitsu特有属性)
    241 0xf1 Total LBAs Written LBA写入总数   LBA写入总数计数。
    242 0xf2 Total LBAs Read LBA读取总数   LBA读取总数计数,部分S.M.A.R.T.检测程序会把原始值显示为负数,这是因为该原始值为48位,而不是32位的。
    250 0xfa Read Error Retry Rate 读取错误重试率
    Dark Green Arrow Down.svg
    从磁盘读取时的错误计数。
    254 0xfe Free Fall Protection 自由跌落保护
    Dark Green Arrow Down.svg
    对“自由落体事件”检测计数。

    [编辑]在非ATA平台上的实现

    [编辑]SCSI

    硬盘的传输端口平台主要分为ATA和SCSI两个平台。作为一种硬盘的检测技术,理论上都能在两个平台上实现的,但由于两个平台也存在巨大的不同,S.M.A.R.T.在SCSI上的实现和在ATA的实现上也有所不同。首先,作为ATA上的专有规范,S.M.A.R.T.对ATA系统的干预要比SCSI更明显,S.M.A.R.T.对SCSI更多是起到检测的作用,即使在检测到磁盘有故障时,其只是报告监控端,要人为地处理故障。其次,由于SCSI平台的硬盘比ATA的更为复杂,所以其检测属性也比ATA的多和复杂准确,如包括对盘片和驱动电路版的温度检测(ATA多只对盘片温度检测),对电压的检测等。

    [编辑]USB

    USB标准中,USB不能用于计算机内部储存设备的基本总线(如ATA,SCSI等),其本身没有为S.M.A.R.T.提供传输数据的途径。在使用ATA硬盘,以USB为传输端口的移动硬盘中,即使硬盘内S.M.A.R.T.仍然运作,但没办法直接向系统提供S.M.A.R.T.的数据。现在新的移动硬盘的内部驱动转换电路已经能以一些方法将硬盘内S.M.A.R.T.的数据通过USB传输到系统或监控程序中读取。

    展开全文
  • 研华USB4711A采集卡是一款USB接口的

    研华USB4711A采集卡是一款USB接口的采集卡,笔记本上就可以用,不用专门要ISA或者PCI插槽,价格上倒是比较贵,AI最多可以到150KS/s。这里记录高速中断模式采集使用方法。

    windows平台:xp sp3 

    框架:vs2010 MFC 单文档

    采集速度:4通道,每通道200HZ

    采集模式:使用FIFO

    ==============================================================

    1. 安装研华设备管理器、4711驱动,再装下研华光盘给的MFC例子,里面AD_INT和MAD_INT是官方给的高速采集demo。
    2. 将研华给的头文件及lib库文件添加到工程文件夹里。本站下载在这里工程属性-linker-input-additinal dependencies里添加adsapi32.lib,工程属性-linker-general-additinal library dependencies里添加adsapi32.lib所在目录,工程.h文件里包含头文件#include "Driver.h"
    3. .h文件里添加变量声明
      LRESULT					m_ErrCode;  
      	LONG					m_DriverHandle;
      	ULONG					m_dwDeviceNum;  
      	DEVFEATURES				m_DevFeatures;
      	ULONG					m_gwActualBufSize;
      	USHORT					m_GainCode;
      	USHORT					m_gwActiveBuf;		// return by FAICheck
      	USHORT					m_gwOverrun;		// return by FAICheck, FAITransfer
      	USHORT					m_gwStopped;		// return by FAICheck
      	ULONG					m_gulRetrieved;     // return by FAICheck
      	USHORT					m_gwHalfReady;		// return by FAICheck
      	PT_DeviceGetFeatures	m_ptDevFeatures;
      	//	PT_FAIIntStart			m_ptFAIIntStart;     // FAIIntStart table
      	PT_FAIIntScanStart		m_ptFAIIntScanStart; // m_ptFAIIntScanStart table
      
      	PT_FAITransfer			m_ptFAITransfer;     // FAITransfer table
      	PT_FAICheck				m_ptFAICheck;        // FAICheck table
      	PT_AllocateDMABuffer	m_ptAllocateDMABuffer;  // buffer table
      	PT_EnableEvent			m_ptEnableEvent;     // Enable event
      	PT_CheckEvent			m_ptCheckEvent;      // Check event
      	USHORT					m_CyclicCount;
      	CWinThread*				m_pEventThread;
      	HGLOBAL					m_hUserBuf;
      	HGLOBAL					m_hBuf;
      	ULONG					m_ulInterruptCount;
      	ULONG					m_ulOverRunCount;
      	ULONG					m_ulBuffCount;
      	//OpenEvent dialog get follow par
      	bool	m_bGetParOk;
      	//int		m_GainOption;
      	USHORT	m_Gain[4];
      	int		m_Model;
      	int		m_Triggering;
      	int		m_Buffer;
      	int		m_DataType;
      	int		m_Event;
      	int		m_PacerRate;
      	int		m_Conv;
      	CString	m_InputRange;
      	int		m_ScanChannel;
      	int		m_FifoSize;
      	BOOL	m_EnableFifo;
      
      	LPVOID temp;
      	void adInterruptEvent();
      	void adBufChangeEvent();
      	void adOverrunEvent();
      	void adTerminateEvent();

    4. .cpp的构造函数里初始化参数,这里直接设定了,没用自带demo从对话框配置参数。
      m_gwActualBufSize	= 0;
      		m_ulBuffCount		= 0;
      		m_ulInterruptCount	= 0;
      		m_ulOverRunCount	= 0;
      
      
      		m_Conv			= 128 ;			//单次采集个数
      		m_Gain[0]		= 4 ;			//增益选项 10v-4   5v-0 
      		m_Gain[1]		= 0 ;
      		m_Gain[2]		= 0 ;
      		m_Gain[3]		= 0 ;
      
      		m_Model			= 1 ;			//是否循环模式
      		m_Triggering	= 0 ;			//内触发模式
      		m_Buffer		= 0 ;			
      		m_DataType		= 1 ;			//0-二进制值  1-电压值			
      		m_dwDeviceNum	= 0 ;			//管理器里设备编号
      		m_Event			= 1 ;			//事件使能
      		m_GainCode		= 0 ;			//内触发模式
      		m_PacerRate		= 800 ;			//采样频率单位 HZ  USB4711A form 1 Hz  to 150 kHz  但是是8个通道累积达到单通道最高150/8
      		m_InputRange	= _T("") ;		//
      
      		m_FifoSize		= 64 ;			//设定FIFO大小 7411为1024  buffer大小应该是FIFO/2 大小的整数倍(偶数),
      		m_EnableFifo	= TRUE ;		//使用FIFO
      		m_bGetParOk		= true ;

    5. 开启采集
      // Step 1: Open Device
      		m_ErrCode = DRV_DeviceOpen(m_dwDeviceNum, (LONG far *)&m_DriverHandle);
      		CString s;
      		if (m_ErrCode != SUCCESS)
      		{
      			DRV_GetErrorMessage(m_ErrCode,(LPSTR)m_szErrorMsg);
      			s = m_szErrorMsg;
      			AfxMessageBox(s);
      			return;
      		}
      
      		// Step 2: Get device features
      		m_ptDevFeatures.buffer = (LPDEVFEATURES)&m_DevFeatures;
      		m_ptDevFeatures.size = sizeof(DEVFEATURES);
      		if ((m_ErrCode = DRV_DeviceGetFeatures(m_DriverHandle,
      			(LPT_DeviceGetFeatures)&m_ptDevFeatures)) != SUCCESS)
      		{
      			DRV_GetErrorMessage(m_ErrCode,(LPSTR)m_szErrorMsg);
      			s = m_szErrorMsg;
      			AfxMessageBox(s);
      			DRV_DeviceClose((LONG far *)&m_DriverHandle);
      			return;
      		}
      		// Step 3: Allocate memory for driver
      		if((m_hBuf=(USHORT far *)GlobalAlloc(GHND,
      			sizeof(USHORT) * m_Conv)) == 0)
      		{		
      			AfxMessageBox(_T("高速缓存不足!"));
      			DRV_DeviceClose((LONG far *)&m_DriverHandle);
      			return;
      		}
      
      		// Step 4: Allocate memory for Voltage data or Raw data
      		if((m_hUserBuf=(FLOAT far *)GlobalAlloc(GHND,
      			sizeof(FLOAT) * m_Conv )) == 0)
      		{		
      			AfxMessageBox(_T("高速缓存不足!"));
      			DRV_DeviceClose((LONG far *)&m_DriverHandle);
      			return;
      		}
      		// Prepare some informations to run
      		m_ptFAIIntScanStart.buffer     = (USHORT far *)GlobalLock(m_hBuf);/*       */
      		m_ptFAITransfer.DataBuffer = (FLOAT far  *)GlobalLock(m_hUserBuf);
      
      
      		// Step 5: Enable event feature
      		m_ptEnableEvent.EventType = ADS_EVT_INTERRUPT  |
      			ADS_EVT_BUFCHANGE  |
      			ADS_EVT_TERMINATED |
      			ADS_EVT_OVERRUN;
      		m_ptEnableEvent.Enabled = m_Event;
      		m_ptEnableEvent.Count   = m_EnableFifo ? m_FifoSize : 1;
      		if ((m_ErrCode = DRV_EnableEvent(m_DriverHandle,
      			(LPT_EnableEvent)&m_ptEnableEvent)) != 0)
      		{
      			DRV_GetErrorMessage(m_ErrCode,(LPSTR)m_szErrorMsg);
      			s = m_szErrorMsg;
      			AfxMessageBox(s);
      			GlobalUnlock(m_hBuf);
      			GlobalUnlock(m_hUserBuf);
      			GlobalFree(m_hBuf);
      			GlobalFree(m_hUserBuf);
      			DRV_DeviceClose((LONG far *)&m_DriverHandle);
      			return;
      		}
      		// Step 6: Start Interrupt transfer
      		m_ptFAIIntScanStart.TrigSrc		 = m_Triggering;
      		m_ptFAIIntScanStart.SampleRate	 = m_PacerRate;
      		m_ptFAIIntScanStart.StartChan	 = 0;
      		m_ptFAIIntScanStart.NumChans	 = 4;
      		m_ptFAIIntScanStart.GainList	 = &m_Gain[0];
      		m_ptFAIIntScanStart.count		 = m_Conv;
      		m_ptFAIIntScanStart.cyclic		 = m_Model;
      		if (m_EnableFifo && m_FifoSize)
      			m_ptFAIIntScanStart.IntrCount = m_FifoSize;
      		else
      			m_ptFAIIntScanStart.IntrCount = 1;
      
      		if ((m_ErrCode = DRV_FAIIntScanStart(m_DriverHandle,
      			(LPT_FAIIntScanStart32)&m_ptFAIIntScanStart)) != 0)
      		{
      			DRV_GetErrorMessage(m_ErrCode,(LPSTR)m_szErrorMsg);
      			s = m_szErrorMsg;
      			AfxMessageBox(s);
      			GlobalUnlock(m_hBuf);
      			GlobalUnlock(m_hUserBuf);
      			GlobalFree(m_hBuf);
      			GlobalFree(m_hUserBuf);
      			DRV_DeviceClose((LONG far *)&m_DriverHandle);
      			return;
      		}
      		m_CyclicCount = 0;		
      
      		if (m_Event)
      		{
      			m_pEventThread = AfxBeginThread(EventThread,this);
      		}
      		m_ulInterruptCount=0;
      		m_ulBuffCount=0;           
      		m_ulOverRunCount=0;

    6. 几个配合函数照抄demo里的函数
      void CUSB4711AIINTView::adInterruptEvent()
      	{
      		return;
      	}
      
      
      
      	void CUSB4711AIINTView::adOverrunEvent()
      	{
      		// clear overrun
      		if ((m_ErrCode = DRV_ClearOverrun(m_DriverHandle)) != 0)
      		{
      			DRV_GetErrorMessage(m_ErrCode,(LPSTR)m_szErrorMsg);
      			CString s;
      			s = m_szErrorMsg;
      			AfxMessageBox(s);
      			return ;
      		}
      
      		return;
      	}
      
      	void CUSB4711AIINTView::adTerminateEvent()
      	{
      
      		// Get real voltage of buffer from driver
      		m_ptFAITransfer.ActiveBuf = 0;   // single buffer
      		m_ptFAITransfer.DataType = m_DataType;
      		m_ptFAITransfer.start    = 0;
      		m_ptFAITransfer.count    = m_Conv;
      		m_ptFAITransfer.overrun  = &m_gwOverrun;
      
      		if ((m_ErrCode = DRV_FAITransfer(m_DriverHandle,
      			(LPT_FAITransfer)&m_ptFAITransfer)) != 0)
      		{
      			DRV_GetErrorMessage(m_ErrCode,(LPSTR)m_szErrorMsg);
      			CString s;
      			s = m_szErrorMsg;
      			AfxMessageBox(s);
      			return ;
      		}
      
      		// Close driver
      		DRV_DeviceClose((LONG far *)&m_DriverHandle);
      
      		GlobalUnlock(m_hBuf);
      		GlobalUnlock(m_hUserBuf);
      		GlobalFree(m_hBuf);
      		GlobalFree(m_hUserBuf);
      
      		return;
      	}
      
      
      
      
      
      	UINT EventThread( LPVOID pParam )
      	{
      		USHORT usEventType;
      		LONG  ErrCde;
      
      
      		CUSB4711AIINTView*	pView = (CUSB4711AIINTView*)pParam;
      
      
      		pView->m_ulInterruptCount=0;
      		pView->m_ulBuffCount=0;           
      		pView->m_ulOverRunCount=0;
      
      		while(1)
      		{
      			// Check message
      			pView->m_ptCheckEvent.EventType = &usEventType;
      
      			if ((pView->m_Triggering) || (pView->m_Model) )
      			{
      				pView->m_ptCheckEvent.Milliseconds = INFINITE;
      			}
      			else
      			{
      				pView->m_ptCheckEvent.Milliseconds = 1000 * (pView->m_Conv / pView->m_PacerRate) + 1000;
      			}
      
      			if ((ErrCde = DRV_CheckEvent(pView->m_DriverHandle,
      				(LPT_CheckEvent)&pView->m_ptCheckEvent)) != 0)
      			{			
      				AfxMessageBox(_T("Check Event Error !"));
      				return 0;
      			}
      
      			// Process interrupt event
      			if (usEventType & ADS_EVT_INTERRUPT)
      			{
      				pView->adInterruptEvent();
      				pView->m_ulInterruptCount++;
      			}
      
      
      			// Process buffer change event
      			if (usEventType & ADS_EVT_BUFCHANGE)
      			{
      				pView->m_ulBuffCount++;           
      				pView->adBufChangeEvent();
      			}
      
      			// Process overrun event
      			if (usEventType & ADS_EVT_OVERRUN)
      			{
      				pView->m_ulOverRunCount++;
      				pView->adOverrunEvent();
      			}
      
      			// Process terminate event
      			if (usEventType & ADS_EVT_TERMINATED)
      			{
      				pView->adTerminateEvent();		
      				return 0;
      			}
      
      
      		}
      
      		AfxEndThread(0,true);
      		return 0;
      	}
      

    7. 最关键的步骤在函数
      	void CUSB4711AIINTView::adBufChangeEvent()
      	{
      		m_ptFAICheck.ActiveBuf = &m_gwActiveBuf;
      		m_ptFAICheck.stopped   = &m_gwStopped;
      		m_ptFAICheck.retrieved = &m_gulRetrieved;
      		m_ptFAICheck.overrun   = &m_gwOverrun;
      		m_ptFAICheck.HalfReady = &m_gwHalfReady;
      
      		if ((m_ErrCode = DRV_FAICheck(m_DriverHandle,
      			(LPT_FAICheck)&m_ptFAICheck)) != 0)
      		{
      			DRV_GetErrorMessage(m_ErrCode,(LPSTR)m_szErrorMsg);
      
      			CString s;
      			s = m_szErrorMsg;
      			AfxMessageBox(s);
      			return ;
      		}
      
      		if( m_gwHalfReady == 1 )
      		{
      			m_ptFAITransfer.ActiveBuf = 0;   // single buffer
      			m_ptFAITransfer.DataType = m_DataType;
      			m_ptFAITransfer.start    = 0;
      			m_ptFAITransfer.count    = m_Conv/2;
      			m_ptFAITransfer.overrun  = &m_gwOverrun;
      
      			if ((m_ErrCode = DRV_FAITransfer(m_DriverHandle,
      				(LPT_FAITransfer)&m_ptFAITransfer)) != 0)
      			{
      				DRV_GetErrorMessage(m_ErrCode,(LPSTR)m_szErrorMsg);
      				CString s;
      				s = m_szErrorMsg;
      				AfxMessageBox(s);
      				return ;
      			}
      			temp = (FLOAT far *)m_ptFAITransfer.DataBuffer;
      			for (int i=0;i<64;i++)
      			{ 					
      				x[i] = (i + 64 * m_CyclicCount * 2) * 1.0 ;
      				y[i] = ((FLOAT far *)temp)[i];
      			}
      			
      		}
      		else if( m_gwHalfReady == 2 )
      		{
      			m_ptFAITransfer.ActiveBuf = 0;   // single buffer
      			m_ptFAITransfer.DataType = m_DataType;
      			m_ptFAITransfer.start    = m_Conv/2;
      			m_ptFAITransfer.count    = m_Conv/2;
      			m_ptFAITransfer.overrun  = &m_gwOverrun;
      
      			if ((m_ErrCode = DRV_FAITransfer(m_DriverHandle,
      				(LPT_FAITransfer)&m_ptFAITransfer)) != 0)
      			{
      				DRV_GetErrorMessage(m_ErrCode,(LPSTR)m_szErrorMsg);
      				CString s;
      				s = m_szErrorMsg;
      				AfxMessageBox(s);
      				return ;
      			}
      			temp = (FLOAT far *)m_ptFAITransfer.DataBuffer;
      
      			for (int i=0;i<64;i++)
      			{
      
      				x[i+64] = (i + 64 * (m_CyclicCount * 2 + 1)) * 1.0 ;
      				y[i+64] = ((FLOAT far *)temp)[i];
      			}
      			
      			m_CyclicCount++;
      
      		}
      
      
      		return;
      	}
      这里涉及到FIFO工作模式,在这个程序里,一次采集128个数据,半个buffer就是64,buffer每半满,系统会产生一个HalfReady,此时读取driver缓存里的数据,不会产生错误。

     

    展开全文
  • 正则表达式中常用的模式修正符有i、g、m、s、x、e等。它们之间可以组合搭配使用。 它们的作用如下:  //修正符:i 不区分大小写的匹配;  //如:"/abc/i"可以与abc或aBC或ABc等匹配; //修正符:g表示全局匹配  /...

    链接自:php讨客网


    第一部分

    正则表达式中常用的模式修正符有i、g、m、s、x、e等。它们之间可以组合搭配使用。

    它们的作用如下:

     //修正符:i不区分大小写的匹配;   IgnoreCase

             //如:"/abc/i"可以与abc或aBC或ABc等匹配;

    //修正符:g表示全局匹配


        //修正符:m将字符串视为多行,不管是那行都能匹配;    Multiline

           例://模式为:$mode="/abc/m";
             //要匹配的字符串为:$str="bcefg5e\nabcdfe"
                 //注意其中\n,换行了;abc换到了下一行;
             //$str和$mode仍可以匹配,修正符m使得多行也可匹配;
        //修正符:s将字符串视为单行,换行符作为普通字符;   Singleline

           例://模式为:$mode="/pr.y/";
               //要匹配字符串为:$str="pr\ny";
               //两者不可匹配; . 是除了换行以外的字符可匹配;
               //修改下模式为:$mode="/pr.y/s";
                   //其中修正符s将\n视为普通字符,即不是换行;
               //最后两者可以匹配;
        //修正符:x将模式中的空白忽略;
        //修正符:A强制从目标字符串开头匹配;

             例://$mode="/abc/A";
               //可以与$str="abcsdfi"匹配,
               //不可以与$str2="sdsdabc"匹配;
               //因为$str2不是以abc开头;
        //修正符:D如果使用$限制结尾字符,则不允许结尾有换行;

             例://模式为:$mode="/abc$/";
               //可以与最后有换行的$str="adshabc\n"匹配;
               //元子符$会忽略最后的换行\n;
               //如果模式为:$mode="/abc/D",
               //则不能与$str="adshabc\n"匹配,
               //修正符D限制其不可有换行;必需以abc结尾;
        //修正符:U只匹配最近的一个字符串;不重复匹配;

             例:
                 如模式为:
                $mode="/a.*c/";
                $str="abcabbbcabbbbbc";
                preg_match($mode,$str,$content);
                echo $content[0];//输出:abcabbbcabbbbbc;

                //如果$mode="/a.*c/";变成$mode="/a.*c/U";
                 // 则只匹配最近一个字符串,输出:abc;

    //修正符:e 配合函数preg_replace()使用,
               可以把匹配来的字符串当作正则表达式执行;  

     

    m的作用详解:
    示例代码

    复制代码
    <html>
    <body>
    <textarea id="aid" cols="55" rows="10"></textarea> <input type="button" onClick="fun()" value="check" />
    <div id="myid">    
    </div>
    </html> 
    <script>
    /*
    测试数据:
    bd76 
    dfsdf 
    sdfsdfs 
    dffs 
    b76dsf 
    sdfsdf
    */
    
    function fun(){
        var reg = /^b./gm; //匹配到两个结果{bd,b7}
        //var reg = /^b./g;//匹配得到一个结果{bd}
        //var reg = /^b./m;//匹配得到一个结果{bd}
        var str = document.all("aid").value;
        var rs=str.match(reg); 
        for(var i=0; i<rs.length; i++){
            document.all("myid").innerHTML += "第"+i+"个元素:"+rs[i] + "<br/>";
        }
    }
    </script>
    复制代码

     

     

     

    下面的转:http://www.cnblogs.com/shunyao8210/archive/2008/11/13/1332591.html

    总结1:附件参数g的用法

    表达式加上参数g之后,表明可以进行全局匹配,注意这里“可以”的含义。我们详细叙述:

    1)对于表达式对象的exec方法,不加入g,则只返回第一个匹配,无论执行多少次均是如此,如果加入g,则第一次执行也返回第一个匹配,再执行返回第二个匹配,依次类推。例如
    var regx=/user\d/;
    var str=“user18dsdfuser2dsfsd”;
    var rs=regx.exec(str);//此时rs的值为{user1}
    var rs2=regx.exec(str);//此时rs的值依然为{user1}
    如果regx=/user\d/g;则rs的值为{user1},rs2的值为{user2}
    通过这个例子说明:对于exec方法,表达式加入了g,并不是说执行exec方法就可以返回所有的匹配,而是说加入了g之后,我可以通过某种方式得到所有的匹配,这里的“方式”对于exec而言,就是依次执行这个方法即可。

    2)对于表达式对象的test方法,加入g于不加上g没有什么区别。

    3)对于String对象的match方法,不加入g,也只是返回第一个匹配,一直执行match方法也总是返回第一个匹配,加入g,则一次返回所有的匹配(注意这与表达式对象的exec方法不同,对于exec而言,表达式即使加上了g,也不会一次返回所有的匹配)。例如:
    var regx=/user\d/;
    var str=“user1sdfsffuser2dfsdf”;
    var rs=str.match(regx);//此时rs的值为{user1}
    var rs2=str.match(regx);//此时rs的值依然为{user1}
    如果regx=/user\d/g,则rs的值为{user1,user2},rs2的值也为{user1,user2}

    4)对于String对象的replace方法,表达式不加入g,则只替换第一个匹配,如果加入g,则替换所有匹配。(开头的三道测试题能很好的说明这一点)

    5)对于String对象的split方法,加上g与不加g是一样的,即:
    var sep=/user\d/;
    var array=“user1dfsfuser2dfsf”.split(sep);
    则array的值为{dfsf, dfsf}
    此时sep=/user\d/g,返回值是一样的。

    6)对于String对象的search方法,加不加g也是一样的。

    总结2:附加参数m的用法

    附加参数m,表明可以进行多行匹配,但是这个只有当使用^和$模式时才会起作用,在其他的模式中,加不加入m都可以进行多行匹配(其实说多行的字符串也是一个普通字符串),我们举例说明这一点

    1)使用^的例子
    var regx=/^b./g;
    var str=“bd76 dfsdf
    sdfsdfs dffs
    b76dsf sdfsdf”;
    var rs=str.match(regx);
    此时加入g和不加入g,都只返回第一个匹配{bd},如果regx=/^b./gm,则返回所有的匹配{bd,b7},注意如果regx=/^b./m,则也只返回第一个匹配。所以,加入m表明可以进行多行匹配,加入g表明可以进行全局匹配,综合到一起就是可以进行多行全局匹配

    2)使用其他模式的例子,例如
    var regx=/user\d/;
    var str=“sdfsfsdfsdf
    sdfsuser3 dffs
    b76dsf user6”;
    var rs=str.match(regx);
    此时不加参数g,则返回{user3},加入参数g返回{user3,user6},加不加入m对此没有影响。

    3)因此对于m我们要清楚它的使用,记住它只对^和$模式起作用,在这两种模式中,m的作用为:如果不加入m,则只能在第一行进行匹配,如果加入m则可以在所有的行进行匹配。我们再看一个^的例子
    var regx=/^b./;
    var str=“ret76 dfsdf
    bjfsdfs dffs
    b76dsf sdfsdf”;
    var rs=str.match(regx);
    此时rs的值为null,如果加入g,rs的值仍然为null,如果加入m,则rs的值为{bj}(也就是说,在第一行没有找到匹配,因为有参数m,所以可以继续去下面的行去找是否有匹配),如果m和g都加上,则返回{bj,b7}(只加m不加g说明,可以去多行进行匹配,但是找到一个匹配后就返回,加入g表明将多行中所有的匹配返回,当然对于match方法是如此,对于exec呢,则需要执行多次才能依次返回)

    总结3:在HTML的textarea输入域中,按一个Enter键,对应的控制字符为“\r\n”,即“回车换行”,而不是“\n\r”,即“换行回车”,我们看一个前面我们举过的例子:
    var regx=/a\r\nbc/;
    var str=“a
    bc”;
    var rs=regx.exec(str);
    结果:匹配成功,rs的值为:{ },如果表达式为/a\n\rbc/,则不会被匹配,因此在一般的编辑器中一个”Enter”键代表着“回车换行”,而非“换行回车”,至少在textarea域中是这样的。


    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    第二部分


    模式修正符
    模式修正符 -- 解说正则表达式模式中使用的修正符
    说明
      下面列出了当前在 PCRE 中可能使用的修正符。括号中是这些修正符的内部 PCRE 名。修正符中的空格和换行被忽略,其它字符会导致错误。

    i (PCRE_CASELESS)
      如果设定此修正符,模式中的字符将同时匹配大小写字母。

    m(PCRE_MULTILINE)
      默认情况下,PCRE 将目标字符串作为单一的一“行”字符所组成的(甚至其中包含有换行符也是如此)。“行起始”元字符(^)仅仅匹配字符串的起始,“行结束”元字符($)仅仅匹配字符串的结束,或者最后一个字符是换行符时其前面(除非设定了 D 修正符)。这和 Perl 是一样的。

      当设定了此修正符,“行起始”和“行结束”除了匹配整个字符串开头和结束外,还分别匹配其中的换行符的之后和之前。这和 Perl 的 /m 修正符是等效的。如果目标字符串中没有“\n”字符或者模式中没有 ^ 或 $,则设定此修正符没有任何效果。

    s(PCRE_DOTALL)
      如果设定了此修正符,模式中的圆点元字符(.)匹配所有的字符,包括换行符。没有此设定的话,则不包括换行符。这和 Perl 的 /s 修正符是等效的。排除字符类例如 [^a] 总是匹配换行符的,无论是否设定了此修正符。

    x(PCRE_EXTENDED)
      如果设定了此修正符,模式中的空白字符除了被转义的或在字符类中的以外完全被忽略,在未转义的字符类之外的 # 以及下一个换行符之间的所有字符,包括两头,也都被忽略。这和 Perl 的 /x 修正符是等效的,使得可以在复杂的模式中加入注释。然而注意,这仅适用于数据字符。空白字符可能永远不会出现于模式中的特殊字符序列,例如引入条件子模式的序列 (?( 中间。

    e
      如果设定了此修正符,preg_replace() 在替换字符串中对逆向引用作正常的替换,将其作为 PHP 代码求值,并用其结果来替换所搜索的字符串。

      只有 preg_replace() 使用此修正符,其它 PCRE 函数将忽略之。

      注: 本修正符在 PHP3 中不可用。

    A(PCRE_ANCHORED)
      如果设定了此修正符,模式被强制为“anchored”,即强制仅从目标字符串的开头开始匹配。此效果也可以通过适当的模式本身来实现(在 Perl 中实现的唯一方法)。

    D(PCRE_DOLLAR_ENDONLY)
      如果设定了此修正符,模式中的美元元字符仅匹配目标字符串的结尾。没有此选项时,如果  最后一个字符是换行符的话,美元符号也会匹配此字符之前(但不会匹配任何其它换行符之前)。如果设定了 m 修正符则忽略此选项。Perl 中没有与其等价的修正符。

    S
      当一个模式将被使用若干次时,为加速匹配起见值得先对其进行分析。如果设定了此修正符则会进行额外的分析。目前,分析一个模式仅对没有单一固定起始字符的 non-anchored 模式有用。

    U(PCRE_UNGREEDY)
      本修正符反转了匹配数量的值使其不是默认的重复,而变成在后面跟上“?”才变得重复。这和 Perl 不兼容。也可以通过在模式之中设定 (?U) 修正符或者在数量符之后跟一个问号(如 .*?)来启用此选项。

    X(PCRE_EXTRA)
      此修正符启用了一个 PCRE 中与 Perl 不兼容的额外功能。模式中的任何反斜线后面跟上一个没有特殊意义的字母导致一个错误,从而保留此组合以备将来扩充。默认情况下,和 Perl 一样,一个反斜线后面跟一个没有特殊意义的字母被当成该字母本身。当前没有其它特性受此修正符控制。

    u(PCRE_UTF8)
      此修正符启用了一个 PCRE 中与 Perl 不兼容的额外功能。模式字符串被当成 UTF-8。本修正符在 Unix 下自 PHP 4.1.0 起可用,在 win32 下自 PHP 4.2.3 起可用。自 PHP 4.3.5 起开始检查模式的 UTF-8 合法性。


    补充内容:编程语言中的转义
    编程中的转义比较特别,值得拿出来讲一讲。

    在一般的编程语言中,正则表达式都是以字符串(String)构造的。而对于字符串这种数据类型,多数编程语言都规定了一些转义字符序列来表示特殊字符(注意,这里说的是编程语言中的特殊字符,而不是正则表达式中的“元字符”),比如〖\n〗表示换行符,〖\t〗表示制表符。因此,在编程语言中使用正则表达式时,转义是一个麻烦的问题,但是,我们又必须弄清楚这个问题——网络上时常看到有人提问“这个正则表达式里到底需要几个反斜线?”。

    转义的问题其实非常简单:在编程语言中,作为字符串出现的正则表达式,必须经过“字符串的转义”,“正则表达式的转义”才能真正生效。

    举例来说,我们的正则表达式里需要出现『\*』,也就是“带转义(去掉了特殊意义)的〖*〗字符”。因为正则表达式是以字符串形式表现的,如果直接在字符串里写 \* ,许多编程语言就会报错:因为它只认识 \t 、 \n 之类的转义序列,不认识 \* 。

    解决的办法是在 \* 之前添加反斜线 \ ,写成 \\* 。这样,编程语言在处理字符串时,会首先将 \\ “翻译”成 \ ,正则表达式得到的就是 \* 了。

    以上说的情况比较简单,可是,如果我们需要在正则表达式中使用反斜线字符『\』呢?这个字符首先必须以转义形式写出,也就是说,在正则表达式中必须写成『\\』;但每个反斜线,在字符串里又需要转义,所以正则表达式中的一个『\』,在字符串里就必须写成【\\\\】!

    不过,也有些语言不会报错——它们遇到“未定义”的转义字符,会直接忽略反斜线的转义功能,将它视为普通字符序列,Python,PHP都是如此(注:JS是否如此尚未验证)。

    我的建议是,在对元字符转义时,心里一定要明白应该写几个反斜线;同时,推荐你在使用Python和PHP时,不要省略一个反斜线字符。

    g 改变模式中的所有匹配 
    i 忽略模式中的大小写 
    e 替换字符串作为表达式 
    m 将待匹配串视为多行 
    o 仅赋值一次 
    s 将待匹配串视为单行 
    x 忽略模式中的空白
    可以使用影响匹配行为的选项修改正则表达式模式。可以通过下列两种基本方法之一设置正则表达式选项:可以在 Regex (pattern, options) 构造函数中的 options 参数中指定,其中 options 是 RegexOptions 枚举值的按位“或”组合;也可以使用内联 (?imnsx-imnsx:) 分组构造或 (?imnsx-imnsx) 其他构造在正则表达式模式内设置它们。

    在内联选项构造中,一个选项或一组选项前面的减号 (-) 用于关闭这些选项。例如,内联构造 (?ix-ms) 将打开 IgnoreCase 和 IgnorePatternWhiteSpace 选项而关闭 Multiline 和 Singleline 选项。默认情况下,关闭所有正则表达式选项。

    下表列出了 RegexOptions 枚举的成员以及等效的内联选项字符。请注意,选项 RightToLeft 和 Compiled 只适用于表达式整体而不允许内联。(它们只能在 Regex 构造函数的 options 参数中指定。)选项 None 和 ECMAScript 不允许内联。

    RegexOption 成员 内联字符 说明 
    None
     N/A
     指定不设置任何选项。
     
    IgnoreCase
     i
     指定不区分大小写的匹配。
     
    Multiline
     m
     指定多行模式。更改 ^ 和 $ 的含义,以使它们分别与任何行的开头和结尾匹配,而不只是与整个字符串的开头和结尾匹配。
     
    ExplicitCapture
     n
     指定唯一有效的捕获是显式命名或编号的 (?<name>…) 形式的组。这允许圆括号充当非捕获组,从而避免了由 (?:…) 导致的语法上的笨拙。
     
    Compiled
     N/A
     指定正则表达式将被编译为程序集。生成该正则表达式的 Microsoft 中间语言 (MSIL) 代码;以较长的启动时间为代价,得到更快的执行速度。
     
    Singleline
     s
     指定单行模式。更改句点字符 (.) 的含义,以使它与每个字符(而不是除 \n 之外的所有字符)匹配。
     
    IgnorePatternWhitespace
     x
     指定从模式中排除非转义空白并启用数字符号 (#) 后面的注释。(有关转义空白字符的列表,请参见字符转义。)请注意,空白永远不会从字符类中消除。
     
    RightToLeft
     N/A
     指定搜索是从右向左而不是从左向右进行的。具有此选项的正则表达式将移动到起始位置的左边而不是右边。(因此,起始位置应指定为字符串的结尾而不是开头。)为了避免构造具有无限循环的正则表达式的可能性,此选项不能在中流指定。但是,(?<) 回顾后发构造提供了可用作子表达式的类似替代物。

    RightToLeft 只更改搜索方向。它不会反转所搜索的子字符串。预测先行和回顾后发断言不改变:预测先行向右搜索;回顾后发向左搜索。
     
    ECMAScript
     N/A
     指定已为表达式启用了符合 ECMAScript 的行为。此选项仅可与 IgnoreCase 和 Multiline 标志一起使用。将 ECMAScript 同任何其他标志一起使用将导致异常。
     
    CultureInvariant
     N/A
     指定忽略语言中的区域性差异。有关更多信息,请参见在 RegularExpressions 命名空间中执行不区分区域性的操作。


    展开全文
  • KMP算法模式匹配

    千次阅读 2014-07-15 21:49:56
    在一个长串中查找一个子串是较常用的操作。各种信息检索系统,文字处理系统都少不了。本文介绍一个非常著名的KMP模式匹配算法用于子串查找
  • MVC设计模式

    万次阅读 多人点赞 2019-05-27 21:03:58
    MVC模式 MVC:Model、View、Controller MVC设计模式的引入 在我们实际开发的最后到产品上线,供给客户使用,客户通过浏览器或者app等进行数据的操作,实现这个的有,处理发送请求,业务逻辑处理以及访问数据库,这三...
  • 单例模式讨论篇:单例模式与垃圾回收

    万次阅读 多人点赞 2012-03-08 09:14:35
    Jvm的垃圾回收机制到底会不会回收掉长时间不用的单例模式对象,这的确是一个比较有争议性的问题。将这一部分内容单独成篇的目的也是为了与广大博友广泛的讨论一下这个问题。为了能让更多的人看到这篇文章,请各位...
  • 设计模式

    千次阅读 2018-07-10 10:39:58
    Num1:单例模式基本概念:保证一个类仅有一个实例,并提供一个访问它的全局访问点。常见写法:懒汉式public class Singleton { /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */ private ...
  • centos 7 查看硬盘健康状态S.M.A.R.T

    万次阅读 2019-05-08 16:32:40
    -F TYPE 设置smartctl的行为,当出现一些已知但没有解决的硬件或软件bug时, smartctl应该怎么做。 -P TYPE 设置smartctl是否对磁盘使用数据库中已有的参数。 SMART 离线测试、自测试 参数 -t TEST 立刻执行测试,...
  • 观察者模式(有时又被称为发布/订阅模式)是软件设计模式的一种。在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此...
  • java设计模式

    千次阅读 2012-04-04 16:09:50
    Java设计模式 分类: java学习 2011-09-04 20:48 883人阅读 评论(6) 收藏 举报 设计模式公司荣誉出品 您的设计模式 我们的设计模式 CBF4LIFE 2009 年5 月 我希望这本书的阅读者具备最基本的代码编写能力,...
  • Java设计模式

    千次阅读 热门讨论 2011-09-04 20:48:09
    设计模式公司荣誉出品 您的设计模式 我们的设计模式 CBF4LIFE 2009 年5 月 我希望这本书的阅读者具备最基本的代码编写能力,您是一个初级的coder,可以从中 领会到怎么设计一段优秀的代码;您是一个高级程序员...
  • Java for循环详解

    千次阅读 2020-11-03 14:53:13
    但是对于for循环来说,它也有很多变体,如果不清楚for循环的结构原理,遇上一些变体的for循环,可能一时不太明白它的含义,for循环虽然有很多变体,但都离不开括号3条语句的模式(除了增强for循环模式例外) ...
  • Java23种设计模式

    万次阅读 多人点赞 2017-07-01 15:57:01
    一、设计模式入门:1.设计模式是人们在面对同类型软件工程设计问题所总结出的一些有用经验。模式不是代码,而是某类问题的通用设计解决方案 2.设计模式的本质目的是使软件工程在维护性、扩展性、变化性、复杂度方面...
  • 模式匹配算法

    千次阅读 2015-06-08 07:38:39
    简单模式匹配算法 BF算法(Brute-Force,又称古典的、经典的、朴素的、穷举的) 带回溯,速度慢 算法设计思想: 将主串S的第pos个字符和模式T的第1个字符比较,若相等,继续逐个比较后续字符;若不等,从主串S...
  • 服务器端编程心得(二)—— Reactor模式

    万次阅读 热门讨论 2016-11-24 15:12:36
    最近一直在看游双的《高性能linux服务器编程》一书...书上是这么介绍Reactor模式的:     按照这个思路,我写个简单的练习:   /** *@desc: 用reactor模式练习服务器程序,main.cpp *@author: zhangyl...
  • 串口工作在DMA模式下有时接收异常

    千次阅读 2016-11-25 16:56:05
    1 前言客户反馈在使用STM32F205的串口工作在DMA模式时,有时能够接收数据,有时完全没有数据,但如果换成中断模式来接收又能100%正常收到数据。2 复现现象2.1 问题背景与客户沟通,客户使用的是STM32F2标准库V1.1.0...
  • A-Frame WebVR开发入门教程

    万次阅读 2017-04-07 00:05:51
    WebVR和WebGL应用程序接口使得我们已经可以在浏览器上创建虚拟现实(VR)体验,但从工程化的角度而言,开发社区需要更多方便强大的开发库来简化编程,Mozilla的 A-Frame 框架就是这样一个工具,提供了一个Web...
  • 《Cortex-A系列编程者指南(3.0版)》 文档来源:DEN0013C_cortex_a_series_PG.pdf 说明:前三章是介绍性的,略过,值得关注的是Linaro:www.linaro.org。 第四章 ARM寄存器、工作模式和指令集 本章介绍ARM处理器...
  • PX4位置控制offboard模式说明

    万次阅读 多人点赞 2017-03-13 17:03:43
    offboard模式的开发及应用 ...基本模式又分为,位置控制模式、自稳模式、手动模式,通常情况下,自驾仪处于自定义模式,目前没有见过处于基本模式工作的代码,所以暂时不讨论基本模式。  地面站
  • 期末作业系列之设计模式

    万次阅读 热门讨论 2017-12-02 16:04:54
    又是一年的期末,今年大三上学期的设计模式作业是:运用5个以上的设计模式创建一个系统。(这里我问了一下老师,老师说反正就是要5个以上联系在一个project里的就行了嘿嘿嘿。) 作为一个平时不认真学习的“大学生...
  • 循环结构(二)——循环嵌套

    千次阅读 2018-12-12 15:23:24
    三种循环可互相嵌套,层数不限 嵌套循环的执行流程
  • Java设计模式——状态模式(STATE PATTERN)

    万次阅读 多人点赞 2016-09-26 23:45:36
     太简单的程序了,是个程序员都会写这个程序,这么简单的程序拿出来show,是不是太小看我们的智商了?!非也,非也,我们继续往下分析,这个程序有什么问题,你想呀电梯门可以打开,但不是随时都可以开,是有前提...
  • 深入理解Java for循环

    千次阅读 2019-01-06 17:12:48
    但是对于for循环来说,它也有很多变体,如果不清楚了解for循环的结构原理,遇上一些变体的for循环,可能一时不太明白它的含义,for循环虽然有很多变体,但都离不开括号3条语句的模式(除了增强fo...
  • 设计模式--状态模式精解

    千次阅读 热门讨论 2013-08-06 10:32:21
    状态模式将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个具体状态类中,所以通过定义新的子类可以很容易地增加新的状态和转换。通俗点说,状态模式是为了消除庞大的条件分支语句,...
  • linux awk 数组和循环

    万次阅读 多人点赞 2013-01-09 00:00:39
    花括号 {} 不需要在程序中始终出现,但它们用于根据特定的模式对一系列指令进行分组(作用域)。 二 数组定义 1 一维数组 a) 数字下标 array[1]="it" array[2]="homer" array[3]="sunboy" array[4]=2050 b) 字符...
  • STM32 USART串口DMA接收和发送模式

    万次阅读 2016-05-18 16:33:00
    STM32 USART串口DMA接收和发送模式 温馨提示:需要下载资料的请到原文下载 串口DMA发送: 发送数据的流程: 前台程序中有数据要发送,则需要做如下几件事 1. 在数据发送缓冲区内放好要发送的数据,...
  • 设计模式之我见

    千次阅读 2011-11-25 15:06:33
    设计模式 Design pattern ( 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。...
  • zeromq模式介绍

    千次阅读 2015-12-30 16:12:04
    REQ和REP不停的重复它们的操作循环。REP类似于一个http服务器,REQ类似于客户端。一个REP可以连接多个REQ端,REP顺序处理REQ的请求。PUSH-PULL模式推拉模式,PUSH发送,send。PULL方接收,recv。PUSH可以和多个PULL...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 152,522
精华内容 61,008
关键字:

内循环模式是a还m