【基础】OPC 通讯协议

OPC 通讯协议基础

OPC 简介

OPC 全称 OLE For Process Control,即用于控制过程的 OLE,是一个工业标准,管理该标准的国际组织是 OPC 基金会。

OPC 出现的目的是为不同的供应商设备与应用程序之间的接口标准化从而使其间的数据交换更加简单,因此,使我们可以开发不依靠于特定开发语言和开发环境的、可以自由组合的过程控制软件。

利用驱动器的系统连接
在这里插入图片描述
利用 OPC 控制的系统组成
在这里插入图片描述

OPC 的分层结构

OPC 对象中最上层的对象是 OPC 服务器,一个 OPC 服务器中可以设置一个以上的 OPC 组。OPC 服务器常对应于某种特定的控制设备,如 DCS 以及 PLC 等。

OPC 组是可以进行数据访问的多个 OPC 标签的集合,OPC 应用程序可以将需要的数据分组进行批量读取,也可以以组为单位启动或者停止数据访问。此外,OPC 组还提供组内 OPC 标签数据变化时向 OPC 应用程序通知的事件。

在这里插入图片描述

OPC 与 OPC UA

OPC DA 与 OPC UA 都是 OPC 协议的标准:

  • OPC 是一种通过微软 COM/DCOM 技术来实现自动化控制的协定,采用 C/S 架构。开发人员只需要按照 OPC 的标准编写 OPC-Client 访问 OPC-Server 进行读写操作即可实现与硬件设备的通信。OPC 的协定中包括:

    • DA (Data Access):访问数据的主要规范;

    • A&E (Alarm and Event):基于事件提供 Client 端订阅,事件触发后 Server 主动提交数据;

    • HDA (History Data Access):历史数据访问;

  • OPC UA 是 OPC 协议的新版,其不再依赖于 COM/DCOM 技术,这意味着其具有跨平台性,不再局限于 Windows 系统。OPC UA 提供了可靠的通信机制,接口简单一致。

举例说明两者之间的区别:

对传统的三种不同类型OPC服务器的访问:数据访问 DA、报警和事件 AE、历史数据访问 HDA,要获得一个温度传感器的当前值、一个高温度事件和温度的历史平均值,要依次使用不同的命令执行;

而使用 OPC UA,仅用一个组件就非常容易地完成了。配置和工程的时间也因此可以大大缩短。

OPC 逻辑对象模型

包括3类对象:OPC Server 对象、OPC Group 对象、OPC Item 对象,每类对象都包括一系列接口。

OPC Server 对象

主要功能:

  • 创建和管理 OPC Group 对象;

  • 管理服务器内部的状态信息;

OPC Group 对象

主要功能:

  • 创建和管理 OPC Item 对象;

  • 管理 OPC Group 对象的内部状态信息;

  • OPC Server 内部实时数据的读写服务;

属性:

  • name:组名,由客户端自定义;

  • active:组的激活状态,若为 false 则组失效,无法对服务器进行读写;

  • update rate:更新速率(该值应大于服务器设定的最小值);

  • percent data band:数据死区;

注意:

  • Group 分为公共组和私有组:公共组对所有连接到服务器的客户端都有效;而私有组仅对建立该组的客户端有效;

OPC Item 对象

主要功能:

  • 用以描述实时数据,代表了与服务器数据源的连接;

属性:

  • name:项名,在服务器中对应 Item ID;

  • active:项的激活状态;

  • value:项的数据值;

  • quality:项的品质,代表数值的可信度;

  • timestamp:时间戳,代表数据的存取事件;

注意:

  • Item 的存储类型为 VARIANT;Item 的数据类型为 VARTYPE;

  • 一个项不能被 OPC 客户端直接访问,因为 OPC 协议中没有对应于项的 COM 接口,对项的访问必须通过 OPC Group 实现;

  • Item 在服务端的定义对应于硬件的实际地址。客户端连接到服务器后创建并添加 OPC Group,并创建一系列的 OPC Item,将逻辑上等价的一组 OPC Item 添加到 OPC Group 中即可通过组对象对数据进行读写操作;

