UI-Project/src/lang/en.js
@@ -158,7 +158,6 @@ inglassgaps:'Введите стекло зазор', sure:'подтверд', cancel:'Отмена', glassID:'Стеклянный ID', operate:'Операция', breakage:'Повреждение', delete:'Удал', @@ -224,7 +223,6 @@ bindingshelves:'Назначенная стеллажка', clear:'Чисто.', workstation:'Станция номер', shelfnumber:'Номер стойки регистрации:', cardnumbera:'Номер карточки процесса:', incardnumber:'Пожалуйста, выберите номер программы', clearglass:'Очистить стекло с полки', UI-Project/src/lang/zh.js
@@ -216,12 +216,11 @@ specifytemperinga:'是否指定钢化该条信息?', temperedswitch:'钢化开关', dutyinformation:'值班信息', line:'线路', process:'工序', team:'班组', basic:'设备', makesure:'确认保存', cancel:'取消', // cancel:'取消', temperingtotal:'钢化炉数:', glasstotal:'玻璃总数:', }, @@ -452,11 +451,9 @@ allstatus: '全部入库状态', completedquantity: '完成数量', scrapquantity: '报废数量', number: '数量', method: '加工方式', innumber: '已入数量', productstatus: '生产状态', projectnumber: '工程号', right: '正常', stop: '终止', inquire: '查询', @@ -490,7 +487,6 @@ glassID :'玻璃ID', projectnumber :'工程号', layoutID :'钢化版图ID', productiontime :'生产时间', type :'类型', state :'状态', processcards :'流程卡', @@ -525,7 +521,6 @@ startslot:'开始工位', endslot:'目标工位', slotid:'格子ID', slot:'格子号', width:'原片宽', widtha:'原片宽:', inwidth:'请输入原片宽', @@ -545,7 +540,6 @@ quantitya:'数量:', enableid:'任务ID', originateslot:'起始格子', endslot:'目标格子', patternquantity:'原片数量', enabletype:'任务类型', enablestate:'工位状态', @@ -563,10 +557,8 @@ taskstatus :'任务状态', built :'新建', execution :'执行中', finish :'完成', tasktype :'任务类型', stocke :'入库', outbound :'出库', dispatch :'调度', inquire :'查询', station :'工位', hangzhoumesParent/common/opcuaClient/pom.xml
New file @@ -0,0 +1,40 @@ <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>common</artifactId> <groupId>com.mes</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>opcuaClient</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> </dependency> <dependency> <groupId>org.eclipse.milo</groupId> <artifactId>sdk-client</artifactId> <version>0.6.8</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.6.2</version> </dependency> </dependencies> </project> hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/configuration/MiloAutoConfiguration.java
New file @@ -0,0 +1,117 @@ package com.mes.milo.configuration; import com.mes.milo.pool.MiloConnectFactory; import com.mes.milo.pool.MiloConnectPool; import com.mes.milo.service.MiloService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig; import org.eclipse.milo.opcua.sdk.client.OpcUaClient; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; import javax.annotation.PreDestroy; /** * @author mes * @date 2020/4/25 * @desc milo-spring-boot-starter * @since 0.0.1 */ @Configuration @EnableConfigurationProperties(MiloProperties.class) @ConditionalOnClass({MiloService.class, MiloConnectPool.class}) @ConditionalOnProperty(prefix = MiloProperties.PREFIX, value = "enabled", havingValue = "true", matchIfMissing = true) @Slf4j public class MiloAutoConfiguration { private final MiloProperties properties; private MiloConnectPool connectPool; public MiloAutoConfiguration(MiloProperties properties) { this.properties = properties; } @Bean(name = "miloConnectPool") @ConditionalOnMissingBean({MiloConnectPool.class}) protected MiloConnectPool miloConnectPool() { MiloConnectFactory objectFactory = new MiloConnectFactory(this.properties); //设置对象池的相关参数 GenericKeyedObjectPoolConfig<OpcUaClient> poolConfig = new GenericKeyedObjectPoolConfig<>(); MiloProperties.Pool pool = properties.getPool(); // 最大空闲数 poolConfig.setMaxIdlePerKey(pool.getMaxIdle()); //最小空闲,设置为2表示池内至少存放2个空闲对象(当池内有2个空闲对象时调用borrowObject去对象时会立即调用创建对象的方法保证池内有2个空闲对象) poolConfig.setMinIdlePerKey(pool.getMinIdle()); //最大总数 10 poolConfig.setMaxTotal(pool.getMaxTotal()); // 多久执行一次对象扫描,将无用的对象销毁,默认-1不扫描 // poolConfig.setTimeBetweenEvictionRuns(Duration.ofMinutes(1)); // 在获取对象的时候检查有效性, 默认false poolConfig.setTestOnBorrow(true); // 在归还对象的时候检查有效性, 默认false poolConfig.setTestOnReturn(false); // 在空闲时检查有效性, 默认false poolConfig.setTestWhileIdle(false); // 最大等待时间, 默认的值为-1,表示无限等待。 // poolConfig.setMaxWait(Duration.ofSeconds(1)); // 是否启用后进先出, 默认true poolConfig.setLifo(true); // 连接耗尽时是否阻塞, false立即抛异常,true阻塞直到超时, 默认true poolConfig.setBlockWhenExhausted(true); // 每次逐出检查时 逐出的最大数目 默认3 poolConfig.setNumTestsPerEvictionRun(3); //一定要关闭jmx,不然springboot启动会报已经注册了某个jmx的错误 poolConfig.setJmxEnabled(false); //新建一个对象池,传入对象工厂和配置 connectPool = new MiloConnectPool(objectFactory, poolConfig); initPool(pool.getInitialSize(), pool.getMaxIdle()); return connectPool; } @Bean @ConditionalOnMissingBean(MiloService.class) @DependsOn("miloConnectPool") public MiloService miloService(MiloConnectPool miloConnectPool) { return new MiloService(miloConnectPool, properties); } /** * 预先加载testObject对象到对象池中 * * @param initialSize 初始化连接数 * @param maxIdle 最大空闲连接数 */ private void initPool(int initialSize, int maxIdle) { if (initialSize <= 0) { return; } properties.getConfig().forEach((key, config) -> { for (int i = 0; i < Math.min(initialSize, maxIdle); i++) { try { connectPool.addObject(config); } catch (Exception e) { throw new RuntimeException(e); } } }); } @PreDestroy public void destroy() { if (connectPool != null) { connectPool.close(); log.info("all opcUaClients are closed"); } } } hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/configuration/MiloProperties.java
New file @@ -0,0 +1,85 @@ package com.mes.milo.configuration; import lombok.Data; import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy; import org.springframework.boot.context.properties.ConfigurationProperties; import java.util.LinkedHashMap; import java.util.Map; /** * @author mes * @date 2020/4/25 * @desc milo-spring-boot-starter * @since 0.0.1 */ @Data @ConfigurationProperties(prefix = MiloProperties.PREFIX) public class MiloProperties { public static final String PREFIX = "mes.milo"; /** * 是否启用组件 */ private Boolean enabled = true; /** * server 默认请求配置,不指定,则默认取 config中第一个 */ private String primary; /** * server 列表 */ private Map<String, Config> config = new LinkedHashMap<>(); /** * 连接池配置 */ private Pool pool = new Pool(); @Data public static class Config { /** * OPC UA地址 */ private String endpoint; /** * 安全策略 */ private SecurityPolicy securityPolicy = SecurityPolicy.None; /** * 用户名 */ private String username; /** * 密码 */ private String password; } @Data public static class Pool { /** * 最大空闲 */ private int maxIdle = 5; /** * 最大总数 */ private int maxTotal = 20; /** * 最小空闲 */ private int minIdle = 2; /** * 初始化连接数 */ private int initialSize = 3; } } hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/exception/EndPointNotFoundException.java
New file @@ -0,0 +1,18 @@ package com.mes.milo.exception; /** * 类 EndPointNotFoundException 功能描述: * * @author mes * @version 0.0.1 * @date 2021/09/04 17:03 */ public class EndPointNotFoundException extends RuntimeException { public EndPointNotFoundException() { super(); } public EndPointNotFoundException(String message) { super(message); } } hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/exception/IdentityNotFoundException.java
New file @@ -0,0 +1,14 @@ package com.mes.milo.exception; /** * 类 IdentityNotFoundException 功能描述: * * @author mes * @version 0.0.1 * @date 2021/09/15 09:35 */ public class IdentityNotFoundException extends RuntimeException { public IdentityNotFoundException(String message) { super(message); } } hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/model/ReadWriteEntity.java
New file @@ -0,0 +1,26 @@ package com.mes.milo.model; import lombok.*; import lombok.experimental.Accessors; import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue; /** * @author mes * @version 0.0.1 * @desc * @since 2020/4/13 */ @Data @Builder @AllArgsConstructor @NoArgsConstructor @Accessors(chain = true) @ToString public class ReadWriteEntity { private String identifier; private Object value; private DataValue dataValue; } hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/model/WriteEntity.java
New file @@ -0,0 +1,23 @@ package com.mes.milo.model; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import org.eclipse.milo.opcua.stack.core.types.builtin.Variant; /** * @author mes * @version 0.0.1 * @since 2020/4/13 */ @Data @Builder @AllArgsConstructor @NoArgsConstructor @Accessors(chain = true) public class WriteEntity { private String identifier; private Variant variant; } hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/pool/MiloConnectFactory.java
New file @@ -0,0 +1,155 @@ package com.mes.milo.pool; import com.mes.milo.configuration.MiloProperties; import com.mes.milo.exception.EndPointNotFoundException; import com.mes.milo.exception.IdentityNotFoundException; import com.mes.milo.utils.CustomUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.pool2.KeyedPooledObjectFactory; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.impl.DefaultPooledObject; import org.eclipse.milo.opcua.sdk.client.OpcUaClient; import org.eclipse.milo.opcua.sdk.client.api.identity.AnonymousProvider; import org.eclipse.milo.opcua.sdk.client.api.identity.IdentityProvider; import org.eclipse.milo.opcua.sdk.client.api.identity.UsernameProvider; 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.UInteger; import org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Optional; /** * 类 MiloConnectFactory 功能描述:<br/> * * @author mes * @version 0.0.1 * @date 2023/5/4 18:56 */ @Slf4j public class MiloConnectFactory implements KeyedPooledObjectFactory<MiloProperties.Config, OpcUaClient> { public MiloConnectFactory(MiloProperties properties) { CustomUtil.verifyProperties(properties); } /** * 创建对象 * * @return * @throws Exception */ @Override public PooledObject<OpcUaClient> makeObject(MiloProperties.Config key) throws Exception { OpcUaClient client = null; try { client = createClient(key); client.connect().get(); return new DefaultPooledObject<>(client); } catch (Exception e) { if (client != null) { client.disconnect().get(); } throw new InterruptedException(e.getMessage()); } } /** * 对象要被销毁时(validateObject方法返回false或者超时)后被调用 * * @param pooledObject * @throws Exception */ @Override public void destroyObject(MiloProperties.Config key, PooledObject<OpcUaClient> pooledObject) throws Exception { OpcUaClient opcUaClient = pooledObject.getObject(); log.info("disconnect opcUaClient {}", opcUaClient.getConfig().getApplicationName().getText()); opcUaClient.disconnect().get(); } /** * 每次获取对象和还回对象时会被调用,如果返回false会销毁对象 */ @Override public boolean validateObject(MiloProperties.Config key, PooledObject<OpcUaClient> pooledObject) { return true; } /** * 调用获取对象方法前被调用 * 此方法一般进行一些前置操作 */ @Override public void activateObject(MiloProperties.Config key, PooledObject<OpcUaClient> pooledObject) throws Exception { } /** * 当还回对象并且validateObject方法返回true后被调用 * 一般在此方法中对刚刚使用完成的对象进行重置 */ @Override public void passivateObject(MiloProperties.Config key, PooledObject<OpcUaClient> pooledObject) throws Exception { } private OpcUaClient createClient(MiloProperties.Config key) throws Exception { Path securityTempDir = Paths.get(System.getProperty("java.io.tmpdir"), "security"); Files.createDirectories(securityTempDir); if (!Files.exists(securityTempDir)) { throw new Exception("unable to create security dir: " + securityTempDir); } return OpcUaClient.create(key.getEndpoint(), endpoints -> { final Optional<EndpointDescription> endpoint = endpoints .stream() // .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri())) .findFirst(); EndpointDescription newEndpoint = new EndpointDescription(key.getEndpoint(), endpoint.get().getServer(), endpoint.get().getServerCertificate(), endpoint.get().getSecurityMode(), endpoint.get().getSecurityPolicyUri(), endpoint.get().getUserIdentityTokens(), endpoint.get().getTransportProfileUri(), endpoint.get().getSecurityLevel()); return Optional.of(newEndpoint); }, configBuilder -> configBuilder .setApplicationName(LocalizedText.english("eclipse milo opc-ua client")) .setApplicationUri("urn:eclipse:milo:examples:client") //访问方式 .setIdentityProvider(new UsernameProvider(key.getUsername(), key.getPassword())) .setRequestTimeout(UInteger.valueOf(5000)) .build() ); } private URI getUri(MiloProperties.Config key) { try { return new URI(endpointUrl(key)); } catch (URISyntaxException e) { throw new EndPointNotFoundException("endpoint 配置异常"); } } private String endpointUrl(MiloProperties.Config key) { return key.getEndpoint(); } private SecurityPolicy securityPolicy(MiloProperties.Config key) { return key.getSecurityPolicy(); } private IdentityProvider identityProvider(MiloProperties.Config key) { if (securityPolicy(key).equals(SecurityPolicy.None)) { return new AnonymousProvider(); } if (key.getUsername() == null || key.getPassword() == null) { throw new IdentityNotFoundException("连接信息未完善"); } else { return new UsernameProvider(key.getUsername(), key.getPassword()); } } } hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/pool/MiloConnectPool.java
New file @@ -0,0 +1,25 @@ package com.mes.milo.pool; import com.mes.milo.configuration.MiloProperties; import org.apache.commons.pool2.KeyedPooledObjectFactory; import org.apache.commons.pool2.impl.GenericKeyedObjectPool; import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig; import org.eclipse.milo.opcua.sdk.client.OpcUaClient; /** * 类 MiloConnectPool 功能描述:<br/> * * @author mes * @version 0.0.1 * @date 2023/5/4 19:17 */ public class MiloConnectPool extends GenericKeyedObjectPool<MiloProperties.Config, OpcUaClient> { public MiloConnectPool(KeyedPooledObjectFactory<MiloProperties.Config, OpcUaClient> factory) { super(factory); } public MiloConnectPool(KeyedPooledObjectFactory<MiloProperties.Config, OpcUaClient> factory, GenericKeyedObjectPoolConfig<OpcUaClient> config) { super(factory, config); } } hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/BrowseNodeRunner.java
New file @@ -0,0 +1,59 @@ package com.mes.milo.runner; import com.mes.milo.utils.CustomUtil; import lombok.extern.slf4j.Slf4j; import org.eclipse.milo.opcua.sdk.client.OpcUaClient; import org.eclipse.milo.opcua.sdk.client.nodes.UaNode; import org.eclipse.milo.opcua.stack.core.UaException; import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; /** * @author mes * @version 0.0.1 * @since 2020/4/14 */ @Slf4j public class BrowseNodeRunner { /** * 要读的节点 */ private final String browseRoot; public BrowseNodeRunner(String browseRoot) { this.browseRoot = browseRoot; } public List<String> run(OpcUaClient opcUaClient) { NodeId nodeId = CustomUtil.parseNodeId(browseRoot); return browseNode(browseRoot, opcUaClient, nodeId); } private List<String> browseNode(String prefix, OpcUaClient client, NodeId browseRoot) { List<String> nodesList = new ArrayList<>(); try { List<? extends UaNode> nodes = client.getAddressSpace().browseNodes(browseRoot); nodes = nodes.stream().filter(item -> !Objects.requireNonNull(item.getBrowseName().getName()).startsWith("_")).collect(Collectors.toList()); for (UaNode node : nodes) { String sub = prefix + "." + node.getBrowseName().getName(); // recursively browse to children List<String> browseNode = browseNode(sub, client, node.getNodeId()); if (browseNode.isEmpty()) { nodesList.add(sub); } else { nodesList.addAll(browseNode(sub, client, node.getNodeId())); } } } catch (UaException e) { log.error("Browsing nodeId={} failed: {}", browseRoot, e.getMessage(), e); } return nodesList; } } hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/BrowseRunner.java
New file @@ -0,0 +1,36 @@ package com.mes.milo.runner; import lombok.extern.slf4j.Slf4j; import org.eclipse.milo.opcua.sdk.client.OpcUaClient; import org.eclipse.milo.opcua.sdk.client.nodes.UaNode; import org.eclipse.milo.opcua.stack.core.Identifiers; import org.eclipse.milo.opcua.stack.core.UaException; import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UShort; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; /** * @author mes * @version 0.0.1 * @since 2020/4/14 */ @Slf4j public class BrowseRunner { public List<String> run(OpcUaClient opcUaClient) { List<String> nodesList = new ArrayList<>(); try { List<? extends UaNode> nodes = opcUaClient.getAddressSpace().browseNodes(Identifiers.ObjectsFolder); nodesList.addAll(nodes.stream().filter(item -> !Objects.requireNonNull(item.getBrowseName().getName()).startsWith("_") && Objects.equals(item.getBrowseName().getNamespaceIndex(), UShort.valueOf(2))) .map(item -> item.getBrowseName().getName()).collect(Collectors.toList())); } catch (UaException e) { log.error("遍历根节点异常:{}", e.getMessage(), e); } return nodesList; } } hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/ReadValuesRunner.java
New file @@ -0,0 +1,60 @@ package com.mes.milo.runner; import com.mes.milo.model.ReadWriteEntity; import com.mes.milo.utils.CustomUtil; import lombok.extern.slf4j.Slf4j; import org.eclipse.milo.opcua.sdk.client.OpcUaClient; 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.enumerated.TimestampsToReturn; import java.util.ArrayList; import java.util.List; /** * @author mes * @version 0.0.1 * @desc * @since 2020/4/14 */ @Slf4j public class ReadValuesRunner { /** * 要读的点位list */ private final List<String> identifiers; public ReadValuesRunner(List<String> identifiers) { this.identifiers = identifiers; } public List<ReadWriteEntity> run(OpcUaClient opcUaClient) { List<ReadWriteEntity> entityList = new ArrayList<>(); try { List<NodeId> nodeIds = new ArrayList<>(); identifiers.forEach(identifier -> nodeIds.add(CustomUtil.parseNodeId(identifier))); // 读取指定点位的值,10s超时 List<DataValue> dataValues = opcUaClient.readValues(10000, TimestampsToReturn.Both, nodeIds).get(); if (dataValues.size() == identifiers.size()) { for (int i = 0; i < identifiers.size(); i++) { String id = identifiers.get(i); Object value = dataValues.get(i).getValue().getValue(); StatusCode status = dataValues.get(i).getStatusCode(); assert status != null; if (status.isGood()) { log.info("读取点位 '{}' 的值为 {}", id, value); } entityList.add(ReadWriteEntity.builder() .identifier(id) .value(value) .dataValue(dataValues.get(i)) .build()); } } } catch (Exception e) { log.error("读值时出现了异常:{}", e.getMessage(), e); } return entityList; } } hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/WriteValuesRunner.java
New file @@ -0,0 +1,50 @@ package com.mes.milo.runner; import com.mes.milo.model.WriteEntity; import com.mes.milo.utils.CustomUtil; import lombok.extern.slf4j.Slf4j; import org.eclipse.milo.opcua.sdk.client.OpcUaClient; 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 java.util.LinkedList; import java.util.List; /** * @author mes * @version 0.0.1 * @since 2020/4/14 */ @Slf4j public class WriteValuesRunner { private final List<WriteEntity> entities; public WriteValuesRunner(List<WriteEntity> entities) { this.entities = entities; } public void run(OpcUaClient opcUaClient) { try { if (!entities.isEmpty()) { List<NodeId> nodeIds = new LinkedList<>(); List<DataValue> dataValues = new LinkedList<>(); for (WriteEntity entity : entities) { nodeIds.add(CustomUtil.parseNodeId(entity.getIdentifier())); dataValues.add(new DataValue(entity.getVariant(), null, null)); } List<StatusCode> statusCodeList = opcUaClient.writeValues(nodeIds, dataValues).join(); for (int i = 0; i < statusCodeList.size(); i++) { if (statusCodeList.get(i).isGood()) { log.info("将值 '{}' 写入到点位:{} 成功", dataValues.get(i).getValue(), nodeIds.get(i)); } else { log.error("点位:{} 写入时出现了异常:{}", nodeIds.get(i), statusCodeList.get(i)); } } } } catch (Exception e) { log.error("批量写值出现异常出现了异常:{}", e.getMessage(), e); } } } hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/subscription/SubscriptionCallback.java
New file @@ -0,0 +1,16 @@ package com.mes.milo.runner.subscription; import org.eclipse.milo.opcua.sdk.client.subscriptions.ManagedDataItem; import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue; /** * 类 SubscriptionCallback 功能描述:<br/> * * @author mes * @version 0.0.1 * @date 2023/5/8 22:14 */ public interface SubscriptionCallback { void onSubscribe(ManagedDataItem dataItem, DataValue value) throws Exception; } hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/subscription/SubscriptionRunner.java
New file @@ -0,0 +1,111 @@ package com.mes.milo.runner.subscription; import com.mes.milo.utils.CustomUtil; import lombok.extern.slf4j.Slf4j; import org.eclipse.milo.opcua.sdk.client.OpcUaClient; import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaSubscription; import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaSubscriptionManager; import org.eclipse.milo.opcua.sdk.client.subscriptions.ManagedDataItem; import org.eclipse.milo.opcua.sdk.client.subscriptions.ManagedSubscription; 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.unsigned.UInteger; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; /** * 类 SubscriptionRunner 功能描述: * * @author mes * @version 0.0.1 * @date 2022/01/01 23:49 */ @Slf4j public class SubscriptionRunner { /** * 点位list */ private final List<String> identifiers; private final double samplingInterval; public SubscriptionRunner(List<String> identifiers) { this.identifiers = identifiers; this.samplingInterval = 1000.0D; } public SubscriptionRunner(List<String> identifiers, double samplingInterval) { this.identifiers = identifiers; this.samplingInterval = samplingInterval; } public void run(OpcUaClient opcUaClient, SubscriptionCallback callback) { final CountDownLatch downLatch = new CountDownLatch(1); //添加订阅监听器,用于处理断线重连后的订阅问题 opcUaClient.getSubscriptionManager().addSubscriptionListener(new CustomSubscriptionListener(opcUaClient, callback)); //处理订阅逻辑 handler(opcUaClient, callback); try { //持续监听 downLatch.await(); } catch (Exception e) { log.error("订阅时出现了异常:{}", e.getMessage(), e); } } private void handler(OpcUaClient opcUaClient, SubscriptionCallback callback) { try { //创建订阅 ManagedSubscription subscription = ManagedSubscription.create(opcUaClient, samplingInterval); subscription.setDefaultSamplingInterval(samplingInterval); subscription.setDefaultQueueSize(UInteger.valueOf(10)); List<NodeId> nodeIdList = new ArrayList<>(); for (String identifier : identifiers) { nodeIdList.add(CustomUtil.parseNodeId(identifier)); } List<ManagedDataItem> dataItemList = subscription.createDataItems(nodeIdList); for (ManagedDataItem dataItem : dataItemList) { dataItem.addDataValueListener((item) -> { try { callback.onSubscribe (dataItem, item); } catch (Exception e) { e.printStackTrace(); } }); } } catch (Exception e) { log.error("订阅时出现了异常:{}", e.getMessage(), e); } } private class CustomSubscriptionListener implements UaSubscriptionManager.SubscriptionListener { private final OpcUaClient client; private final SubscriptionCallback callback; public CustomSubscriptionListener(OpcUaClient client, SubscriptionCallback callback) { this.client = client; this.callback = callback; } /** * 重连时 尝试恢复之前的订阅失败时 会调用此方法 * * @param uaSubscription 订阅 * @param statusCode 状态 */ @Override public void onSubscriptionTransferFailed(UaSubscription uaSubscription, StatusCode statusCode) { log.debug("恢复订阅失败 需要重新订阅"); //在回调方法中重新订阅 handler(client, callback); } } } hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/service/MiloService.java
New file @@ -0,0 +1,553 @@ package com.mes.milo.service; import com.mes.milo.configuration.MiloProperties; import com.mes.milo.model.ReadWriteEntity; import com.mes.milo.model.WriteEntity; import com.mes.milo.pool.MiloConnectPool; import com.mes.milo.runner.BrowseNodeRunner; import com.mes.milo.runner.BrowseRunner; import com.mes.milo.runner.ReadValuesRunner; import com.mes.milo.runner.WriteValuesRunner; import com.mes.milo.runner.subscription.SubscriptionCallback; import com.mes.milo.runner.subscription.SubscriptionRunner; import com.mes.milo.utils.CustomUtil; import lombok.extern.slf4j.Slf4j; import org.eclipse.milo.opcua.sdk.client.OpcUaClient; import org.eclipse.milo.opcua.stack.core.types.builtin.Variant; import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * @author mes * @date 2020/4/25 * @desc milo-spring-boot-starter * @since 0.0.1 */ @Service @Slf4j public class MiloService { private final MiloConnectPool connectPool; private final MiloProperties properties; public MiloService(MiloConnectPool connectPool, MiloProperties properties) { this.connectPool = connectPool; this.properties = properties; } /** * 遍历OPC UA服务器根节点 * * @return 根节点列表 */ public List<String> browseRoot() throws Exception { return browseRoot(null); } /** * 遍历OPC UA服务器根节点 * * @param clientName 配置key * @return 根节点列表 */ public List<String> browseRoot(String clientName) throws Exception { MiloProperties.Config config = CustomUtil.getConfig(properties, clientName); BrowseRunner runner = new BrowseRunner(); OpcUaClient client = connectPool.borrowObject(config); if (client != null) { try { return runner.run(client); } finally { connectPool.returnObject(config, client); } } return Collections.emptyList(); } /** * 遍历OPC UA服务器指定节点 * * @param browseRoot 节点名称 * @return 指定节点 tag列表 */ public List<String> browseNode(String browseRoot) throws Exception { return browseNode(browseRoot, null); } /** * 遍历OPC UA服务器指定节点 * * @param browseRoot 节点名称 * @param clientName 配置key * @return 指定节点 tag列表 */ public List<String> browseNode(String browseRoot, String clientName) throws Exception { MiloProperties.Config config = CustomUtil.getConfig(properties, clientName); BrowseNodeRunner runner = new BrowseNodeRunner(browseRoot); OpcUaClient client = connectPool.borrowObject(config); if (client != null) { try { return runner.run(client); } finally { connectPool.returnObject(config, client); } } return Collections.emptyList(); } /** * 指定类型 写入kep点位值 * * @param entity 待写入数据 */ public void writeSpecifyType(WriteEntity entity) throws Exception { writeSpecifyType(Collections.singletonList(entity)); } /** * 指定类型 写入kep点位值 * * @param entity 待写入数据 * @param clientName 配置key */ public void writeSpecifyType(WriteEntity entity, String clientName) throws Exception { writeSpecifyType(Collections.singletonList(entity), clientName); } /** * 指定类型 写入kep点位值,可批量写入不同类型的值 * * @param entities 待写入数据 */ public void writeSpecifyType(List<WriteEntity> entities) throws Exception { writeSpecifyType(entities, null); } /** * 指定类型 写入kep点位值,可批量写入不同类型的值 * * @param entities 待写入数据 * @param clientName 配置key */ public void writeSpecifyType(List<WriteEntity> entities, String clientName) throws Exception { MiloProperties.Config config = CustomUtil.getConfig(properties, clientName); WriteValuesRunner runner = new WriteValuesRunner(entities); OpcUaClient client = connectPool.borrowObject(config); if (client != null) { try { runner.run(client); } finally { connectPool.returnObject(config, client); } } } /** * 写入kep点位值 * * @param entity 待写入数据 */ public void writeToOpcUa(ReadWriteEntity entity) throws Exception { writeToOpcUa(Collections.singletonList(entity)); } /** * 写入kep点位值 * * @param entity 待写入数据 * @param clientName 配置key */ public void writeToOpcUa(ReadWriteEntity entity, String clientName) throws Exception { writeToOpcUa(Collections.singletonList(entity), clientName); } /** * 写入kep点位值 * * @param entities 待写入数据 */ public void writeToOpcUa(List<ReadWriteEntity> entities) throws Exception { writeToOpcUa(entities, null); } /** * 写入kep点位值 * * @param entities 待写入数据 * @param clientName 配置key */ public void writeToOpcUa(List<ReadWriteEntity> entities, String clientName) throws Exception { MiloProperties.Config config = CustomUtil.getConfig(properties, clientName); List<WriteEntity> writeEntityList = new ArrayList<>(); if (!entities.isEmpty()) { for (ReadWriteEntity entity : entities) { writeEntityList.add(WriteEntity.builder() .identifier(entity.getIdentifier()) .variant(new Variant(entity.getValue())) .build()); } } WriteValuesRunner runner = new WriteValuesRunner(writeEntityList); OpcUaClient client = connectPool.borrowObject(config); if (client != null) { try { runner.run(client); } finally { connectPool.returnObject(config, client); } } } /** * kepware 数据类型为:Char<br/> * 8位带符号整数 * * @param entity 待写入数据 */ public void writeToOpcChar(ReadWriteEntity entity) throws Exception { writeToOpcChar(Collections.singletonList(entity)); } /** * kepware 数据类型为:Char<br/> * 8位带符号整数 * * @param entity 待写入数据 * @param clientName 配置key */ public void writeToOpcChar(ReadWriteEntity entity, String clientName) throws Exception { writeToOpcChar(Collections.singletonList(entity), clientName); } /** * kepware 数据类型为:Char<br/> * 8位带符号整数 * * @param entities 待写入数据 */ public void writeToOpcChar(List<ReadWriteEntity> entities) throws Exception { writeToOpcChar(entities, null); } /** * kepware 数据类型为:Char<br/> * 8位带符号整数 * * @param entities 待写入数据 * @param clientName 配置key */ public void writeToOpcChar(List<ReadWriteEntity> entities, String clientName) throws Exception { MiloProperties.Config config = CustomUtil.getConfig(properties, clientName); List<WriteEntity> writeEntityList = new ArrayList<>(); if (!entities.isEmpty()) { for (ReadWriteEntity entity : entities) { writeEntityList.add(WriteEntity.builder() .identifier(entity.getIdentifier()) .variant(new Variant(((Integer) entity.getValue()).byteValue())) .build()); } } WriteValuesRunner runner = new WriteValuesRunner(writeEntityList); OpcUaClient client = connectPool.borrowObject(config); if (client != null) { try { runner.run(client); } finally { connectPool.returnObject(config, client); } } } /** * kepware 数据类型为:Byte<br/> * 8位无符号整数 * * @param entity 待写入数据 */ public void writeToOpcByte(ReadWriteEntity entity) throws Exception { writeToOpcByte(Collections.singletonList(entity)); } /** * kepware 数据类型为:Byte<br/> * 8位无符号整数 * * @param entity 待写入数据 * @param clientName 配置key */ public void writeToOpcByte(ReadWriteEntity entity, String clientName) throws Exception { writeToOpcByte(Collections.singletonList(entity), clientName); } /** * kepware 数据类型为:Byte<br/> * 8位无符号整数 * * @param entities 待写入数据 */ public void writeToOpcByte(List<ReadWriteEntity> entities) throws Exception { writeToOpcByte(entities, null); } /** * kepware 数据类型为:Byte<br/> * 8位无符号整数 * * @param entities 待写入数据 * @param clientName 配置key */ public void writeToOpcByte(List<ReadWriteEntity> entities, String clientName) throws Exception { MiloProperties.Config config = CustomUtil.getConfig(properties, clientName); List<WriteEntity> writeEntityList = new ArrayList<>(); if (!entities.isEmpty()) { for (ReadWriteEntity entity : entities) { writeEntityList.add(WriteEntity.builder() .identifier(entity.getIdentifier()) .variant(new Variant(Unsigned.ubyte((Integer) entity.getValue()))) .build()); } } WriteValuesRunner runner = new WriteValuesRunner(writeEntityList); OpcUaClient client = connectPool.borrowObject(config); if (client != null) { try { runner.run(client); } finally { connectPool.returnObject(config, client); } } } /** * kepware 数据类型为:Short<br/> * 16位带符号整数 * * @param entity 待写入数据 */ public void writeToOpcShort(ReadWriteEntity entity) throws Exception { writeToOpcShort(Collections.singletonList(entity)); } /** * kepware 数据类型为:Short<br/> * 16位带符号整数 * * @param entity 待写入数据 * @param clientName 配置key */ public void writeToOpcShort(ReadWriteEntity entity, String clientName) throws Exception { writeToOpcShort(Collections.singletonList(entity), clientName); } /** * kepware 数据类型为:Short<br/> * 16位带符号整数 * * @param entities 待写入数据 */ public void writeToOpcShort(List<ReadWriteEntity> entities) throws Exception { writeToOpcShort(entities, null); } /** * kepware 数据类型为:Short<br/> * 16位带符号整数 * * @param entities 待写入数据 * @param clientName 配置key */ public void writeToOpcShort(List<ReadWriteEntity> entities, String clientName) throws Exception { MiloProperties.Config config = CustomUtil.getConfig(properties, clientName); List<WriteEntity> writeEntityList = new ArrayList<>(); if (!entities.isEmpty()) { for (ReadWriteEntity entity : entities) { writeEntityList.add(WriteEntity.builder() .identifier(entity.getIdentifier()) .variant(new Variant(((Integer) entity.getValue()).shortValue())) .build()); } } WriteValuesRunner runner = new WriteValuesRunner(writeEntityList); OpcUaClient client = connectPool.borrowObject(config); if (client != null) { try { runner.run(client); } finally { connectPool.returnObject(config, client); } } } /** * kepware 数据类型为:Word<br/> * 16位无符号整数 * * @param entity 待写入数据 */ public void writeToOpcWord(ReadWriteEntity entity) throws Exception { writeToOpcWord(Collections.singletonList(entity)); } /** * kepware 数据类型为:Word<br/> * 16位无符号整数 * * @param entity 待写入数据 * @param clientName 配置key */ public void writeToOpcWord(ReadWriteEntity entity, String clientName) throws Exception { writeToOpcWord(Collections.singletonList(entity), clientName); } /** * kepware 数据类型为:Word<br/> * 16位无符号整数 * * @param entities 待写入数据 */ public void writeToOpcWord(List<ReadWriteEntity> entities) throws Exception { writeToOpcWord(entities, null); } /** * kepware 数据类型为:Word<br/> * 16位无符号整数 * * @param entities 待写入数据 * @param clientName 配置key */ public void writeToOpcWord(List<ReadWriteEntity> entities, String clientName) throws Exception { MiloProperties.Config config = CustomUtil.getConfig(properties, clientName); List<WriteEntity> writeEntityList = new ArrayList<>(); if (!entities.isEmpty()) { for (ReadWriteEntity entity : entities) { writeEntityList.add(WriteEntity.builder() .identifier(entity.getIdentifier()) .variant(new Variant(Unsigned.ushort((Integer) entity.getValue()))) .build()); } } WriteValuesRunner runner = new WriteValuesRunner(writeEntityList); OpcUaClient client = connectPool.borrowObject(config); if (client != null) { try { runner.run(client); } finally { connectPool.returnObject(config, client); } } } /** * 读取kep点位值 * * @param id 点位id * @return */ public ReadWriteEntity readFromOpcUa(String id) throws Exception { return readFromOpcUa(id, null); } /** * 读取kep点位值 * * @param id 点位id * @param clientName 配置key * @return */ public ReadWriteEntity readFromOpcUa(String id, String clientName) throws Exception { List<ReadWriteEntity> entityList = readFromOpcUa(Collections.singletonList(id), clientName); if (!entityList.isEmpty()) { return entityList.get(0); } return null; } /** * 读取kep点位值 * * @param ids 点位id数组 * @return */ public List<ReadWriteEntity> readFromOpcUa(List<String> ids) throws Exception { return readFromOpcUa(ids, null); } /** * 读取kep点位值 * * @param ids 点位id数组 * @param clientName 配置key * @return */ public List<ReadWriteEntity> readFromOpcUa(List<String> ids, String clientName) throws Exception { MiloProperties.Config config = CustomUtil.getConfig(properties, clientName); ReadValuesRunner runner = new ReadValuesRunner(ids); OpcUaClient client = connectPool.borrowObject(config); if (client != null) { try { return runner.run(client); } finally { connectPool.returnObject(config, client); } } return new ArrayList<>(); } /** * 订阅kep点位值 * * @param ids 点位id数组 * @return */ public void subscriptionFromOpcUa(List<String> ids, SubscriptionCallback callback) throws Exception { subscriptionFromOpcUa(ids, 1000.0, callback); } /** * 订阅kep点位值 * * @param ids 点位id数组 * @param clientName 配置key * @return */ public void subscriptionFromOpcUa(List<String> ids, String clientName, SubscriptionCallback callback) throws Exception { subscriptionFromOpcUa(ids, 1000.0, clientName, callback); } /** * 订阅kep点位值 * * @param ids 点位id数组 * @param samplingInterval 订阅时间间隔 默认1000 ms * @return */ public void subscriptionFromOpcUa(List<String> ids, double samplingInterval, SubscriptionCallback callback) throws Exception { subscriptionFromOpcUa(ids, samplingInterval, null, callback); } /** * 订阅kep点位值 * * @param ids 点位id数组 * @param samplingInterval 订阅时间间隔 默认1000 ms * @param clientName 配置key * @return */ public void subscriptionFromOpcUa(List<String> ids, double samplingInterval, String clientName, SubscriptionCallback callback) throws Exception { MiloProperties.Config config = CustomUtil.getConfig(properties, clientName); SubscriptionRunner runner = new SubscriptionRunner(ids, samplingInterval); OpcUaClient client = connectPool.borrowObject(config); if (client != null) { try { runner.run(client, callback); } finally { connectPool.returnObject(config, client); } } } } hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/utils/CustomUtil.java
New file @@ -0,0 +1,114 @@ package com.mes.milo.utils; import com.google.common.collect.Sets; import com.mes.milo.configuration.MiloProperties; import com.mes.milo.exception.EndPointNotFoundException; import com.mes.milo.exception.IdentityNotFoundException; import lombok.extern.slf4j.Slf4j; import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId; import org.springframework.util.StringUtils; import java.net.*; import java.util.*; /** * @author mes * @version 0.0.1 * @desc * @since 2020/4/13 */ @Slf4j public class CustomUtil { private static final String OPC_UA_NOT_CONFIG = "请配置OPC UA地址信息"; private CustomUtil() { } public static String getHostname() { try { return InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException var1) { return "localhost"; } } public static Set<String> getHostnames(String address) { return getHostnames(address, true); } public static Set<String> getHostnames(String address, boolean includeLoopback) { HashSet<String> hostnames = Sets.newHashSet(); try { InetAddress inetAddress = InetAddress.getByName(address); if (inetAddress.isAnyLocalAddress()) { try { Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces(); for (NetworkInterface ni : Collections.list(nis)) { Collections.list(ni.getInetAddresses()).forEach((ia) -> { if (ia instanceof Inet4Address) { boolean loopback = ia.isLoopbackAddress(); if (!loopback || includeLoopback) { hostnames.add(ia.getHostName()); hostnames.add(ia.getHostAddress()); hostnames.add(ia.getCanonicalHostName()); } } }); } } catch (SocketException var7) { log.warn("Failed to NetworkInterfaces for bind address: {}", address, var7); } } else { boolean loopback = inetAddress.isLoopbackAddress(); if (!loopback || includeLoopback) { hostnames.add(inetAddress.getHostName()); hostnames.add(inetAddress.getHostAddress()); hostnames.add(inetAddress.getCanonicalHostName()); } } } catch (UnknownHostException var8) { log.warn("Failed to get InetAddress for bind address: {}", address, var8); } return hostnames; } public static NodeId parseNodeId(String identifier) { NodeId nodeId = new NodeId(2, identifier); if (identifier.startsWith("ns=") && identifier.contains(";")) { nodeId = NodeId.parseOrNull(identifier); } if (nodeId == null) { throw new IdentityNotFoundException("NodeId 解析失败,请检查"); } return nodeId; } public static void verifyProperties(MiloProperties properties) { if (properties.getConfig().isEmpty()) { throw new EndPointNotFoundException(OPC_UA_NOT_CONFIG); } if (!StringUtils.hasText(properties.getPrimary())) { Set<String> keySet = properties.getConfig().keySet(); properties.setPrimary(keySet.stream().findFirst().orElseThrow(() -> new EndPointNotFoundException(OPC_UA_NOT_CONFIG))); } properties.getConfig().forEach((key, config) -> { if (!StringUtils.hasText(config.getEndpoint())) { throw new EndPointNotFoundException(OPC_UA_NOT_CONFIG + ": " + key); } }); } public static MiloProperties.Config getConfig(MiloProperties properties) { return getConfig(properties, null); } public static MiloProperties.Config getConfig(MiloProperties properties, String clientName) { Map<String, MiloProperties.Config> config = properties.getConfig(); return StringUtils.hasText(clientName) ? config.get(clientName) : config.get(properties.getPrimary()); } } hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/utils/KeyStoreLoader.java
New file @@ -0,0 +1,123 @@ package com.mes.milo.utils; import lombok.extern.slf4j.Slf4j; import org.eclipse.milo.opcua.stack.client.security.DefaultClientCertificateValidator; import org.eclipse.milo.opcua.stack.core.security.DefaultTrustListManager; import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateBuilder; import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateGenerator; import java.io.File; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.*; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.regex.Pattern; @Slf4j public class KeyStoreLoader { private static final Pattern IP_ADDR_PATTERN = Pattern.compile( "^(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])$"); private static final String CLIENT_ALIAS = "client-ai"; private static final char[] PASSWORD = "password".toCharArray(); private X509Certificate clientCertificate; private X509Certificate[] clientCertificateChain; private KeyPair clientKeyPair; private DefaultClientCertificateValidator certificateValidator; public KeyStoreLoader load() throws Exception { Path securityTempDir = Paths.get(System.getProperty("java.io.tmpdir"), "security"); Files.createDirectories(securityTempDir); if (!Files.exists(securityTempDir)) { throw new Exception("unable to create security dir: " + securityTempDir); } File pkiDir = securityTempDir.resolve("pki").toFile(); log.info("security temp dir: {}", securityTempDir.toAbsolutePath()); KeyStore keyStore = KeyStore.getInstance("PKCS12"); Path serverKeyStore = securityTempDir.resolve("milo-client.pfx"); DefaultTrustListManager trustListManager = new DefaultTrustListManager(pkiDir); certificateValidator = new DefaultClientCertificateValidator(trustListManager); log.info("Loading KeyStore at {}", serverKeyStore); if (!Files.exists(serverKeyStore)) { keyStore.load(null, PASSWORD); KeyPair keyPair = SelfSignedCertificateGenerator.generateRsaKeyPair(2048); SelfSignedCertificateBuilder builder = new SelfSignedCertificateBuilder(keyPair) .setCommonName("Milo Client") .setOrganization("kangaroohy") .setOrganizationalUnit("dev") .setLocalityName("Folsom") .setStateName("CA") .setCountryCode("US") .setApplicationUri("urn:kangaroohy:milo:client") .addDnsName("localhost") .addIpAddress("127.0.0.1"); // Get as many hostnames and IP addresses as we can listed in the certificate. for (String hostname : CustomUtil.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 clientPrivateKey = keyStore.getKey(CLIENT_ALIAS, PASSWORD); if (clientPrivateKey instanceof PrivateKey) { clientCertificate = (X509Certificate) keyStore.getCertificate(CLIENT_ALIAS); clientCertificateChain = Arrays.stream(keyStore.getCertificateChain(CLIENT_ALIAS)) .map(X509Certificate.class::cast) .toArray(X509Certificate[]::new); PublicKey clientPublicKey = clientCertificate.getPublicKey(); clientKeyPair = new KeyPair(clientPublicKey, (PrivateKey) clientPrivateKey); } return this; } public X509Certificate getClientCertificate() { return clientCertificate; } public X509Certificate[] getClientCertificateChain() { return clientCertificateChain; } public DefaultClientCertificateValidator getCertificateValidator() { return certificateValidator; } public KeyPair getClientKeyPair() { return clientKeyPair; } } hangzhoumesParent/common/opcuaClient/src/main/resources/META-INF/spring.factories
New file @@ -0,0 +1,3 @@ # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.mes.milo.configuration.MiloAutoConfiguration hangzhoumesParent/common/opcuaClient/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
New file @@ -0,0 +1 @@ com.mes.milo.configuration.MiloAutoConfiguration hangzhoumesParent/common/opcuaClient/src/main/resources/application.yml
New file @@ -0,0 +1,9 @@ mes: milo: primary: default config: default: endpoint: opc.tcp://zidonghua:49320 security-policy: basic256sha256 username: zsh password: 1qaz2wsx3edc4rfv hangzhoumesParent/common/pom.xml
@@ -15,6 +15,7 @@ <modules> <module>servicebase</module> <module>springsecurity</module> <module>opcuaClient</module> </modules> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> hangzhoumesParent/moduleService/CacheGlassModule/src/main/java/com/mes/job/OpcCacheGlassTask.java
New file @@ -0,0 +1,31 @@ package com.mes.job; import com.mes.milo.runner.subscription.SubscriptionCallback; import com.mes.milo.service.MiloService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.util.Arrays; /** * @Author : zhoush * @Date: 2024/10/10 8:05 * @Description: */ @Component @Slf4j public class OpcCacheGlassTask { @Autowired MiloService miloService; @Autowired SubscriptionCallback cacheGlassSubscriptionCallback; @Scheduled(fixedDelay = Long.MAX_VALUE) public void startOpcTask() throws Exception { miloService.subscriptionFromOpcUa(Arrays.asList("my.device.x1", "my.device.x2"), cacheGlassSubscriptionCallback); } } hangzhoumesParent/moduleService/CacheGlassModule/src/main/java/com/mes/job/entity/CacheGlassInfo.java
New file @@ -0,0 +1,46 @@ package com.mes.job.entity; import lombok.Data; /** * @Author : zhoush * @Date: 2024/9/20 10:56 * @Description: */ @Data public class CacheGlassInfo { /** * 请求字 */ private String requestWord; /** * 请求字 */ private String confirmationWrod; /** * 玻璃id */ private String glassId; /** * 机1状态 */ private String oneMachineState; /** * 机2状态 */ private String twoMachineState; /** * 一号卧式当前格子 */ private int oneCurrentSlot; /** * 二号卧式当前格子 */ private int twoCurrentSlot; } hangzhoumesParent/moduleService/CacheGlassModule/src/main/java/com/mes/opccallback/CacheGlassSubscriptionCallback.java
New file @@ -0,0 +1,35 @@ package com.mes.opccallback; import com.mes.milo.model.ReadWriteEntity; import com.mes.milo.runner.subscription.SubscriptionCallback; import com.mes.milo.service.MiloService; import lombok.extern.slf4j.Slf4j; import org.eclipse.milo.opcua.sdk.client.subscriptions.ManagedDataItem; import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @Author : zhoush * @Date: 2024/10/10 14:13 * @Description: */ @Service @Slf4j public class CacheGlassSubscriptionCallback implements SubscriptionCallback { @Autowired MiloService miloService; @Override public void onSubscribe(ManagedDataItem dataItem, DataValue value) { try { log.info("{}___________{}" + dataItem, value); ReadWriteEntity readWriteEntity = miloService.readFromOpcUa("my.device.x1"); log.info("{}___________{}" + readWriteEntity); } catch (Exception e) { } } } hangzhoumesParent/moduleService/CacheGlassModule/src/main/resources/application.yml
@@ -27,4 +27,12 @@ secondLength: 400 sequence: order: false milo: primary: default config: default: endpoint: opc.tcp://127.0.0.1:49320 security-policy: basic256sha256 username: zsh password: 1qaz2wsx3edc4rfv hangzhoumesParent/moduleService/pom.xml
@@ -55,6 +55,11 @@ <groupId>com.mes</groupId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <artifactId>opcuaClient</artifactId> <groupId>com.mes</groupId> <version>1.0-SNAPSHOT</version> </dependency> <!--开发者工具--> <!-- <dependency>--> hangzhoumesParent/moduleService/yiwumes/src/main/resources/application.yml
@@ -15,4 +15,14 @@ configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl mes: milo: primary: default config: default: endpoint: opc.tcp://127.0.0.1:49320 security-policy: basic256sha256 username: zsh password: 1qaz2wsx3edc4rfv