在Java 中 利用Milo通信库,实现OPCUA客户端,并生成证书

news2024/9/23 17:18:52

程序结构:
在这里插入图片描述

配置文件resources:

在这里插入图片描述

opcua.properties

西门子PLC端口号为4840,kepserver为49320

#opcua服务端配置参数
#opcua.server.endpoint.url=opc.tcp://192.168.2.102:49320
opcua.server.endpoint.url=opc.tcp://192.168.2.11:4840
opcua.server.idp.username=Administrator
opcua.server.idp.password=123456

#opcua客户端配置参数
opcua.client.app.name=zgOpcUaClient
opcua.client.app.uri=urn:Shanghai:Kx:KxAutomation:Zg
opcua.client.cert.path=D:/zhengshu/
opcua.client.cert.file=zg-client.pfx
opcua.client.cert.alias=zgclient-ai
opcua.client.cert.common.name=KxZg
opcua.client.cert.organization=Kaixin
opcua.client.cert.organization.unit=Kx
opcua.client.cert.locality.name=Terran
opcua.client.cert.state.name=Shanghai
opcua.client.cert.country.code=CN
opcua.client.cert.dns.name=Zg
opcua.client.cert.ip.address=127.0.0.1
opcua.client.cert.keystore.password=123456

它对应的实体类调用:

package com.zg.mymes.myConnPLC.myOPCUA.config;

import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

/**
 * @Auther: Zg
 * @Date: 2022/11/23 - 11 - 23 - 15:07
 * @Description: com.zg.mymes.myConnPLC.myOPCUA.config
 * @version: 1.0
 */
@Getter
@Configuration
@PropertySource("classpath:opcua.properties")
public class MyOPCUAProperties {
    @Value("${opcua.server.endpoint.url}")
    private String endpointUrl;
    @Value("${opcua.server.idp.username}")
    private String idpUsername;
    @Value("${opcua.server.idp.password}")
    private String idpPassword;
    @Value("${opcua.client.app.name}")
    private String appName;
    @Value("${opcua.client.app.uri}")
    private String appUri;
    @Value("${opcua.client.cert.path}")
    private String certPath;
    @Value("${opcua.client.cert.file}")
    private String certFile;
    @Value("${opcua.client.cert.alias}")
    private String certAlias;
    @Value("${opcua.client.cert.common.name}")
    private String commonName;
    @Value("${opcua.client.cert.organization}")
    private String organization;
    @Value("${opcua.client.cert.organization.unit}")
    private String orgUnit;
    @Value("${opcua.client.cert.locality.name}")
    private String localityName;
    @Value("${opcua.client.cert.state.name}")
    private String stateName;
    @Value("${opcua.client.cert.country.code}")
    private String countryCode;
    @Value("${opcua.client.cert.dns.name}")
    private String dnsName;
    @Value("${opcua.client.cert.ip.address}")
    private String ipAddress;
    @Value("${opcua.client.cert.keystore.password}")
    private String keyPassword;
}

opcnode.properties:

opcnode.index,西门子PLC为3,kepserver为2

opcnode.index=3
opcnode.var.var0="S7MesData"."S7Real"[0]
opcnode.var.var1="S7MesData"."S7Real"[1]
opcnode.var.var2="S7MesData"."S7Real"[2]
opcnode.var.var3="S7MesData"."S7Real"[3]
opcnode.var.var4="S7MesData"."S7Real"[4]
opcnode.var.var5="S7MesData"."S7Real"[5]
opcnode.var.var6="S7MesData"."S7Real"[100]
opcnode.var.var7="S7MesData"."S7String"[0]
opcnode.var.var8="S7MesData"."S7Int"[0]

它对应的实体类调用:

package com.zg.mymes.myConnPLC.myOPCUA.config;

import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

/**
 * @Auther: Zg
 * @Date: 2022/11/23 - 11 - 23 - 15:27
 * @Description: com.zg.mymes.myConnPLC.myOPCUA.config
 * @version: 1.0
 *
 * 相当于点表
 * */
@Getter
@Configuration
@PropertySource("classpath:opcnode.properties")
public class MyOPCNode {
    @Value("${opcnode.index}")
    private String index;