OPC 通信方式

  • 同步通信:OPC Client 对 OPC Server 进行读取操作时,OPC Client 必须等到 OPC Server 完成对应操作后才能返回,在此期间 OPC Client 处于一直等待的状态。

  • 异步通信:OPC Client 对 OPC Server 进行读取操作时,OPC Client 发送请求后立即返回,不用等待 OPC Server,当 OPC Server 完成操作后再通知 OPC Client 程序。

  • 订阅:需要 OPC Server 支持OPC A&E规范,由 OPC Client 设定数据的变化限度,如果数据源的实时数据变化超过了该限度,OPC Server 通过回调返回数据给OPC Client。

Java 实现 OPC 的方式

OPC Client 开发大致流程

  1. COM 组件初始化;
  2. 创建服务器 Server 对象;
  3. 创建组 Group 对象;
  4. 创建项 Item 对象;
  5. 添加 Item 到 Group 中;
  6. 添加 Group 到 Server 对象中;
  7. 连接服务器,完成相应操作;
  8. COM 组件关闭

OPC DA

Java 关于 OPC DA 的开源库只有 Utgard 和 Jeasyopc,两者区别如下:

UtgardJeasyopc
Linux支持不支持
Windows支持不支持
用户名和密码需要不需要
组查询支持不支持
压力测试(单线程同步)略快 7W 点约 4224ms略慢 7W 点约 22540ms
DCOM通过 DCOM 实现,需进行配置不需要配置
开源库现状作者删库跑路只支持 32 位系统

OPC UA

推荐使用 Eclipse 的 milo 开源库

Java 实现 OPC-client

本测试使用的 OPC-Server 软件为 KEPServerEX6,具体下载与使用参考博客OPCServer:使用KEPServer

OPC-DA

因为开源库 Jeasyopc 不支持 windows 和 linux 系统,且只支持 32 位系统,因此此处使用 Utgard 库实现。本测试采用虚拟机实现,使用的系统为 Windows 10 专业版,版本号 1903。

Utgard 开源库通过 DCOM 技术实现,因此首先需要配置 DCOM,参考博客OPC和DCOM配置

引入相应的依赖

        <dependency>
            <groupId>org.kohsuke.jinterop</groupId>
            <artifactId>j-interop</artifactId>
            <version>2.0.5</version>
        </dependency>
        <dependency>
            <groupId>org.openscada.utgard</groupId>
            <artifactId>org.openscada.opc.lib</artifactId>
            <version>1.5.0</version>
        </dependency>

从 OPC-Server 读取数据

public class OPCRead {

