在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时自动加载方式ÿ…...
蓝牙 - 如何实现安全性
蓝牙技术在加密上做了很多工作,来保证你的数据安全。 这些年来,我们的许多电子设备都转向了使用无线技术进行连接。我们的鼠标、键盘、耳机和扬声器上不再有长长的纠缠的电线,而使用了简单方便的无线技术,科技进步改善了我们的生活…...
深入理解顺序io和随机io(全网最详细篇)
MySql系列整体栏目 内容链接地址【一】深入理解mysql索引本质https://blog.csdn.net/zhenghuishengq/article/details/121027025【二】深入理解mysql索引优化以及explain关键字https://blog.csdn.net/zhenghuishengq/article/details/124552080【三】深入理解mysql的索引分类&a…...
面试准备知识点与总结——(基础篇)
目录Java基础Java面向对象有哪些特征ArrayList和LinkedList有什么区别高并发的集合有哪些问题迭代器的fail-fast和fail-safeArrayList底层扩容机制HashMap面试合集解答设计模式单例设计模式哪些地方体现了单例模式Java基础 Java面向对象有哪些特征 Java面向对象有三大特征&am…...
Linux共享库,静态库与相关系统调用,工具的使用总结
tags: Linux C Syscall 写在前面 总结Unix/Linux操作系统的共享库/静态库部分, 以及一些系统调用. 参考Linux/UNIX系统编程手册41-42章. 测试程序均在Ubuntu下使用cc(gcc-9)运行成功. $ gcc -v Using built-in specs. COLLECT_GCCgcc COLLECT_LTO_WRAPPER/usr/lib/gcc/x86_64…...
「JVM 编译优化」javac 编译器源码解读
Java 的编译过程 前端编译: 编译器的前端,将 Java 文件转变成 Class 文件的过程;如 JDK 的 javac、Eclipse JDT 中的增量式编译器 ECJ;即使编译: JIT,Just In Time Compiler,在运行期将字节码转变成本地机器码的过程&…...
Leetcode DAY 34: K次取反后最大化的数组和 and 加油站 and 分发糖果
1005.K次取反后最大化的数组和 class Solution:def largestSumAfterKNegations(self, nums: List[int], k: int) -> int:nums sorted(nums, key abs, reverse True)for i in range(len(nums)):if nums[i] < 0:nums[i] -nums[i]k - 1else:continueif k 0:return sum(…...
2023美赛A题思路
在线解析 https://kdocs.cn/l/ccNGjN9sGugLkdocs.cn/l/ccNGjN9sGugL A题思路:(具体以题目解决问题顺序为主) 这道题分析植被就行,主要涉及不同植被间的相互作用,有竞争有相互促进,我查了下“植物科学数…...
前端上传文件
前言 以 vue 举例,原生 html css js 现在应该很少有人去写了 一、绘制样式 绘制两个标签,一个 <div></div> ,一个 <input type"file" />; 为 <div></div>添加 css 样式,…...
后台管理系统中选项卡的动态渲染
动态渲染选项卡其中router-link是为了当点击选项卡时跳转到选项卡所在的列表选项卡需要动态渲染,其中active是当选中后激活选中的样式为图标添加点击删除事件在状态机配置tabMenu(为了动态渲染)需要在tabMenu添加:active、title、…...
网络层重点协议之IP协议(IPv4)
网络层的作用就是来路由的选择,规划传输的路径,其中网络层的重点协议就是IP协议。4位版本号版本号的取值只有4和64位首部长度描述了IP报头有多长,报头中有一个选项部分,是变长的,是可有可无的部分,所以IP报…...
单招网站开发/企业网站建设公司
eclipse插件的安装方法大体有以下三种:...
济南学习做网站/5118和百度指数
项目中有个Activity中用到了ProgressDialog,而在跳转到下个Activity时就会报这个错: has leaked window com.android.internal.policy.impl.PhoneWindow$ that was originally added here 之前还没注意这个错误,看字面意思是泄露了窗口&#…...
网站后台做完文章不显示/四种营销策略
有scp命令,传输文件却显示报错无此命令今天下午在一台服务器上使用scp命令向另外一台服务器传文件的时候,报此错误 bash: scp: command not found ,lost connection,以为是该服务器没有安装此命令,w ...学习mongo系列(五) AND,$or,&…...
旅游网站建设目的/seo公司 彼亿营销
目录 一、前言 二、模拟实现 1、构造函数 2、拷贝构造函数 3、operator 4、operator[] 5、迭代器 6、string类的比较 7、string类的扩容 7.1、reserve 7.2、resize 8、string类的尾插 8.1、push_back 与 append 8.2、operator 9、string类的insert 9.1、插入字符…...
杭州网站建设公司有哪些/新闻最新热点
今天在配置完定时器以后,为了查看实际多久进一次中断,在keil中设置断点来查看了下,结果发现与配置的200ms中断一次差了5个ms 之后把自动重载值减去1,发现误差减小到了50us,整整两个数量级的误差! 之后又使…...
网站运营推广方法总结/杭州网站外包
您或您的团队目前是否手动测试并尝试采用自动化测试?在本文中,我们将概述小型QA团队如何从手工测试到无代码测试再到完全自动化的测试。这个过渡不会一蹴而就,但成功实现要比预期的容易得多。 1、对单调重复的手动测试说不 愿意对单调乏味的…...