    @Value("${opcnode.var.var0}")
    private String var0;
    @Value("${opcnode.var.var1}")
    private String var1;
    @Value("${opcnode.var.var2}")
    private String var2;
    @Value("${opcnode.var.var3}")
    private String var3;
    @Value("${opcnode.var.var4}")
    private String var4;
    @Value("${opcnode.var.var5}")
    private String var5;
    @Value("${opcnode.var.var6}")
    private String var6;
    @Value("${opcnode.var.var7}")
    private String var7;
    @Value("${opcnode.var.var8}")
    private String var8;
}

生成证书类:KeyStoreLoader

package com.zg.mymes.myConnPLC.myOPCUA.cert;

import org.eclipse.milo.opcua.sdk.server.util.HostnameUtil;
import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateBuilder;
import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.*;
import java.security.cert.X509Certificate;
import java.util.regex.Pattern;

/**
 * @Auther: Zg
 * @Date: 2022/11/23 - 11 - 23 - 15:01
 * @Description: com.zg.mymes.myConnPLC.myOPCUA.cert
 * @version: 1.0
 */
@Component
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 = "jlclient-ai";
    // 获取私钥的密码
    private static final char[] PASSWORD = "123456".toCharArray();

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

    // 证书对象
    private X509Certificate clientCertificate;
    // 密钥对对象
    private KeyPair clientKeyPair;

    /**
     * @MethodName: load
     * @Description: load
     * @param baseDir
     * @return
     * @throws Exception
     * @CreateTime 2019年12月11日 下午4:02:43
     */
    public KeyStoreLoader load(Path baseDir) throws Exception {
        // 创建一个使用`PKCS12`加密标准的KeyStore。KeyStore在后面将作为读取和生成证书的对象。
        KeyStore keyStore = KeyStore.getInstance("PKCS12");

        // PKCS12的加密标准的文件后缀是.pfx,其中包含了公钥和私钥。
        // 而其他如.der等的格式只包含公钥,私钥在另外的文件中。
        Path serverKeyStore = baseDir.resolve("zg-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("KxZg")
                    .setOrganization("Kaixin")
                    .setOrganizationalUnit("Kx")
                    .setLocalityName("Terran")
                    .setStateName("Shanghai")
                    .setCountryCode("CN")
                    .setApplicationUri("urn:Shanghai:Kx:KxAutomation:Zg")
                    .addDnsName("Zg")
                    .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;
    }

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

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

OPCUA订阅,写入,读取等工具类:

ClientRunner:

package com.zg.mymes.myConnPLC.myOPCUA.client;

import com.zg.mymes.myConnPLC.myOPCUA.cert.KeyStoreLoader;
import com.zg.mymes.myConnPLC.myOPCUA.config.MyOPCUAProperties;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.api.config.OpcUaClientConfig;
import org.eclipse.milo.opcua.sdk.client.api.identity.UsernameProvider;
import org.eclipse.milo.opcua.stack.client.DiscoveryClient;
import org.eclipse.milo.opcua.stack.core.Stack;
import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned;
import org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Predicate;

/**
 * @Auther: Zg
 * @Date: 2022/11/23 - 11 - 23 - 15:04
 * @Description: com.zg.mymes.myConnPLC.myOPCUA.client
 * @version: 1.0
 */
@Slf4j
@Component
public class ClientRunner {

    private final CompletableFuture<OpcUaClient> future = new CompletableFuture<>();

    @Autowired
    private MyOPCUAProperties properties;

    @Autowired
    private KeyStoreLoader keyStoreLoader;

    /**
     * @MethodName: run
     * @Description: 启动
     * @return
     * @throws Exception
     * @CreateTime 2019年12月18日 下午4:03:47
     */
    public OpcUaClient run() throws Exception {

        OpcUaClient client = createClient();

        future.whenCompleteAsync((c, ex) -> {
            if (ex != null) {
                log.error("Error running example: {}", ex.getMessage(), ex);
            }

            try {
                c.disconnect().get();
                Stack.releaseSharedResources();
            } catch (InterruptedException | ExecutionException e) {
                log.error("Error disconnecting:", e.getMessage(), e);
            }
        });

        return client;
    }

    /**
     * @MethodName: createClient
     * @Description: 创建客户端
     * @return
     * @throws Exception
     * @CreateTime 2019年12月18日 下午4:02:54
     */
    private OpcUaClient createClient() throws Exception {

        Path securityTempDir = Paths.get(properties.getCertPath(), "security");

        Files.createDirectories(securityTempDir);
        if (!Files.exists(securityTempDir)) {
            log.error("unable to create security dir: " + securityTempDir);
            return null;
        }

        KeyStoreLoader loader = keyStoreLoader.load(securityTempDir);

        // 搜索OPC节点
        List<EndpointDescription> endpoints = null;
        try {
            endpoints = DiscoveryClient.getEndpoints(properties.getEndpointUrl()).get();
        } catch (Throwable e) {
            // try the explicit discovery endpoint as well
            String discoveryUrl = properties.getEndpointUrl();

            if (!discoveryUrl.endsWith("/")) {
                discoveryUrl += "/";
            }
            discoveryUrl += "discovery";

            log.info("Trying explicit discovery URL: {}", discoveryUrl);
            endpoints = DiscoveryClient.getEndpoints(discoveryUrl).get();
        }

        //OPC服务器地址
        EndpointDescription endpoint = endpoints.stream()
                .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri())).filter(endpointFilter())
                .findFirst().orElseThrow(() -> new Exception("no desired endpoints returned"));

        //setKeyPair()接受一个KeyPair对象表示密匙对。
        //setEndpoint()接受一个EndpointDescription对象,就是设置刚刚我们选择的节点就可以了。
        //setIdentityProvider()该方法表示指定客户端使用的访问验证方式
        OpcUaClientConfig config = OpcUaClientConfig.builder()
                .setApplicationName(LocalizedText.english("zgOpcUaClient"))
                .setApplicationUri("urn:Shanghai:Kx:KxAutomation:Zg")
                .setCertificate(loader.getClientCertificate())
                .setKeyPair(loader.getClientKeyPair())
                .setEndpoint(endpoint)
                .setIdentityProvider(new UsernameProvider("Administrator", "123456"))
//				.setIdentityProvider(new AnonymousProvider()) // 匿名验证
                .setRequestTimeout(Unsigned.uint(5000)).build();

        return OpcUaClient.create(config);
    }

    /**
     * @MethodName: endpointFilter
     * @Description: endpointFilter
     * @return
     * @CreateTime 2019年12月18日 下午4:06:22
     */
    private Predicate<EndpointDescription> endpointFilter() {
        return e -> true;
    }

    /**
     * @return the future
     */
    public CompletableFuture<OpcUaClient> getFuture() {
        return future;
    }

}

