在Java 中 利用Milo通信库,实现OPCUA客户端,并生成证书
程序结构:
配置文件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<>();@Autowiredprivate MyOPCUAProperties properties;@Autowiredprivate 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 wellString 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;@Autowiredprivate 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// intervalnull, // filter, null means use defaultUnsigned.uint(10), // queue sizetrue // 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读取的数据存入nodeEntitiesfor (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 {@Autowiredprivate ClientHandler clientHandler;@Autowiredpublic KeyStoreLoader keyStoreLoader;@Autowiredprivate MyOPCUAProperties properties;@Autowiredprivate MyOPCNode myOPCNode;/***connect* @return*/@RequestMapping("/connect")@ResponseBodypublic String connect() {try {return clientHandler.connect();} catch (Exception e) {e.printStackTrace();return "fail";}}@RequestMapping("/disconnect")@ResponseBodypublic String disconnect() {try {return clientHandler.disconnect();} catch (Exception e) {e.printStackTrace();return "fail";}}@RequestMapping("/subscribe")@ResponseBodypublic 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")@ResponseBodypublic 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")@ResponseBodypublic 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")@ResponseBodypublic 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 {@Autowiredprivate ClientHandler clientHandler;@Autowiredpublic KeyStoreLoader keyStoreLoader;@Autowiredprivate MyOPCNode myOPCNode;@Autowiredpublic S7ConnHelper s7ConnHelper;@Autowiredpublic ActualDataService actualDataService;Integer errorTimes = 0;Boolean last = false;Boolean trigIn = false;Boolean trigQ = false;//OPC@PostConstructpublic 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("===========-------============");}
相关文章:

在Java 中 利用Milo通信库,实现OPCUA客户端,并生成证书
程序结构: 配置文件resources: opcua.properties 西门子PLC端口号为4840,kepserver为49320 #opcua服务端配置参数 #opcua.server.endpoint.urlopc.tcp://192.168.2.102:49320 opcua.server.endpoint.urlopc.tcp://192.168.2.11:4840 opcu…...

三分钟学会用Vim
Vim知识点 目录Vim知识点一:什么是vim二:vim常用的三种模式三:vim的基本操作一:什么是vim vim最小集 vim是一款多模式的编辑器—各种模式—每种模式的用法有差别—每种模式之间可以互相切换 但是我们最常用的就是3~5个模式 vi…...

编译链接实战(8)认识elf文件格式
🎀 关于博主👇🏻👇🏻👇🏻 🥇 作者简介: 热衷于知识探索和分享的技术博主。 💂 csdn主页::【奇妙之二进制】 ✍️ 微信公众号:【Linux …...

新手小白如何入门黑客技术?
你是否对黑客技术感兴趣呢?感觉成为黑客是一件很酷的事。那么作为新手小白,我们该如何入门黑客技术,黑客技术又是学什么呢? 其实不管你想在哪个新的领域里有所收获,你需要考虑以下几个问题: 首先ÿ…...

【java】Spring Boot --深入SpringBoot注解原理及使用
步骤一 首先,先看SpringBoot的主配置类: SpringBootApplication public class StartEurekaApplication {public static void main(String[] args){SpringApplication.run(StartEurekaApplication.class, args);} }步骤二 点进SpringBootApplication来…...

一文掌握如何对项目进行诊断?【步骤方法和工具】
作为项目经理和PMO,面对错综复杂的项目,需要对组织的项目运作情况进行精确的分析和诊断,找出组织项目管理中和项目运行中存在的问题和潜在隐患,分析其原因,预防风险,并且形成科学合理的决策建议和解决方案&…...
系统分析师真题2020试卷相关概念二
结构化设计相关内容: 结构化设计是一种面向数据流的系统设计方法,它以数据流图和数据字典等文档为基础。数据流图从数据传递和加工的角度,以图形化方式来表达系统的逻辑功能、数据在系统内部的逻辑流向和逻辑变换过程,是结构化系统分析方法的主要表达工具及用于表示软件模…...