    public static void main(String[] args) {
        // 配置连接信息
        final ConnectionInformation ci = new ConnectionInformation();
        ci.setHost("localhost");         // 本机IP
        ci.setDomain("");                // 域,为空就行
        ci.setUser("OPCServer");         // 用户名
        ci.setPassword("OPCServer");     // 密码

        // 配置 KEPServer
        ci.setClsid("7BC0CC8E-482C-47CA-ABDC-0FE7F9C6E729"); // KEPServer 的注册表ID,可以在“组件服务”里看到
        final String itemId = "通道 1.设备 1.标记 2";    // KEPServer 上配置的项的名字

        // 启动服务
        final Server server = new Server(ci, Executors.newSingleThreadScheduledExecutor());

        try {
            // 连接服务器
            server.connect();
            // 创建 Group,用于对 Item 的访问
            final Group group = server.addGroup("test");
            // 将要访问的 Item 加入创建的 Group
            final Item item = group.addItem(itemId);
            // 读取 Item 状态
            ItemState itemState = item.read(true);
            // 获取 Item 的数据类型,该类型使用常量定义,见 JIVariant 类
            int type = 0;
            try {
                type = itemState.getValue().getType(); // 类型实际是数字,用常量定义的
            } catch (JIException e) {
                e.printStackTrace();
            }
            // 打印 Item 相应状态
            System.out.println(">>>监控项的数据类型是:" + type);
            System.out.println(">>>监控项的时间戳是:" + itemState.getTimestamp().getTime());
            System.out.println(">>>监控项的详细信息是:" + itemState);
            // 若读到是 short 类型(对应数字 2)
            if (type == JIVariant.VT_I2) {
                short value = 0;
                try {
                    value = itemState.getValue().getObjectAsShort();
                } catch (JIException e) {
                    e.printStackTrace();
                }
                System.out.println(">>>short类型值: " + value);
            }
            // 删除 Group
            server.removeGroup(group, true);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

从 OPC-Server 写入数据

public class OPCWrite {

    public static void main(String[] args) {
        // 配置连接信息
        final ConnectionInformation ci = new ConnectionInformation();
        ci.setHost("localhost");         // 本机IP
        ci.setDomain("");                // 域,为空就行
        ci.setUser("OPCServer");         // 用户名
        ci.setPassword("OPCServer");     // 密码

        // 配置 KEPServer
        ci.setClsid("7BC0CC8E-482C-47CA-ABDC-0FE7F9C6E729"); // KEPServer 的注册表ID,可以在“组件服务”里看到
        final String itemId = "通道 1.设备 1.标记 2";    // KEPServer 上配置的项的名字

        // 启动服务
        final Server server = new Server(ci, Executors.newSingleThreadScheduledExecutor());

        try {
            // 连接服务器
            server.connect();
            // 创建 Group,用于对 Item 的访问
            final Group group = server.addGroup("test");
            // 将要访问的 Item 加入创建的 Group
            final Item item = group.addItem(itemId);
            // 写入前:打印 Item 状态及对应数据
            printRead(item);
            // 写入数据
            item.write(new JIVariant("100"));
            // 写入后:打印 Item 状态及对应数据
            printRead(item);
            // 删除 Group
            server.removeGroup(group, true);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 读取 Item 状态、数据
     * @param item item
     * @throws JIException
     */
    public static void printRead(Item item) throws JIException {
        // 读取 Item 状态
        ItemState itemState = item.read(true);
        int type = 0;
        try {
            type = itemState.getValue().getType(); // 类型实际是数字,用常量定义的
        } catch (JIException e) {
            e.printStackTrace();
        }
        // 打印 Item 相应状态
        System.out.println(">>>监控项的数据类型是:" + type);
        System.out.println(">>>监控项的时间戳是:" + itemState.getTimestamp().getTime());
        System.out.println(">>>监控项的详细信息是:" + itemState);
        // 若读到是 short 类型(对应数字 2)
        if (type == JIVariant.VT_I2) {
            short value = 0;
            try {
                value = itemState.getValue().getObjectAsShort();
            } catch (JIException e) {
                e.printStackTrace();
            }
            System.out.println(">>>short类型值: " + value);
        }
    }

}

OPC-UA

OPC-UA 是目前比较流行的协议,采用开源库 milo 实现,引入相应依赖:

        <dependency>
            <groupId>org.eclipse.milo</groupId>
            <artifactId>sdk-client</artifactId>
            <version>0.6.3</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.milo</groupId>
            <artifactId>sdk-server</artifactId>
            <version>0.6.3</version>
        </dependency>

使用 opc-ua 实现数据读写:

public class OpcUaDemo {

    public static void main(String[] args) throws Exception {
        // 创建OPC UA客户端
        OpcUaClient opcUaClient = createClient();

        // 开启连接
        opcUaClient.connect().get();

        // 遍历节点
        browseNode(opcUaClient, null);

        // 读
        readNode(opcUaClient);

        // 写
        writeNodeValue(opcUaClient);
        readNode(opcUaClient);

        // 关闭连接
        opcUaClient.disconnect().get();
    }

    /**
     * 创建 opc-ua 客户端
     * @return OpcUaClient
     * @throws Exception
     */
    private static OpcUaClient createClient() throws Exception {
        String endPointUrl = "opc.tcp://127.0.0.1:49320";
        Path securityTmpdir = Paths.get(System.getProperty("java.io.tmpdir"), "security");
        Files.createDirectories(securityTmpdir);
        if (!Files.exists(securityTmpdir)) {
            throw new Exception("unable to create security dir: " + securityTmpdir);
        }
        return OpcUaClient.create(endPointUrl,
                endpoints ->
                        endpoints.stream()
                                .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri()))
                                .findFirst(),
                configBuilder ->
                        configBuilder
                                .setApplicationName(LocalizedText.english("KEPServerEX/UA Client Driver"))
                                .setApplicationUri("urn:Thinkbook-ZQF:Kepware.KEPServerEX.V6:UA%20Client%20Driver")
                                //访问方式
                                .setIdentityProvider(new AnonymousProvider())
                                .setRequestTimeout(UInteger.valueOf(5000))
                                .build()
        );
    }

    /**
     * 遍历树形节点
     * @param client 客户端
     * @param uaNode 节点
     * @throws Exception
     */
    private static void browseNode(OpcUaClient client, UaNode uaNode) throws Exception {
        List<? extends UaNode> nodes;
        if (uaNode == null) {
            nodes = client.getAddressSpace().browseNodes(Identifiers.ObjectsFolder);
        } else {
            nodes = client.getAddressSpace().browseNodes(uaNode);
        }
        for (UaNode nd : nodes) {
            //排除系统行性节点,这些系统性节点名称一般都是以"_"开头
            if (Objects.requireNonNull(nd.getBrowseName().getName()).contains("_")) {
                continue;
            }
            System.out.println("Node= " + nd.getBrowseName().getName());
            browseNode(client, nd);
        }
    }

    /**
     * 读取节点数据
     *
     * @param client OPC UA客户端
     * @throws Exception
     */
    private static void readNode(OpcUaClient client) throws Exception {
        int namespaceIndex = 2;
        String identifier = "通道 1.设备 1.标记 1";
        //节点
        NodeId nodeId = new NodeId(namespaceIndex, identifier);
        //读取节点数据
        DataValue value = client.readValue(0.0, TimestampsToReturn.Neither, nodeId).get();
        //标识符
        System.out.println(identifier + ": " + String.valueOf(value.getValue().getValue()));
    }

    /**
     * 写入节点数据
     *
     * @param client
     * @throws Exception
     */
    private static void writeNodeValue(OpcUaClient client) throws Exception {
        //节点
        NodeId nodeId = new NodeId(2, "通道 1.设备 1.标记 1");
        short i = 3;
        //创建数据对象,此处的数据对象一定要定义类型,不然会出现类型错误,导致无法写入
        DataValue nowValue = new DataValue(new Variant(i), null, null);
        //写入节点数据
        StatusCode statusCode = client.writeValue(nodeId, nowValue).join();
        System.out.println("结果:" + statusCode.isGood());
    }

}

模拟数据进行代码测试

OPC-DA 代码验证

使用 KEPServerEX 6 作为 OPC-Server 进行测试,百度网盘下载链接如下:

链接:https://pan.baidu.com/s/1pigppR62xTsE_4ecXx9m8Q?pwd=3aig 
提取码:3aig

在界面上可以右键单击标记名称进行数据格式的设置

在这里插入图片描述
单击工具栏最后一个,创建一个 OPC Quick client,可以观察到,此时通道 1.设备 1.标记 2的值为 0

在这里插入图片描述
修改 opc-da 写入程序,写入数值为 300

在这里插入图片描述
执行写入程序,查看 client 中的对应值,写入成功

在这里插入图片描述
使用读取程序对数据进行读取,同样可以读取到对应的数值

在这里插入图片描述

OPC-UA 代码验证

在验证之前首先右键项目,选择属性,修改 OPC-UA 属性中的”允许匿名登录“为是

在这里插入图片描述

配置写入数据的方法,将对应的数值从 300 修改为 3

在这里插入图片描述

执行代码,查看输出结果

在这里插入图片描述

  • 10
    点赞
  • 94
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: OPC通讯协议是指用于工业自动化系统中,实现不同厂商的设备和软件之间进行通信的一种通讯协议。它的全称是OLE for Process Control(对象链接与嵌入式技术用于过程控制),是由微软公司发布的标准。 Java是一种面向对象的编程语言,它具有跨平台性和易学性的特点。通过使用Java编程语言,可以方便地实现OPC通讯协议。 在Java中,可以通过调用一些特定的库或API来实现OPC通讯协议。这些库或API提供了一些方法和函数,使得开发人员能够在Java程序中与OPC服务器进行通信。通过这些方法和函数,可以实现对OPC服务器的读取和写入操作,获取实时数据和历史数据。此外,还可以通过Java中的事件机制,实现对OPC服务器中数据变化的实时监控。 在使用Java来实现OPC通讯协议时,需要首先导入相应的库或API,并建立与OPC服务器的连接。然后,可以通过调用相应的方法和函数来实现与OPC服务器的通信。在读取数据时,可以通过指定OPC服务器的地址和标签名来获取相应的数据。在写入数据时,可以通过指定OPC服务器的地址和标签名,设置相应的值。同时,还可以设置相应的订阅和回调函数,以实现对OPC服务器数据变化的监控。 总之,使用Java来实现OPC通讯协议可以有效地实现不同厂商设备和软件之间的通信,提高工业自动化系统的效率和可操作性。 ### 回答2: OPC是指开放式过程控制(Open Process Control)的英文缩写。它是一种可应用于工业自动化领域的通讯协议标准,旨在实现不同品牌或型号的设备和系统之间的互联互通。 OPC通讯协议的特点是开放性和互操作性。它采用了面向对象的设计理念,基于COM(Component Object Model)技术构建,在各种操作系统和编程语言环境下都能进行快速实施和开发。 在Java编程中,可以使用一些特定的库或API来实现OPC通讯协议的功能。常用的有OPC基础接口(OPC Foundation)和EasyOPCOPC Labs)等。 使用Java实现OPC通讯协议的主要步骤包括: 1. 导入相关的库或API,并设置运行环境。 2. 创建一个OPC客户端对象,用于与OPC服务器建立连接。 3. 通过客户端对象,使用相应的方法和属性从OPC服务器中读取或写入数据。 4. 根据需要,可以对数据进行处理和解析,也可以监听特定的事件。 5. 最后,关闭连接并释放资源。 使用Java实现OPC通讯协议可以使得不同品牌或型号的设备和系统之间能够方便地进行数据交换和共享,提高工业自动化系统的集成化和智能化水平。此外,Java作为一种兼容性强的编程语言,还具有跨平台性和多线程支持等特点,能够更好地满足工业控制领域对实时性和可靠性的要求。 总而言之,通过使用Java实现OPC通讯协议,可以有效地实现不同设备和系统之间的数据互联互通,提高工业自动化系统的效率和可扩展性。 ### 回答3: OPC通讯协议是基于COM(Component Object Model)的开放式通信协议,用于在工业自动化系统中实现设备之间的数据通信和集成。Java可以通过使用OPC Java库实现与OPC服务器之间的通信。 OPC通讯协议使Java开发人员能够轻松地访问、监视和控制不同设备的数据。它提供了一种标准化的方式,使得各种设备和系统可以使用相同的协议进行数据交换。Java开发人员可以使用OPC Java库将其应用程序连接到OPC服务器上,获取设备的数据并执行控制操作。 使用OPC Java库进行通信时,Java开发人员需要注意以下几点: 1. 引入OPC Java库:需要将OPC Java库添加到Java项目的类路径中,以便在代码中使用相关的类和方法。 2. 初始化OPC服务器连接:Java开发人员需要根据OPC服务器的地址和端口等信息初始化OPC服务器的连接。通常情况下,OPC服务器会提供一些方法和参数来帮助开发人员进行连接。 3. 读取数据:通过OPC Java库提供的方法,开发人员可以读取设备的数据,例如温度、压力等参数。开发人员可以根据自己的需求选择合适的数据读取方法。 4. 控制设备:通过OPC Java库提供的方法,开发人员可以对设备进行控制,例如打开/关闭设备、修改设备参数等。 总结起来,通过OPC通讯协议OPC Java库,Java开发人员可以实现与OPC服务器之间的数据通信和集成。这使得Java应用程序能够方便地与各种设备进行连接、数据读取和控制操作,实现工业自动化系统的开发和集成。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值