ClientHandler:

package com.zg.mymes.myConnPLC.myOPCUA.client;

import com.google.common.collect.ImmutableList;
import com.zg.mymes.myConnPLC.myOPCUA.opcEntities.NodeEntity;
import com.zg.mymes.myConnPLC.myOPCUA.opcEntities.SubscriptNode;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.api.nodes.VariableNode;
import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaSubscription;
import org.eclipse.milo.opcua.stack.core.AttributeId;
import org.eclipse.milo.opcua.stack.core.BuiltinDataType;
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode;
import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UByte;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned;
import org.eclipse.milo.opcua.stack.core.types.enumerated.MonitoringMode;
import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
import org.eclipse.milo.opcua.stack.core.types.structured.MonitoredItemCreateRequest;
import org.eclipse.milo.opcua.stack.core.types.structured.MonitoringParameters;
import org.eclipse.milo.opcua.stack.core.types.structured.ReadValueId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * @Auther: Zg
 * @Date: 2022/11/23 - 11 - 23 - 15:10
 * @Description: com.zg.mymes.myConnPLC.myOPCUA.client
 * @version: 1.0
 */
@Slf4j
@Service
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ClientHandler {

    // 客户端实例
    private OpcUaClient client = null;

    @Autowired
    private ClientRunner clientRunner;

    private SubscriptNode myNode = new SubscriptNode();

    private List<NodeEntity> nodeEntities = new ArrayList<NodeEntity>();

    /**
     *
     * @MethodName: connect
     * @Description: connect
     * @throws Exception
     * @CreateTime 2019年12月18日 上午10:41:09
     */
    public String connect() throws Exception {

        if (client != null) {
            return "客户端已创建";
        }

        client = clientRunner.run();

        if (client == null) {
            return "客户端配置实例化失败";
        }

        // 创建连接
        client.connect().get();
        return "创建连接成功";
    }

    /**
     * @MethodName: disconnect
     * @Description: 断开连接
     * @return
     * @throws Exception
     * @CreateTime 2019年12月18日 上午10:45:21
     */
    public String disconnect() throws Exception {

        if (client == null) {
            return "连接已断开";
        }

        // 断开连接
        clientRunner.getFuture().complete(client);
        client = null;
        return "断开连接成功";
    }

    /**
     * @MethodName: subscribe
     * @Description: 订阅节点变量
     * @throws Exception
     * @CreateTime 2019年12月18日 上午10:38:11
     */
    public String subscribe(List<NodeEntity> nodes) throws Exception {
        this.nodeEntities = nodes;
        if (client == null) {
            return "找不到客户端,操作失败";
        }

//		List<Node> ns = client.getAddressSpace().browse(new NodeId(2, "模拟通道一.模拟设备一")).get();

        // 查询订阅对象,没有则创建
        UaSubscription subscription = null;
        ImmutableList<UaSubscription> subscriptionList = client.getSubscriptionManager().getSubscriptions();
        if (CollectionUtils.isEmpty(subscriptionList)) {
            subscription = client.getSubscriptionManager().createSubscription(1000.0).get();
        } else {
            subscription = subscriptionList.get(0);
        }

        // 监控项请求列表
        List<MonitoredItemCreateRequest> requests = new ArrayList<>();

        if (!CollectionUtils.isEmpty(nodes)) {
            for (NodeEntity node : nodes) {
                // 创建监控的参数
                MonitoringParameters parameters = new MonitoringParameters(subscription.nextClientHandle(), 1000.0, // sampling
                        // interval
                        null, // filter, null means use default
                        Unsigned.uint(10), // queue size
                        true // discard oldest
                );
                // 创建订阅的变量, 创建监控项请 求
                MonitoredItemCreateRequest request = new MonitoredItemCreateRequest(
                        new ReadValueId(new NodeId(node.getIndex(), node.getIdentifier()), AttributeId.Value.uid(),
                                null, null),
                        MonitoringMode.Reporting, parameters);
                requests.add(request);
            }
        }

        // 创建监控项,并且注册变量值改变时候的回调函数
        subscription.createMonitoredItems(TimestampsToReturn.Both, requests, (item, id) -> {
            item.setValueConsumer((i, v) -> {
                log.info("========+++==========");
                log.info("item={}, value={}", i.getReadValueId(), v.getValue().getValue());
                log.info("========+++==========");
//                NodeId nodeId = i.getReadValueId().getNodeId();
//                Variant value = v.getValue();

                //将OPC读取的数据存入nodeEntities
                for (NodeEntity nodeEntity:this.nodeEntities
                     ) {
                    if (i.getReadValueId().getNodeId().getIdentifier().equals(nodeEntity.getIdentifier())){
                        nodeEntity.setValue(v.getValue().getValue());
                    }
                }
            });
        }).get();

        return "订阅成功";
    }

    /**
     * @MethodName: write
     * @Description: 变节点量写入
     * @param node
     * @throws Exception
     * @CreateTime 2019年12月18日 上午9:51:40
     */
    public String write(NodeEntity node) throws Exception {

        if (client == null) {
            return "找不到客户端,操作失败";
        }

        NodeId nodeId = new NodeId(node.getIndex(), node.getIdentifier());
        Variant value = null;
        switch (node.getType()) {
            case "int":
                value = new Variant(Integer.parseInt(node.getValue().toString()));
                break;
            case "boolean":
                value = new Variant(Boolean.parseBoolean(node.getValue().toString()));
                break;
            case "Short":
                value = new Variant(Short.parseShort(node.getValue().toString()));
                break;
            case "String":
                value = new Variant(node.getValue().toString());
                break;
            case "Float":
                value = new Variant(Float.parseFloat(node.getValue().toString()));
                break;
            case "UByte":
                value = new Variant(UByte.valueOf(node.getValue().toString()));
                break;
        }
        DataValue dataValue = new DataValue(value, null, null);

        StatusCode statusCode = client.writeValue(nodeId, dataValue).get();

        return "节点【" + node.getIdentifier() + "】写入状态:" + statusCode.isGood();
    }

    /**
     * @MethodName: read
     * @Description: 读取
     * @param node
     * @return
     * @throws Exception
     * @CreateTime 2019年12月19日 下午2:40:34
     */
    public String read(NodeEntity node) throws Exception {

        if (client == null) {
            return "找不到客户端,操作失败";
        }

        NodeId nodeId = new NodeId(node.getIndex(), node.getIdentifier());
        VariableNode vnode = client.getAddressSpace().createVariableNode(nodeId);
        DataValue value = vnode.readValue().get();
        log.info("Value={}", value);

        Variant variant = value.getValue();
        log.info("Variant={}", variant.getValue());

        log.info("BackingClass={}", BuiltinDataType.getBackingClass(variant.getDataType().get()));

        return "节点【" + node.getIdentifier() + "】:" + variant.getValue();
    }
}

