SpringBoot集成Milo库实现OPC UA客户端:连接、遍历节点、读取、写入、订阅与批量订阅

这篇具有很好参考价值的文章主要介绍了SpringBoot集成Milo库实现OPC UA客户端:连接、遍历节点、读取、写入、订阅与批量订阅。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

背景

前面我们搭建了一个本地的 PLC 仿真环境,并通过 KEPServerEX6 读取 PLC 上的数据,最后还使用 UAExpert 作为OPC客户端完成从 KEPServerEX6 这个OPC服务器的数据读取与订阅功能。在这篇文章中,我们将通过 SpringBoot 集成 Milo 库实现一个 OPC UA 客户端,包括连接、遍历节点、读取、写入、订阅与批量订阅等功能。

Milo库

Milo 库的 GitHub 地址:https://github.com/eclipse/milo

Milo 库提供了 OPC UA 的服务端和客户端 SDK ,显然,我们这里仅用到了OPC UA Client SDK

引入依赖

SpringBoot 后端项目中引入 Milo 库依赖(客户端 SDK )。

实现OPCUA客户端

连接

    /**
     * 创建OPC UA客户端
     *
     * @param ip
     * @param port
     * @param suffix
     * @return
     * @throws Exception
     */
    public OpcUaClient connectOpcUaServer(String ip, String port, String suffix) throws Exception {
        String endPointUrl = "opc.tcp://" + ip + ":" + port + suffix;
        Path securityTempDir = Paths.get(System.getProperty("java.io.tmpdir"), "security");
        Files.createDirectories(securityTempDir);
        if (!Files.exists(securityTempDir)) {
            throw new Exception("unable to create security dir: " + securityTempDir);
        }
        OpcUaClient opcUaClient = OpcUaClient.create(endPointUrl,
                endpoints ->
                        endpoints.stream()
                                .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri()))
                                .findFirst(),
                configBuilder ->
                        configBuilder
                                .setApplicationName(LocalizedText.english("eclipse milo opc-ua client"))
                                .setApplicationUri("urn:eclipse:milo:examples:client")
                                //访问方式
                                .setIdentityProvider(new AnonymousProvider())
                                .setRequestTimeout(UInteger.valueOf(5000))
                                .build()
        );
        opcUaClient.connect().get();
        Thread.sleep(2000); // 线程休眠一下再返回对象,给创建过程一个时间。
        return opcUaClient;
    }

遍历节点

    /**
     * 遍历树形节点
     *
     * @param client OPC UA客户端
     * @param uaNode 节点
     * @throws Exception
     */
    public void listNode(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());
            listNode(client, nd);
        }
    }

读取指定节点

    /**
     * 读取节点数据
     *
     * namespaceIndex可以通过UaExpert客户端去查询,一般来说这个值是2。
     * identifier也可以通过UaExpert客户端去查询,这个值=通道名称.设备名称.标记名称
     *
     * @param client
     * @param namespaceIndex
     * @param identifier
     * @throws Exception
     */
    public void readNodeValue(OpcUaClient client, int namespaceIndex, String identifier) throws Exception {
        //节点
        NodeId nodeId = new NodeId(namespaceIndex, identifier);

        //读取节点数据
        DataValue value = client.readValue(0.0, TimestampsToReturn.Neither, nodeId).get();

        // 状态
        System.out.println("Status: " + value.getStatusCode());

        //标识符
        String id = String.valueOf(nodeId.getIdentifier());
        System.out.println(id + ": " + value.getValue().getValue());
    }

写入指定节点

    /**
     * 写入节点数据
     *
     * @param client
     * @param namespaceIndex
     * @param identifier
     * @param value
     * @throws Exception
     */
    public void writeNodeValue(OpcUaClient client, int namespaceIndex, String identifier, Float value) throws Exception {
        //节点
        NodeId nodeId = new NodeId(namespaceIndex, identifier);
        //创建数据对象,此处的数据对象一定要定义类型,不然会出现类型错误,导致无法写入
        DataValue newValue = new DataValue(new Variant(value), null, null);
        //写入节点数据
        StatusCode statusCode = client.writeValue(nodeId, newValue).join();
        System.out.println("结果:" + statusCode.isGood());
    }

