在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时自动加载方式ÿ…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...
Qemu arm操作系统开发环境
使用qemu虚拟arm硬件比较合适。 步骤如下: 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载,下载地址:https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...
上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式
简介 在我的 QT/C 开发工作中,合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式:工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...
Mac flutter环境搭建
一、下载flutter sdk 制作 Android 应用 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 1、查看mac电脑处理器选择sdk 2、解压 unzip ~/Downloads/flutter_macos_arm64_3.32.2-stable.zip \ -d ~/development/ 3、添加环境变量 命令行打开配置环境变量文件 ope…...
Docker环境下安装 Elasticsearch + IK 分词器 + Pinyin插件 + Kibana(适配7.10.1)
做RAG自己打算使用esmilvus自己开发一个,安装时好像网上没有比较新的安装方法,然后找了个旧的方法对应试试: 🚀 本文将手把手教你在 Docker 环境中部署 Elasticsearch 7.10.1 IK分词器 拼音插件 Kibana,适配中文搜索…...
分布式计算框架学习笔记
一、🌐 为什么需要分布式计算框架? 资源受限:单台机器 CPU/GPU 内存有限。 任务复杂:模型训练、数据处理、仿真并发等任务耗时严重。 并行优化:通过任务拆分和并行执行提升效率。 可扩展部署:适配从本地…...