生成证书,读,写,验证等操作:

package com.zg.mymes.controller;

import com.zg.mymes.myConnPLC.myOPCUA.opcEntities.NodeEntity;
import com.zg.mymes.myConnPLC.myOPCUA.cert.KeyStoreLoader;
import com.zg.mymes.myConnPLC.myOPCUA.client.ClientHandler;
import com.zg.mymes.myConnPLC.myOPCUA.config.MyOPCNode;
import com.zg.mymes.myConnPLC.myOPCUA.config.MyOPCUAProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Auther: Zg
 * @Date: 2022/11/23 - 11 - 23 - 15:32
 * @Description: com.zg.mymes.controller
 * @version: 1.0
 */
@Controller
@Slf4j
public class OPCUAController {
    @Autowired
    private ClientHandler clientHandler;

    @Autowired
    public KeyStoreLoader keyStoreLoader;

    @Autowired
    private MyOPCUAProperties properties;

    @Autowired
    private MyOPCNode myOPCNode;

    /**
     *connect
     * @return
     */
    @RequestMapping("/connect")
    @ResponseBody
    public String connect() {

        try {
            return clientHandler.connect();
        } catch (Exception e) {
            e.printStackTrace();
            return "fail";
        }
    }


    @RequestMapping("/disconnect")
    @ResponseBody
    public String disconnect() {

        try {
            return clientHandler.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
            return "fail";
        }
    }