订阅指定节点

    /**
     * 订阅(单个)
     *
     * @param client
     * @param namespaceIndex
     * @param identifier
     * @throws Exception
     */
    private static final AtomicInteger atomic = new AtomicInteger();

    public void subscribe(OpcUaClient client, int namespaceIndex, String identifier) throws Exception {
        //创建发布间隔1000ms的订阅对象
        client
                .getSubscriptionManager()
                .createSubscription(1000.0)
                .thenAccept(t -> {
                    //节点
                    NodeId nodeId = new NodeId(namespaceIndex, identifier);
                    ReadValueId readValueId = new ReadValueId(nodeId, AttributeId.Value.uid(), null, null);
                    //创建监控的参数
                    MonitoringParameters parameters = new MonitoringParameters(UInteger.valueOf(atomic.getAndIncrement()), 1000.0, null, UInteger.valueOf(10), true);
                    //创建监控项请求
                    //该请求最后用于创建订阅。
                    MonitoredItemCreateRequest request = new MonitoredItemCreateRequest(readValueId, MonitoringMode.Reporting, parameters);
                    List<MonitoredItemCreateRequest> requests = new ArrayList<>();
                    requests.add(request);
                    //创建监控项,并且注册变量值改变时候的回调函数。
                    t.createMonitoredItems(
                            TimestampsToReturn.Both,
                            requests,
                            (item, id) -> item.setValueConsumer((it, val) -> {
                                System.out.println("nodeid :" + it.getReadValueId().getNodeId());
                                System.out.println("value :" + val.getValue().getValue());
                            })
                    );
                }).get();

        //持续订阅
        Thread.sleep(Long.MAX_VALUE);
    }

