使用java语言以及opc工具包实现对opcua服务器数据的读取

1.关于本次使用的opc工具包

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

2.本次读取数据所需参数

下面的实体类为一个标签读取数据所需参数

public class Tag{
    private Long id;
	//  标签名称
    private String name;
   // opc服务器内的名字 通过此名字来读取对应的标签的数据
    private String opcName;
}

3.本次读取数据所需配置参数

public class OpcServer {
    /**
     * opc地址
     */
    private String opcUrl;

    /**
     * opc账号名
     */
    private String opcUsername;

    /**
     * opc密码
     */
    private String opcPassword;

    /**
     * opc cls id
     */
    private String opcClsId;

    /**
     * opc类型(OPCDA,OPCUA)
     */
    private String opcType;
}

4.加密证书解析

public class KeyStoreLoader {
    private static final Pattern IP_ADDR_PATTERN = Pattern.compile(
            "^(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])$");

    /**
     * 证数别名
     */
    private static final String CLIENT_ALIAS = "client-ai";
    /**
     * 获取私钥得密码
     */
    private static final char[] PASSWORD = "password".toCharArray();

    private final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 证书对象
     */
    private X509Certificate clientCertificate;

    /**
     * 密钥对对象
     */
    private KeyPair clientKeyPair;

    KeyStoreLoader load(Path baseDir) throws Exception {
        //创建一个使用`PKCS12`加密标准的KeyStore。KeyStore在后面将作为读取和生成证书的对象。
        KeyStore keyStore = KeyStore.getInstance("PKCS12");

        //PKCS12的加密标准的文件后缀是.pfx,其中包含了公钥和私钥。
        //而其他如.der等的格式只包含公钥,私钥在另外的文件中。
        Path serverKeyStore = baseDir.resolve("example-client.pfx");

        logger.info("Loading KeyStore at {}", serverKeyStore);

        //如果文件不存在则创建.pfx证书文件。
        if (!Files.exists(serverKeyStore)) {
            keyStore.load(null, PASSWORD);

            //用2048位的RAS算法。`SelfSignedCertificateGenerator`为Milo库的对象。
            KeyPair keyPair = SelfSignedCertificateGenerator.generateRsaKeyPair(2048);

            //`SelfSignedCertificateBuilder`也是Milo库的对象,用来生成证书。
            //中间所设置的证书属性可以自行修改。
            SelfSignedCertificateBuilder builder = new SelfSignedCertificateBuilder(keyPair)
                    .setCommonName("Eclipse Milo Example Client")
                    .setOrganization("digitalpetri")
                    .setOrganizationalUnit("dev")
                    .setLocalityName("Folsom")
                    .setStateName("CA")
                    .setCountryCode("US")
                    .setApplicationUri("urn:eclipse:milo:examples:client")
                    .addDnsName("localhost")
                    .addIpAddress("127.0.0.1");

            // Get as many hostnames and IP addresses as we can listed in the certificate.
            for (String hostname : HostnameUtil.getHostnames("0.0.0.0")) {
                if (IP_ADDR_PATTERN.matcher(hostname).matches()) {
                    builder.addIpAddress(hostname);
                } else {
                    builder.addDnsName(hostname);
                }
            }
            //创建证书
            X509Certificate certificate = builder.build();

            //设置对应私钥的别名,密码,证书链
            keyStore.setKeyEntry(CLIENT_ALIAS, keyPair.getPrivate(), PASSWORD, new X509Certificate[]{certificate});
            try (OutputStream out = Files.newOutputStream(serverKeyStore)) {
                //保存证书到输出流
                keyStore.store(out, PASSWORD);
            }
        } else {
            try (InputStream in = Files.newInputStream(serverKeyStore)) {
                //如果文件存在则读取
                keyStore.load(in, PASSWORD);
            }
        }

        //用密码获取对应别名的私钥。
        Key serverPrivateKey = keyStore.getKey(CLIENT_ALIAS, PASSWORD);
        if (serverPrivateKey instanceof PrivateKey) {
            //获取对应别名的证书对象。
            clientCertificate = (X509Certificate) keyStore.getCertificate(CLIENT_ALIAS);
            //获取公钥
            PublicKey serverPublicKey = clientCertificate.getPublicKey();
            //创建Keypair对象。
            clientKeyPair = new KeyPair(serverPublicKey, (PrivateKey) serverPrivateKey);
        }

        return this;
    }

    /**
     * 返回证书
     * @return
     */
    X509Certificate getClientCertificate() {
        return clientCertificate;
    }

    /**
     * 返回密钥对
     * @return
     */
    KeyPair getClientKeyPair() {
        return clientKeyPair;
    }
}

5. opc客户端创建及连接断开