    @RequestMapping("/subscribe")
    @ResponseBody
    public String subscribe(HttpServletRequest request) {

        try {
            List<NodeEntity> nodes = Stream.of(request.getParameter("id").split(","))
                    .map(id -> NodeEntity.builder().index(2).identifier(id).build()).collect(Collectors.toList());

            return clientHandler.subscribe(nodes);
        } catch (Exception e) {
            e.printStackTrace();
            return "fail";
        }
    }



    @RequestMapping("/write")
    @ResponseBody
    public String write(HttpServletRequest request) {

        NodeEntity node = NodeEntity.builder().index(2).identifier(request.getParameter("id"))
                .value(request.getParameter("value")).type(request.getParameter("type")).build();

        try {
            return clientHandler.write(node);
        } catch (Exception e) {
            e.printStackTrace();
            return "fail";
        }
    }

    @RequestMapping("/read")
    @ResponseBody
    public String read(HttpServletRequest request) {

        NodeEntity node = NodeEntity.builder().index(2).identifier(request.getParameter("id")).build();

        try {
            return clientHandler.read(node);
        } catch (Exception e) {
            e.printStackTrace();
            return "fail";
        }
    }

    //创建证书
    @RequestMapping("/createCert")
    @ResponseBody
    public String Created() throws Exception {
        Path securityTempDir = Paths.get(properties.getCertPath(), "security");
        keyStoreLoader.load(securityTempDir);
        log.info("==========+++++");
        log.info(securityTempDir.toString());
        return securityTempDir.toString();
    }
}

开启定时器,订阅模式读取变量:

package com.zg.mymes.helper;