批量订阅指定节点

    /**
     * 批量订阅
     *
     * @param client
     * @throws Exception
     */
    public void subscribeBatch(OpcUaClient client) throws Exception {
        final CountDownLatch eventLatch = new CountDownLatch(1);
        //处理订阅业务
        handlerMultipleNode(client);
        //持续监听
        eventLatch.await();
    }

    /**
     * 处理订阅业务
     *
     * @param client OPC UA客户端
     */
    private void handlerMultipleNode(OpcUaClient client) {
        try {
            //创建订阅
            ManagedSubscription subscription = ManagedSubscription.create(client);
            List<NodeId> nodeIdList = new ArrayList<>();
            for (String id : batchIdentifiers) {
                nodeIdList.add(new NodeId(batchNamespaceIndex, id));
            }
            //监听
            List<ManagedDataItem> dataItemList = subscription.createDataItems(nodeIdList);
            for (ManagedDataItem managedDataItem : dataItemList) {
                managedDataItem.addDataValueListener((t) -> {
                    System.out.println(managedDataItem.getNodeId().getIdentifier().toString() + ":" + t.getValue().getValue().toString());
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

关于断线重连的批量订阅,可以参考文末源码,我没有进行实际测试。

测试

连接KEPServerEX6的OPC UA服务器

将上一篇文章中的 KEPServerEX6 作为 OPC UA 服务器来测试我们实现的客户端功能。这里 namespaceIndexidentifier 参考 KEPServerEX6 的配置或者 UAExpert 的右上角 Attribute 显示。

SpringBoot集成Milo库实现OPC UA客户端:连接、遍历节点、读取、写入、订阅与批量订阅

public class OpcUaStart {
    public void start() throws Exception {
        OpcUaClientService opcUaClientService = new OpcUaClientService();

        // 与OPC UA服务端建立连接,并返回客户端实例
        OpcUaClient client = opcUaClientService.connectOpcUaServer("127.0.0.1", "49320", "");

        // 遍历所有节点
        opcUaClientService.listNode(client, null);

        // 读取指定节点的值
//        opcUaClientService.readNodeValue(client, 2, "Demo.1500PLC.D1");
//        opcUaClientService.readNodeValue(client, 2, "Demo.1500PLC.D2");

        // 向指定节点写入数据
        opcUaClientService.writeNodeValue(client, 2, "Demo.1500PLC.D1", 6f);

        // 订阅指定节点
//        opcUaClientService.subscribe(client, 2, "Demo.1500PLC.D1");

        // 批量订阅多个节点
        List<String> identifiers = new ArrayList<>();
        identifiers.add("Demo.1500PLC.D1");
        identifiers.add("Demo.1500PLC.D2");

        opcUaClientService.setBatchNamespaceIndex(2);
        opcUaClientService.setBatchIdentifiers(identifiers);

//        opcUaClientService.subscribeBatch(client);
        opcUaClientService.subscribeBatchWithReconnect(client);
    }
}

记得在启动类中开启 OPC UA 的客户端。

@SpringBootApplication
public class SpringbootOpcuaApplication {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(SpringbootOpcuaApplication.class, args);
        OpcUaStart opcUa = new OpcUaStart();
        opcUa.start();
    }
}

连接Milo提供的测试性OPC UA服务器

Milo 官方提供了一个开放的 OPC UA 服务器: opc.tcp://milo.digitalpetri.com:62541/milo ,可以先使用 UAExpert 测试连接(我用的是匿名连接),查看其中的节点及地址信息。

SpringBoot集成Milo库实现OPC UA客户端:连接、遍历节点、读取、写入、订阅与批量订阅

public class OpcUaStart {
    public void start() throws Exception {
        OpcUaClientService opcUaClientService = new OpcUaClientService();

        // 与OPC UA服务端建立连接,并返回客户端实例
        OpcUaClient client = opcUaClientService.connectOpcUaServer("milo.digitalpetri.com", "62541", "/milo");

        // 遍历所有节点
//        opcUaClientService.listNode(client, null);

        // 读取指定节点的值
        opcUaClientService.readNodeValue(client, 2, "Dynamic/RandomInt32");
        opcUaClientService.readNodeValue(client, 2, "Dynamic/RandomInt64");

        // 向指定节点写入数据
//        opcUaClientService.writeNodeValue(client, 2, "Demo.1500PLC.D1", 6f);

        // 订阅指定节点
//        opcUaClientService.subscribe(client, 2, "Dynamic/RandomDouble");

        // 批量订阅多个节点
        List<String> identifiers = new ArrayList<>();
        identifiers.add("Dynamic/RandomDouble");
        identifiers.add("Dynamic/RandomFloat");

        opcUaClientService.setBatchNamespaceIndex(2);
        opcUaClientService.setBatchIdentifiers(identifiers);

//        opcUaClientService.subscribeBatch(client);
        opcUaClientService.subscribeBatchWithReconnect(client);
    }
}

测试结果如下:
SpringBoot集成Milo库实现OPC UA客户端:连接、遍历节点、读取、写入、订阅与批量订阅

可能遇到的问题

UaException: status=Bad_SessionClosed, message=The session was closed by the client.

原因分析: opcUaClient.connect().get(); 是一个异步的过程,可能在读写的时候,连接还没有创建好。

解决方法: Thread.sleep(2000); // 线程休眠一下再返回对象,给创建过程一个时间。

Reference

https://blog.csdn.net/u013457145/article/details/121283612

Source Code

https://github.com/heartsuit/demo-spring-boot/tree/master/springboot-opcua


If you have any questions or any bugs are found, please feel free to contact me.

Your comments and suggestions are welcome!文章来源地址https://www.toymoban.com/news/detail-483626.html

到了这里,关于SpringBoot集成Milo库实现OPC UA客户端:连接、遍历节点、读取、写入、订阅与批量订阅的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【OPC UA】C# 通过OpcUaHelper建立OPC客户端访问KEPServerEx6 OPC服务器数据

    OpcUaHelper 一个通用的opc ua客户端类库,基于.net 4.6.1创建,基于官方opc ua基金会跨平台库创建,封装了节点读写,批量节点读写,引用读取,特性读取,历史数据读取,方法调用,节点订阅,批量订阅等操作。还提供了一个节点浏览器工具。 KEPServerEX 第三方的OPC服务器,各不

    2023年04月11日
    浏览(28)
  • 二、springboot集成CAS客户端实现单点登录

    pom中引入依赖 yml中添加cas配置 读取CAS相关配置 cas配置类 单点登录接口demo 访问loingCas接口时,若未在CASserver登录,则会被拦截跳转到CAS的登陆页面,登陆成功后放行继续访问loginCas接口

    2024年02月15日
    浏览(40)
  • SpringBoot集成WebSocket实现客户端与服务端通信

    话不多说,直接上代码看效果! 一、服务端: 1、引用依赖 2、添加配置文件 WebSocketConfig 3、编写WebSocket服务端接收、发送功能   声明接口代码:   实现类代码: 4、如果不需要实现客户端功能,此处可选择前端调用,奉上代码 二、客户端: 1、引用依赖 2、自定义WebSocket客

    2024年01月23日
    浏览(40)
  • SpringBoot集成WebSocket实现客户端与服务端长连接通信

    场景: 1、WebSocket协议是用于前后端长连接交互的技术,此技术多用于交互不断开的场景。特点是连接不间断、更轻量,只有在关闭浏览器窗口、或者关闭浏览器、或主动close,当前会话对象才会关闭。 2、相较于 Http/Https 通信只能由客户端主动发起请求,而 Socket 通信不仅能

    2024年02月02日
    浏览(43)
  • springboot集成webstock实战:服务端数据推送数据到客户端实现实时刷新

        之前介绍过springboot集成webstock方式,具体参考: springboot集成websocket实战:站内消息实时推送 这里补充另外一个使用webstock的场景,方便其他同学理解和使用,废话不多说了,直接开始!简单介绍一下业务场景:     现在有一个投票活动,活动详情中会显示投票活动的参与人数、访

    2024年02月08日
    浏览(89)
  • Forest-声明式HTTP客户端框架-集成到SpringBoot实现调用第三方restful api并实现接口数据转换

    声明式HTTP客户端API框架,让Java发送HTTP/HTTPS请求不再难。它比OkHttp和HttpClient更高层, 是封装调用第三方restful api client接口的好帮手,是retrofit和feign之外另一个选择。 通过在接口上声明注解的方式配置HTTP请求接口。 官网: Forest   代码地址: forest: 声明式HTTP客户端API框架,让

    2024年02月04日
    浏览(100)
  • kafka:java集成 kafka(springboot集成、客户端集成)

    摘要 对于java的kafka集成,一般选用springboot集成kafka,但可能由于对接方kafka老旧、kafka不安全等问题导致kafak版本与spring版本不兼容,这个时候就得自己根据kafka客户端api集成了。 一、springboot集成kafka 具体官方文档地址:https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/

    2023年04月22日
    浏览(39)
  • SpringBoot2.0集成WebSocket,多客户端

    适用于单客户端,一个账号登陆一个客户端,登陆多个客户端会报错 The remote endpoint was in state [TEXT_FULL_WRITING]  这是因为此时的session是不同的,只能锁住一个session,解决此问题的方法把全局静态对象锁住,因为账号是唯一的

    2024年02月10日
    浏览(39)
  • SpringBoot集成Elasticsearch客户端(新旧版本)(2023-01-28)

    第一章 SpringBoot集成ElasticSearch(2023-01-28) 例如:业务中需要使用es,所以做一些客户端选型,熟悉一下基本的操作,所以记录这篇博客,有关概念理论性的文章还在整理过程中,后续会整理个系列 Spring认证中国教育管理中心-Spring Data Elasticsearch教程一 SpringData集成Elasticsearch Sp

    2024年02月07日
    浏览(62)
  • Springboot 集成WebSocket作为客户端,含重连接功能,开箱即用

    使用演示 只需要init后调用sendMessage方法即可,做到开箱即用。内部封装了失败重连接、断线重连接等功能。 基于Springboot工程 引入websocket依赖 开箱即用的工具类

    2024年02月04日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包