public OpcUaClient createClient(){
        OpcUaClient opcUaClient = null;
        // 连接地址端口号
        try {
            // 安全策略选择 此处使用的是无安全策略 可根据具体项目情况来对安全策略进行修改过滤
            List<EndpointDescription> endpointDescriptions = DiscoveryClient.getEndpoints(url).get();
            EndpointDescription endpoint = endpointDescriptions.stream().filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri()))
                    .findFirst().orElseThrow(() -> new Exception("no desired endpoints returned"));
            OpcUaClientConfig config = OpcUaClientConfig.builder()
                    // opc ua定义的名
                    .setApplicationName(endpoint.getServer().getApplicationName())
                    // 地址
                    .setApplicationUri(endpoint.getServer().getApplicationUri())
                    // 安全策略配置
                    .setEndpoint(endpoint)
                    .setProductUri(endpoint.getServer().getProductUri())
                    .setIdentityProvider(getIdentityProvider())
                    // 等待时间
                    .setRequestTimeout(UInteger.valueOf(50000))
                    .build();
            opcUaClient = OpcUaClient.create(config);
            logger.info("初始化OPC UA Client......成功");
        }catch (Exception e){
            logger.error("初始化OPC UA Client失败, {}", e.getMessage());
            return null;
        }

        if (!opcUaClient.getSession().isDone()) {
            try {
                // synchronous connect
                opcUaClient.connect().get();
                logger.info("OPC UA Client连接connect成功");
            } catch (Exception e) {
                logger.error("OPC UA Client连接connect失败, {}", e.getMessage());
                opcUaClient.disconnect();
                return null;
            }
        }
        return opcUaClient;
   }
   
   public void disconnect() {
        runningState = false;
        try {
            client.disconnect().get();
            Stack.releaseSharedResources();
        } catch (InterruptedException | ExecutionException e) {
            logger.error("设备断开连接的时候发生了错误: {}", e.getMessage(), e);
        }
    }
    /**
     * 获取登录验证,没有输入用户名密码就匿名登录
     * @return
     */
    private IdentityProvider getIdentityProvider(String userName,String passWord) {
        if (userName != null && passWord != null) {
            return new UsernameProvider(userName, passWord);
        }
        return new AnonymousProvider();
    }

6. 使用opc对实时数据进行读取

/**
 *  OPC对象
 */
public class Opc extends SuperEntity {

    /**
     * opc地址
     */
    @TableField(value = "opc_url")
    @ApiModelProperty(value = "opc地址")
    private String opcUrl;

    /**
     * opc账号名
     */
    @TableField(value = "opc_username")
    @ApiModelProperty(value = "opc用户名")
    private String opcUsername;

    /**
     * opc密码
     */
    @TableField(value = "opc_password")
    @ApiModelProperty(value = "opc密码")
    private String opcPassword;

    /**
     * opc cls id
     */
    @TableField(value = "opc_cls_id")
    @ApiModelProperty(value = "opcClsId")
    private String opcClsId;

    /**
     * opc类型(OPCDA,OPCUA)
     */
    @TableField(value = "opc_type")
    @ApiModelProperty(value = "opc类型")
    private String opcType;

    /**
     * 标签id
     */
    @TableField(value = "tag_id")
    @ApiModelProperty(value = "标签id")
    private Long tagId;

    /**
     * 标签名
     */
    @TableField(value = "tag_name")
    @ApiModelProperty(value = "标签名")
    private String tagName;

    /**
     * opc地址
     */
    @TableField(value = "opc_name")
    @ApiModelProperty(value = "opc地址")
    private String opcName;
}

@Slf4j
public class OpcUtils {

    /**
     * 读取数据
     * @param tags
     * @param client
     * @return
     */
    public static List<DataValue> getValue(List<Opc> tags, OpcUaClient client,int nameSpaceIndex){
        List<DataValue> values = new ArrayList<>();
        try {
            List<NodeId> nodeIds = new ArrayList<>();
            for (Opc tag : tags) {
                NodeId nodeId = new NodeId(nameSpaceIndex, tag.getOpcName());
                nodeIds.add(nodeId);
            }
            CompletableFuture<List<DataValue>> future = client.readValues(0.0, TimestampsToReturn.Both, nodeIds);
            values = future.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return  values;
    }

    public 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("_")) {
                System.out.println(nd.getBrowseName().getName());
                continue;
            }
            System.out.println(nd.getBrowseName().getName());
            browseNode(client, nd);
        }
    }


    /**
     * 订阅
     * @param opcUaClient
     */
    public static void subscribe(OpcUaClient opcUaClient) throws ExecutionException, InterruptedException {
        OpcUaSubscriptionManager subscriptionManager = opcUaClient.getSubscriptionManager();
        opcUaClient.getSubscriptionManager()
                .createSubscription(1000.0)
                .thenAccept( t -> {
                    //节点
                    NodeId nodeId = new NodeId(2, "OPCUA.DeviceGroup.CX_001");
                    ReadValueId readValueId = new ReadValueId(nodeId, AttributeId.Value.uid(), null, null);
                    // 创建监控参数
                    MonitoringParameters parameters = new MonitoringParameters(UInteger.valueOf(1), 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(new Date());
                                System.out.println("=====订阅nodeid====== :" + it.getReadValueId().getNodeId());
                                System.out.println("=====订阅value====== :" + val.getValue().getValue());
                            }));
                })
                .get();

        // 持续订阅
        Thread.sleep(2000);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值