import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import com.zg.mymes.entities.ActualData;
import com.zg.mymes.myConnPLC.myOPCUA.opcEntities.NodeEntity;
import com.zg.mymes.myConnPLC.myOPCUA.cert.KeyStoreLoader;
import com.zg.mymes.myConnPLC.myOPCUA.client.ClientHandler;
import com.zg.mymes.myConnPLC.myOPCUA.config.MyOPCNode;
import com.zg.mymes.myConnPLC.myS7.S7ConnHelper;
import com.zg.mymes.myConnPLC.myS7.myS7entities.MyS7Entity;
import com.zg.mymes.service.ActualDataService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.ExceptionHandler;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.Timestamp;
import java.util.*;
import java.util.function.Consumer;

/**
 * @Auther: Zg
 * @Date: 2022/11/23 - 11 - 23 - 16:56
 * @Description: com.zg.mymes.helper
 * @version: 1.0
 */
@Service
@Slf4j
public class Listener {
    @Autowired
    private ClientHandler clientHandler;

    @Autowired
    public KeyStoreLoader keyStoreLoader;

    @Autowired
    private MyOPCNode myOPCNode;

    @Autowired
    public S7ConnHelper s7ConnHelper;

    @Autowired
    public ActualDataService actualDataService;


    Integer errorTimes = 0;
    Boolean last = false;
    Boolean trigIn = false;
    Boolean trigQ = false;

    //OPC
    @PostConstruct
    public void OpcUAConn() throws Exception {
        String connect = clientHandler.connect();
        log.info(connect);
        log.info("==========");


        ArrayList<NodeEntity> nodeEntities = new ArrayList<>();
        NodeEntity build0 = NodeEntity.builder()
                .identifier(myOPCNode.getVar0())
                .index(Integer.parseInt(myOPCNode.getIndex())).build();
        NodeEntity build1 = NodeEntity.builder()
                .identifier(myOPCNode.getVar1())
                .index(Integer.parseInt(myOPCNode.getIndex())).build();

        NodeEntity build2 = NodeEntity.builder()
                .identifier(myOPCNode.getVar2())
                .index(Integer.parseInt(myOPCNode.getIndex())).build();

        NodeEntity build3 = NodeEntity.builder()
                .identifier(myOPCNode.getVar3())
                .index(Integer.parseInt(myOPCNode.getIndex())).build();

        NodeEntity build4 = NodeEntity.builder()
                .identifier(myOPCNode.getVar4())
                .index(Integer.parseInt(myOPCNode.getIndex())).build();

        NodeEntity build5 = NodeEntity.builder()
                .identifier(myOPCNode.getVar5())
                .index(Integer.parseInt(myOPCNode.getIndex())).build();

        NodeEntity build6 = NodeEntity.builder()
                .identifier(myOPCNode.getVar6())
                .index(Integer.parseInt(myOPCNode.getIndex())).build();

        NodeEntity build7 = NodeEntity.builder()
                .identifier(myOPCNode.getVar7())
                .index(Integer.parseInt(myOPCNode.getIndex())).build();

        NodeEntity build8 = NodeEntity.builder()
                .identifier(myOPCNode.getVar8())
                .index(Integer.parseInt(myOPCNode.getIndex())).build();

        nodeEntities.add(build0);
        nodeEntities.add(build1);
        nodeEntities.add(build2);
        nodeEntities.add(build3);
        nodeEntities.add(build4);
        nodeEntities.add(build5);
        nodeEntities.add(build6);
        nodeEntities.add(build7);
        nodeEntities.add(build8);
        String subscribe = clientHandler.subscribe(nodeEntities);
        log.info(subscribe);

        //写入,注意类型
//        NodeEntity build = NodeEntity.builder()
//                .identifier(myOPCNode.getVar8())
//                .index(Integer.parseInt(myOPCNode.getIndex()))
//                .type("Short")
//                .value("19")
//                .build(); //整数


//        NodeEntity buildFloat = NodeEntity.builder()
//                .identifier(myOPCNode.getVar6())
//                .index(Integer.parseInt(myOPCNode.getIndex()))
//                .type("Float")
//                .value("55.5")
//                .build(); //浮点数

//        NodeEntity build = NodeEntity.builder()
//                .identifier(myOPCNode.getVar7())
//                .index(Integer.parseInt(myOPCNode.getIndex()))
//                .type("String")
//                .value("abcde")
//                .build(); //字符
//        clientHandler.write(build);
    }