<<Java开发环境配置>>5-MySQL安装教程(绿色版)
一.MySQL绿色版安装: 1.直接解压下载的ZIP文件到对应的目录下(切记安装目录不要有中文); 如图:我的安装目录:D:Program Files 2.创建配置文件: 在MySQL安装目录下,创建一个my.ini配置文件,然后在里面添加以下内容(别忘了MySQL安装目录要改成…...

空间复杂度与时间复杂度
1、时间复杂度和空间复杂度 (1)时间复杂度、空间复杂度是什么? 算法效率分析分为两种:第一种是时间效率,第二种是空间效率。时间效率被称为时间复杂度,空间效率被称作空间复杂度时间复杂度主要衡量的是一…...

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

Twitter账号老被封?一文教会你怎么养号
昨天龙哥给大家科普完要怎么批量注册Twitter账号,立刻有朋友来私信龙哥说里面提到的这个养号和防关联具体是个怎么样的做法。由于Twitter检测机制还是比较敏感的,账号很容易被冻结,所以养号是非常重要的步骤。其实要养好Twitter账号其实并不难…...

当遇到国外客户的问题,你解决不了的时候怎么办
对我来说,今年的这个春节假期有点长,差不多休了一个月。复工之后,截止目前做到了60万RMB的业绩,但是相较于往年,整体状态还是差了些。往年的春节,我都是随时待命的状态,整个春节天天坐于电脑前&…...

算法刷题打卡第93天: 最大的以 1 为边界的正方形
最大的以 1 为边界的正方形 难度:中等 给你一个由若干 0 和 1 组成的二维网格 grid,请你找出边界全部由 1 组成的最大 正方形 子网格,并返回该子网格中的元素数量。如果不存在,则返回 0。 示例 1: 输入:…...

python语言基础(最详细版)
文章目录一、程序的格式框架缩进1、定义2、这里就简单的举几个例子注释二、语法元素的名称三、数据类型四、数值运算符五、关系运算六、逻辑运算七、运算符的结合性八、字符串一、程序的格式框架 缩进 1、定义 (1)python中通常用缩进来表示代码包含和…...
Java小技能:字符串
文章目录 引言I 预备知识1.1 Object类1.2 重写的规则1.3 hashCode方法II String2.1 String的特性2.2 字符串和正则2.3 StringBuilder,StringBuffer引言 String,StringBuffer,StringBuilder,char[],用来表示字符串。 I 预备知识 1.1 Object类 是所有类的根类 toString…...

2023美赛D题:可持续发展目标
以下内容全部来自人工翻译,仅供参考。 文章目录背景要求术语表文献服务背景 联合国制定了17个可持续发展目标(SDGs)。实现这些目标最终将改善世界上许多人的生活。这些目标并不相互独立,因此,一些目标的积极进展常常…...

openwrt开发板与ubuntu nfs挂载
1.ubuntu需要安装nfs服务 sudo apt-get install nfs-common nfs-kernel-server2.修改 /etc/exports文件: /home/test *(rw,nohide,insecure,no_subtree_check,async,no_root_squash) 前面是挂载的目录,后边是相应权限 rw:读写 insecure&am…...

【Redis】Redis持久化之AOF详解(Redis专栏启动)
📫作者简介:小明java问道之路,2022年度博客之星全国TOP3,专注于后端、中间件、计算机底层、架构设计演进与稳定性建工设优化。文章内容兼具广度深度、大厂技术方案,对待技术喜欢推理加验证,就职于知名金融公…...

Git小乌龟每次推送拉取都弹窗和用户名密码报错(解决办法)
目录 一、小乌龟推送代码到云端用户名和密码报错 (一) 遇到问题 (二)解决办法 二、小乌龟每次推送拉取都要输入账号和密码 (一)遇到问题 (二)解决办法 一、小乌龟推送代码到云…...
emacs 使用集锦
emacs 使用集锦 声明, 主要在c/c环境中使用! ---------------------------------------- 1. emacs 中 TAGS 位置设置 ---------------------------------------- a)临时使用方式: M-x visit-tags-table b)启动Emacs时自动加载方式ÿ…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...

CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...