    //OPC
    @Async
    @Scheduled(cron = "*/1 * * * * *")
    public void OpcUATestPlc() throws Exception {
        log.info("===========-------============");
        log.info(clientHandler.getNodeEntities().toString());
        log.info("===========-------============");




    }






本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/351252.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年私募股权基金研究报告

第一章 概况 PE是私募&#xff0c;也即私募投资基金&#xff0c;是指以非公开发行方式向合格投资者募集的&#xff0c;投资于股票、股权、债券、期货、期权、基金份额及投资合同约定的其他投资标的&#xff08;如艺术品、红酒等&#xff09;的投资基金&#xff0c;简称私募基金…

【PyTorch学习3】《PyTorch深度学习实践》——反向传播(Back Propagation)

目录一、Tensor1.定义2.Tensor常见形式3.torch.tensor和torch.Tensor4.Tensor.grad二、反向传播一、Tensor 1.定义 张量的定义是一个可用来表示在一些矢量、标量和其他张量之间的线性关系的多线性函数。 在PyTorch上有这样一句话&#xff0c;A torch.Tensor is a multi…

TeeChart for .NET 2023.2.13 Crack

TeeChart NET Pro Edition 是一个基于 Nuget 的图表控件&#xff0c;旨在为您的 NET 应用程序提供即时图表、地图和仪表功能。TeeChart 拥有数十种图表类型、统计函数和开发人员工具&#xff0c;是当今开发人员可用的最全面的图表库之一。易于使用设计时编辑器和直观的编程结构…

蒙特卡洛计算圆周率

使用MC计算圆周率的小例子&#xff0c;使用python的numpy&#xff0c;matplotlib库import numpy as npimport matplotlib.pyplot as pltdef mc_calculate_pi(t):np.random.seed(t)rand_num np.random.rand(t)rand_num2 np.random.rand(t)l1 rand_num-0.5l2 rand_num2-0.5l0…

记录-配置unity多人游戏服务器中的踩坑经历

最近沉迷于gpt这个“魔法海螺”无法自拔&#xff0c;总之这是题外话了&#xff0c;Let.s go 文章目录前言一、IIS的HTTPS站点设置二、VS的远程部署设置三、运行.NET CORE的IIS站点——注意项四、SQL Server使用sa账号不能登录的问题解决五、SVN忽略so文件的提交&#xff0c;导致…

深入浅出带你学习IIS中间件常见漏洞

前言 在渗透过程中我们经常会思考如何去进行渗透&#xff0c;假如给了我们一个有很多中间件的网站我们要如何进行渗透呢&#xff1f;于是本人准备写一些包含常见中间件漏洞攻击的总结&#xff0c;希望可以帮到大家在面临不同渗透环境时会有渗透思路&#xff0c;本篇文章就先给…

开源单点登录MaxKey和JumpServer 堡垒机单点登录集成指南

1. MaxKey介绍 MaxKey社区专注于身份安全管理(IM)、单点登录(SSO)和云身份认证(IDaas)领域&#xff0c;将为客户提供企业级的身份管理和认证&#xff0c;提供全面的4A安全管理&#xff08;指Account&#xff0c;Authentication&#xff0c;Authorization和Audit&#xff09;。…

HCIP中期实验(华为)

题目&#xff1a; 实验要求&#xff1a; 1、该拓扑为公司网络&#xff0c;其中包括公司总部、公司分部以及公司骨干网&#xff0c;不包含运营商公网部分。 2、设备名称均使用拓扑上名称改名&#xff0c;并且区分大小写。 3、整张拓扑均使用私网地址进行配置。 4、整张网络中&am…

javaEE 初阶 — 延迟应答与捎带应答

文章目录1. 延迟应答2. 捎带应答TCP 工作机制&#xff1a;确认应答机制 超时重传机制 连接管理机制 滑动窗口 流量控制与拥塞控制 1. 延迟应答 延时应答 也是提升效率的机制&#xff0c;也是在滑动窗口基础上搞点事情。 滑动窗口的关键是让窗口大小大一点&#xff0c;传输…

骑行是什么?骑行是这十一非物质的东西

很多骑友一直在想&#xff0c;我们骑行是为什么&#xff1f;又没有什么实质性的价值&#xff0c;又不算是劳动&#xff0c;但究竟是什么&#xff1f;让我们坚持不懈的在坚持&#xff0c;在热爱&#xff1f; 1.骑行是一种自由&#xff0c;它可以让你放松心情&#xff0c;放空思绪…

JS中的数据类型判断

JS中的数据类型 基本数据类型 number、string、boolean、null、undefined、symbol 引用数据类型 object、array、function、date、regexp 判断数据类型的方法有 typeof 、instanceof、constructor、toString() typeof用法 从上面可以看出&#xff0c;typeof 对于JS中基本类型…

Promise学习笔记(一)

课程内容 1、Promise基础 2、手写Promise 3、Async和Await 4、应用实战 1、初始Promise 1.1、promise介绍 1.2、回调地狱&#xff08;嵌套金字塔&#xff09; 1.3、promise使用 &#xff08;1&#xff09;then(成功函数&#xff0c;失败函数)&#xff1a;要么使用then传…

滑块连杆模型仿真(三菱ST+博途SCL计算源代码)

由滑块位置逆计算曲柄角度,请参看下面的文章链接。这篇博客主要计算由曲柄转动角度计算滑块位置。 https://blog.csdn.net/m0_46143730/article/details/128943228https://blog.csdn.net/m0_46143730/article/details/128943228简单的看下连杆滑块模型 模型的数学推导过程,大…

SpringCloud Alibaba项目环境

前言 有小伙伴说自己搭建Spring Cloud项目时&#xff0c;总会出现很多问题&#xff0c;项目无法正常启动&#xff0c;就算启动成功了也会出现一些难以预料的异常。 项目无法正常启动或者项目搭建不起来的原因大多是各个组件间的依赖和版本问题&#xff0c;这里就提供一个可以…

虹科HiveMQ MQTT解决方案在奔驰汽车制造中的应用

前言 戴姆勒股份公司&#xff08;Daimler AG&#xff09;&#xff0c;总部位于德国斯图加特&#xff0c;是全球最大的商用车制造商&#xff0c;全球第一大豪华车生产商、第二大卡车生产商。公司旗下包括梅赛德斯-奔驰汽车、梅赛德斯-奔驰轻型商用车、戴姆勒载重车和戴姆勒金融…

excel批处理:如何按给定目录批量创建系列文件夹

建立一个文件夹&#xff0c;小意思&#xff1b;建立十个文件夹&#xff0c;也很快完成&#xff1b;建立171个文件夹&#xff0c;这就是一项大工程了。如果不能批量创建的话&#xff0c;2个小时能完成就算不错了。收到领导的小要求&#xff0c;按照下面图片中的目录建立文件夹。…

基于SpringBoot的智慧社区网站

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏…

Spring Cloud Alibaba--gateway微服务详解之网关(二)

1、网关介绍 上篇对微服务中的nacos注册中心进行集成讲解。nacos主要作用是管理多服务之间复杂关系的组件。微服务是非常庞大且问题突出的架构&#xff0c;HTTP协议具有跨源资源共享 (CORS) Cross- Origin Resource Sharing机制&#xff0c;而处于安全考虑往往前端架构都会对跨…

Linux配置JDK

目录 第一步&#xff1a;下载JDK 第二步&#xff1a;上传jdk到服务器 第三步&#xff1a;解压并移动到指定目录 第四步&#xff1a;配置JAVA环境变量 配置所有用户 配置个别用户&#xff0c;以user1用户为例 第一步&#xff1a;下载JDK 自己下载jdk&#xff0c;网上下载或者…

云原生安全检测器 Narrows发布,在Harbor上增加容器安全的动态扫描

国内外的用户都在使用云原生技术来提高应用的开发效率和可管理性&#xff0c;不少用户运用开源 Harbor 制品仓库&#xff0c;管理敏捷软件供应链中云原生应用的镜像&#xff0c;包括镜像存储、镜像扫描和镜像签名等功能。 Harbor 已经提供了一些高级的安全功能&#xff0c;例如…