yantian yue
2023-10-17 487b2f2a353b89ab46cd9b784226b600b7b915b8
新增OPCUA模块
已添加23个文件
已修改1个文件
1975 ■■■■■ 文件已修改
guns-vip-main/pom.xml 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/cert/KeyStoreLoader.java 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/cert/MethodName.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/client/ClientHandler.java 223 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/client/ClientRunner.java 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/config/Properties.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/controller/CommonController.java 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/controller/OpcuaConfController.java 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/entity/NodeEntity.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/entity/OpcuaConf.java 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/mapper/OpcuaConfMapper.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/mapper/mapping/OpcuaConfMapper.xml 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/model/params/OpcuaConfParam.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/model/result/OpcuaConfResult.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/service/OpcuaConfService.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/service/impl/OpcuaConfServiceImpl.java 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/resources/opcua.properties 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/resources/opcua.yml 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/webapp/assets/modular/bs/opcuaConf/opcuaConf.js 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/webapp/assets/modular/bs/opcuaConf/opcuaConf_add.js 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/webapp/assets/modular/bs/opcuaConf/opcuaConf_edit.js 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/webapp/pages/modular/bs/opcuaConf/opcuaConf.html 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/webapp/pages/modular/bs/opcuaConf/opcuaConf_add.html 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/src/main/webapp/pages/modular/bs/opcuaConf/opcuaConf_edit.html 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
guns-vip-main/pom.xml
@@ -73,6 +73,12 @@
            <groupId>cn.stylefeng</groupId>
            <artifactId>guns-sys</artifactId>
            <version>1.0.0</version>
            <exclusions>
                <exclusion>
                    <artifactId>guava</artifactId>
                    <groupId>com.google.guava</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- å·¥ä½œæµ -->
@@ -134,33 +140,33 @@
        <dependency>
            <groupId>org.eclipse.milo</groupId>
            <artifactId>sdk-client</artifactId>
            <version>0.2.4</version>
            <version>0.3.6</version>
        </dependency>
        <!--Server SDK依赖-->
        <dependency>
            <groupId>org.eclipse.milo</groupId>
            <artifactId>sdk-server</artifactId>
            <version>0.2.4</version>
            <version>0.3.6</version>
        </dependency>
        <!-- Stack依赖-->
        <dependency>
            <groupId>org.eclipse.milo</groupId>
            <artifactId>stack-client</artifactId>
            <version>0.2.4</version>
            <version>0.3.6</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.milo</groupId>
            <artifactId>stack-server</artifactId>
            <version>0.2.4</version>
            <version>0.3.6</version>
        </dependency>
        <!--Milo客户端的依赖-->
        <dependency>
            <groupId>org.eclipse.milo</groupId>
            <artifactId>sdk-client</artifactId>
            <version>0.2.4</version>
            <artifactId>stack-client</artifactId>
            <version>0.3.6</version>
        </dependency>
        <dependency>
@@ -183,6 +189,12 @@
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>3.1.3</version>
            <exclusions>
                <exclusion>
                    <artifactId>guava</artifactId>
                    <groupId>com.google.guava</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/cert/KeyStoreLoader.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,131 @@
package cn.stylefeng.guns.opcua.cert;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.regex.Pattern;
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;
/**
 * @ClassName: KeyStoreLoader
 * @Description: KeyStoreLoader
 * @author yyt
 * @date 2023å¹´10月13日
 */
@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 = "yyt@8888888888".toCharArray();
    private final Logger logger = LoggerFactory.getLogger(getClass());
    // è¯ä¹¦å¯¹è±¡
    private X509Certificate clientCertificate;
    // å¯†é’¥å¯¹å¯¹è±¡
    private KeyPair clientKeyPair;
    /**
     * @MethodName: load
     * @Description: load
     * @param baseDir
     * @return
     * @throws Exception
     * @CreateTime 2023å¹´10月13日
     */
    public KeyStoreLoader load(Path baseDir) throws Exception {
        // åˆ›å»ºä¸€ä¸ªä½¿ç”¨`PKCS12`加密标准的KeyStore。KeyStore在后面将作为读取和生成证书的对象。
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        // PKCS12的加密标准的文件后缀是.pfx,其中包含了公钥和私钥。
        // è€Œå…¶ä»–如.der等的格式只包含公钥,私钥在另外的文件中。
        Path serverKeyStore = baseDir.resolve("OPCUA-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("UaClient@Jellyleo")
                    .setOrganization("JL")
                    .setOrganizationalUnit("per")
                    .setLocalityName("jl")
                    .setStateName("JiangSu")
                    .setCountryCode("CN")
                    .setApplicationUri("urn:Yyt_PC:UnifiedAutomation:UaExpert")
                    .addDnsName("Yyt_PC")
                    .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;
    }
}
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/cert/MethodName.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package cn.stylefeng.guns.opcua.cert;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class MethodName {
    public void a(String name){
        log.info("str={}", name);
    }
    public void b(String name,String value){
        log.info("item={}, value={},{}", name, value,"BBBBBB");
    }
    public void c(String name,String value){
        log.info("item={}, value={},{}", name, value,"CCCCCC");
        }
}
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/client/ClientHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,223 @@
package cn.stylefeng.guns.opcua.client;
import com.google.common.collect.ImmutableList;
import cn.stylefeng.guns.opcua.entity.NodeEntity;
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.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.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
 * @ClassName: ClientHandler
 * @Description: å®¢æˆ·ç«¯å¤„理
 * @author Jellyleo
 * @date 2019å¹´12月12日
 */
@Slf4j
@Service
public class ClientHandler {
    // å®¢æˆ·ç«¯å®žä¾‹
    private OpcUaClient client = null;
    @Autowired
    private ClientRunner clientRunner;
    /**
     *
     * @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 {
        if (client == null) {
            return "找不到客户端,操作失败";
        }
//        List<Node> ns = client.getAddressSpace().browse(new NodeId(2, "模拟通道一.模拟设备一")).get();
        // æŸ¥è¯¢è®¢é˜…对象,没有则创建
        UaSubscription subscription = null;
        ImmutableList<UaSubscription> subscriptionList = client.getSubscriptionManager().getSubscriptions();
        if (CollectionUtils.isEmpty(subscriptionList)) {
            subscription = client.getSubscriptionManager().createSubscription(1000.0).get();
        } else {
            subscription = subscriptionList.get(0);
        }
        // ç›‘控项请求列表
        List<MonitoredItemCreateRequest> requests = new ArrayList<>();
        if (!CollectionUtils.isEmpty(nodes)) {
            for (NodeEntity node : nodes) {
                // åˆ›å»ºç›‘控的参数
                MonitoringParameters parameters = new MonitoringParameters(subscription.nextClientHandle(), 1000.0, // sampling
                        // interval
                        null, // filter, null means use default
                        Unsigned.uint(10), // queue size
                        true // discard oldest
                );
                // åˆ›å»ºè®¢é˜…的变量, åˆ›å»ºç›‘控项请 æ±‚
                MonitoredItemCreateRequest request = new MonitoredItemCreateRequest(
                        new ReadValueId(new NodeId(node.getIndex(), node.getIdentifier()), AttributeId.Value.uid(),
                                null, null),
                        MonitoringMode.Reporting, parameters);
                requests.add(request);
            }
        }
        // åˆ›å»ºç›‘控项,并且注册变量值改变时候的回调函数
        subscription.createMonitoredItems(TimestampsToReturn.Both, requests, (item, id) -> {
            item.setValueConsumer((i, v) -> {
                handle(i.getReadValueId().getNodeId(), v.getValue());
            });
        }).get();
        return "订阅成功";
    }
    /**
     * * @MethodName: write
     * @Description: å›žè°ƒå‡½æ•°
     * @CreateTime 2023å¹´10月13日
     */
    public void handle(NodeId id, Variant value){
        String className = "cn.stylefeng.guns.opcua.cert.MethodName";
        String methodName = "a";
        String str1 = id.getIdentifier().toString()+":"+value.getValue().toString();
        try {
            Class<?> clazz = Class.forName(className);
            Method method = clazz.getMethod(methodName, String.class);
            method.invoke(clazz.newInstance(), str1);
        } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException |
                 InvocationTargetException e) {
            e.printStackTrace();
        }
        //if(id.getIdentifier().toString().equals("my.device.x1")){
        //    log.info("item={}, value={},{}", id.getIdentifier().toString(), value,"返回一个SN号");
        //}
    }
    /**
     * @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;
        }
        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();
    }
}
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/client/ClientRunner.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,150 @@
package cn.stylefeng.guns.opcua.client;
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;
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.AnonymousProvider;
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 cn.stylefeng.guns.opcua.cert.KeyStoreLoader;
import cn.stylefeng.guns.opcua.config.Properties;
import lombok.extern.slf4j.Slf4j;
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;
/**
 * @ClassName: ClientRunner
 * @Description: å®¢æˆ·ç«¯å¯åŠ¨ç±»
 * @author yyt
 * @date 2023å¹´10月13日
 */
@Slf4j
@Component
public class ClientRunner {
    private final CompletableFuture<OpcUaClient> future = new CompletableFuture<>();
    @Autowired
    private Properties properties;
    @Autowired
    private KeyStoreLoader keyStoreLoader;
    /**
     * @MethodName: run
     * @Description: å¯åŠ¨
     * @return
     * @throws Exception
     * @CreateTime 2023å¹´10月13日
     */
    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 2023å¹´10月13日
     */
    private OpcUaClient createClient() throws Exception {
        Path securityTempDir = Paths.get(properties.getCertPath(), "security");
        Files.createDirectories(securityTempDir);
        if (!Files.exists(securityTempDir)) {
            log.error("unable to create security dir: " + securityTempDir);
            return null;
        }
        KeyStoreLoader loader = keyStoreLoader.load(securityTempDir);
        // æœç´¢OPC节点
        List<EndpointDescription> endpoints = null;
        try {
            //获取安全策略
            endpoints = DiscoveryClient.getEndpoints(properties.getEndpointUrl()).get();
        } catch (Throwable e) {
            // try the explicit discovery endpoint as well
            String discoveryUrl = properties.getEndpointUrl();
            if (!discoveryUrl.endsWith("/")) {
                discoveryUrl += "/";
            }
            discoveryUrl += "discovery";
            log.info("Trying explicit discovery URL: {}", discoveryUrl);
            endpoints = DiscoveryClient.getEndpoints(discoveryUrl).get();
        }
        EndpointDescription endpoint = endpoints.stream()
                .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri())).filter(endpointFilter())
                .findFirst().orElseThrow(() -> new Exception("no desired endpoints returned"));
        OpcUaClientConfig config = OpcUaClientConfig.builder()
                // opc ua自定义的名称
                .setApplicationName(LocalizedText.english("plc"))
                // åœ°å€
                .setApplicationUri("opc.tcp://127.0.0.1:49320")
                .setCertificate(loader.getClientCertificate()).setKeyPair(loader.getClientKeyPair())
                // å®‰å…¨ç­–略等配置
                //.setEndpoint(endpoint).setIdentityProvider(new UsernameProvider("OPCUA", "yyt@8888888888"))
                .setEndpoint(endpoint)
                // åŒ¿åéªŒè¯
                .setIdentityProvider(new AnonymousProvider())
                //等待时间
                .setRequestTimeout(Unsigned.uint(5000)).build();
        return OpcUaClient.create(config);
    }
    /**
     * @MethodName: endpointFilter
     * @Description: endpointFilter
     * @return
     * @CreateTime 2023å¹´10月13日
     */
    private Predicate<EndpointDescription> endpointFilter() {
        return e -> true;
    }
    /**
     * @return the future
     */
    public CompletableFuture<OpcUaClient> getFuture() {
        return future;
    }
}
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/config/Properties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
/**
 * Created by Jellyleo on 2019å¹´12月19日
 * Copyright Â© 2019 jellyleo.com
 * All rights reserved.
 */
package cn.stylefeng.guns.opcua.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import lombok.Getter;
/**
 * @ClassName: Properties
 * @Description: OpcUa参数
 * @author yyt
 * @date 2023å¹´10月13日
 */
@Getter
@Configuration
@PropertySource("classpath:opcua.properties")
public class Properties {
    @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;
}
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/controller/CommonController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,131 @@
package cn.stylefeng.guns.opcua.controller;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.servlet.http.HttpServletRequest;
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 cn.stylefeng.guns.opcua.client.ClientHandler;
import cn.stylefeng.guns.opcua.entity.NodeEntity;
//import com.google.common.collect.Lists;
/**
 * @ClassName: OpcUaController
 * @Description: OpcUa控制器
 * @author yyt
 * @date 2023å¹´10月13日
 */
@Controller
public class CommonController {
    @Autowired
    private ClientHandler clientHandler;
    /**
     * @MethodName: connect
     * @Description: opcua连接并订阅变量
     * @param request
     * @param response
     * @return
     * @CreateTime 2023å¹´10月13日
     */
    @RequestMapping("/connect")
    @ResponseBody
    public String connect() {
        try {
            return clientHandler.connect();
        } catch (Exception e) {
            e.printStackTrace();
            return "fail";
        }
    }
    /**
     * @MethodName: disconnect
     * @Description: disconnect
     * @return
     * @CreateTime 2023å¹´10月13日
     */
    @RequestMapping("/disconnect")
    @ResponseBody
    public String disconnect() {
        try {
            return clientHandler.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
            return "fail";
        }
    }
    /**
     * @MethodName: subscribe
     * @Description: subscribe
     * @return
     * @CreateTime 2023å¹´10月13日
     */
    @RequestMapping("/subscribe")
    @ResponseBody
    public String subscribe(HttpServletRequest request) {
        try {
            List<NodeEntity> nodes = Stream.of(request.getParameter("id").split(","))
                    .map(id -> NodeEntity.builder().index(2).identifier(id).build()).collect(Collectors.toList());
            return clientHandler.subscribe(nodes);
        } catch (Exception e) {
            e.printStackTrace();
            return "fail";
        }
    }
    /**
     * @MethodName: write
     * @Description: èŠ‚点写入
     * @param request
     * @return
     * @CreateTime 2023å¹´10月13日
     */
    @RequestMapping("/write")
    @ResponseBody
    public String write(HttpServletRequest request) {
        NodeEntity node = NodeEntity.builder().index(2).identifier(request.getParameter("id"))
                .value(request.getParameter("value")).type(request.getParameter("type")).build();
        try {
            return clientHandler.write(node);
        } catch (Exception e) {
            e.printStackTrace();
            return "fail";
        }
    }
    /**
     * @MethodName: read
     * @Description: read
     * @param request
     * @return
     * @CreateTime 2023å¹´10月13日
     */
    @RequestMapping("/read")
    @ResponseBody
    public String read(HttpServletRequest request) {
        NodeEntity node = NodeEntity.builder().index(2).identifier(request.getParameter("id")).build();
        try {
            return clientHandler.read(node);
        } catch (Exception e) {
            e.printStackTrace();
            return "fail";
        }
    }
}
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/controller/OpcuaConfController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,137 @@
package cn.stylefeng.guns.opcua.controller;
import cn.stylefeng.guns.base.pojo.page.LayuiPageInfo;
import cn.stylefeng.guns.opcua.entity.OpcuaConf;
import cn.stylefeng.guns.opcua.model.params.OpcuaConfParam;
import cn.stylefeng.guns.opcua.service.OpcuaConfService;
import cn.stylefeng.roses.core.base.controller.BaseController;
import cn.stylefeng.roses.kernel.model.response.ResponseData;
import cn.stylefeng.roses.core.mutidatasource.annotion.DataSource;
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;
/**
 * æŽ§åˆ¶å™¨
 *
 * @author yyt
 * @Date 2023-10-17 09:29:17
 */
@Controller
@RequestMapping("/opcuaConf")
public class OpcuaConfController extends BaseController {
    private String PREFIX = "/modular/bs/opcuaConf";
    @Autowired
    private OpcuaConfService opcuaConfService;
    /**
     * è·³è½¬åˆ°ä¸»é¡µé¢
     *
     * @author yyt
     * @Date 2023-10-17
     */
    @RequestMapping("")
    public String index() {
        return PREFIX + "/opcuaConf.html";
    }
    /**
     * æ–°å¢žé¡µé¢
     *
     * @author yyt
     * @Date 2023-10-17
     */
    @RequestMapping("/add")
    public String add() {
        return PREFIX + "/opcuaConf_add.html";
    }
    /**
     * ç¼–辑页面
     *
     * @author yyt
     * @Date 2023-10-17
     */
    @RequestMapping("/edit")
    public String edit() {
        return PREFIX + "/opcuaConf_edit.html";
    }
    /**
     * æ–°å¢žæŽ¥å£
     *
     * @author yyt
     * @Date 2023-10-17
     */
    @RequestMapping("/addItem")
    @ResponseBody
    @DataSource(name = "self")
    public ResponseData addItem(OpcuaConfParam opcuaConfParam) {
        this.opcuaConfService.add(opcuaConfParam);
        return ResponseData.success();
    }
    /**
     * ç¼–辑接口
     *
     * @author yyt
     * @Date 2023-10-17
     */
    @RequestMapping("/editItem")
    @ResponseBody
    @DataSource(name = "self")
    public ResponseData editItem(OpcuaConfParam opcuaConfParam) {
        this.opcuaConfService.update(opcuaConfParam);
        return ResponseData.success();
    }
    /**
     * åˆ é™¤æŽ¥å£
     *
     * @author yyt
     * @Date 2023-10-17
     */
    @RequestMapping("/delete")
    @ResponseBody
    @DataSource(name = "self")
    public ResponseData delete(OpcuaConfParam opcuaConfParam) {
        this.opcuaConfService.delete(opcuaConfParam);
        return ResponseData.success();
    }
    /**
     * æŸ¥çœ‹è¯¦æƒ…接口
     *
     * @author yyt
     * @Date 2023-10-17
     */
    @RequestMapping("/detail")
    @ResponseBody
    @DataSource(name = "self")
    public ResponseData detail(OpcuaConfParam opcuaConfParam) {
        OpcuaConf detail = this.opcuaConfService.getById(opcuaConfParam.getId());
        return ResponseData.success(detail);
    }
    /**
     * æŸ¥è¯¢åˆ—表
     *
     * @author yyt
     * @Date 2023-10-17
     */
    @ResponseBody
    @RequestMapping("/list")
    @DataSource(name = "self")
    public LayuiPageInfo list(OpcuaConfParam opcuaConfParam) {
        return this.opcuaConfService.findPageBySpec(opcuaConfParam);
    }
}
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/entity/NodeEntity.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package cn.stylefeng.guns.opcua.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder(toBuilder = true)
@AllArgsConstructor
@NoArgsConstructor
public class NodeEntity {
    private Integer index;
    private String identifier;
    private Object value;
    private String type;
    private Integer clientHandle;
}
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/entity/OpcuaConf.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,176 @@
package cn.stylefeng.guns.opcua.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableField;
import java.io.Serializable;
/**
 * <p>
 *
 * </p>
 *
 * @author yyt
 * @since 2023-10-17
 */
@TableName("sys_opcua_conf")
public class OpcuaConf implements Serializable {
    private static final long serialVersionUID=1L;
    /**
     * ID
     */
    @TableId(value = "id", type = IdType.ID_WORKER)
    private Long id;
    /**
     * æ‰€å±žæ¨¡å—
     */
    @TableField("module")
    private String module;
    /**
     * èŠ‚点
     */
    @TableField("node")
    private String node;
    /**
     * é•¿åº¦
     */
    @TableField("length")
    private Integer length;
    /**
     * ç±»åž‹
     */
    @TableField("sys_types")
    private String sys_types;
    /**
     * åŠŸèƒ½è¯´æ˜Ž
     */
    @TableField("functionality")
    private String functionality;
    /**
     * æ˜¯å¦è®¢é˜…
     */
    @TableField("subscribe")
    private Integer subscribe;
    /**
     * è®¢é˜…响应模块
     */
    @TableField("r_module")
    private String rModule;
    /**
     * è®¢é˜…响应函数
     */
    @TableField("r_function")
    private String rFunction;
    /**
     * å¤‡æ³¨
     */
    @TableField("remarks")
    private String remarks;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getModule() {
        return module;
    }
    public void setModule(String module) {
        this.module = module;
    }
    public String getNode() {
        return node;
    }
    public void setNode(String node) {
        this.node = node;
    }
    public Integer getLength() {
        return length;
    }
    public void setLength(Integer length) {
        this.length = length;
    }
    public String getSys_types() {
        return sys_types;
    }
    public void setSys_types(String sys_types) {
        this.sys_types = sys_types;
    }
    public String getFunctionality() {
        return functionality;
    }
    public void setFunctionality(String functionality) {
        this.functionality = functionality;
    }
    public Integer getSubscribe() {
        return subscribe;
    }
    public void setSubscribe(Integer subscribe) {
        this.subscribe = subscribe;
    }
    public String getrModule() {
        return rModule;
    }
    public void setrModule(String rModule) {
        this.rModule = rModule;
    }
    public String getrFunction() {
        return rFunction;
    }
    public void setrFunction(String rFunction) {
        this.rFunction = rFunction;
    }
    public String getRemarks() {
        return remarks;
    }
    public void setRemarks(String remarks) {
        this.remarks = remarks;
    }
    @Override
    public String toString() {
        return "OpcuaConf{" +
        "id=" + id +
        ", module=" + module +
        ", node=" + node +
        ", length=" + length +
        ", sys_types=" + sys_types + ", " + "functionality=" + functionality +
        ", subscribe=" + subscribe +
        ", rModule=" + rModule +
        ", rFunction=" + rFunction +
        ", remarks=" + remarks +
        "}";
    }
}
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/mapper/OpcuaConfMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,56 @@
package cn.stylefeng.guns.opcua.mapper;
import cn.stylefeng.guns.opcua.entity.OpcuaConf;
import cn.stylefeng.guns.opcua.model.params.OpcuaConfParam;
import cn.stylefeng.guns.opcua.model.result.OpcuaConfResult;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
 * <p>
 *  Mapper æŽ¥å£
 * </p>
 *
 * @author yyt
 * @since 2023-10-17
 */
public interface OpcuaConfMapper extends BaseMapper<OpcuaConf> {
    /**
     * èŽ·å–列表
     *
     * @author yyt
     * @Date 2023-10-17
     */
    List<OpcuaConfResult> customList(@Param("paramCondition") OpcuaConfParam paramCondition);
    /**
     * èŽ·å–map列表
     *
     * @author yyt
     * @Date 2023-10-17
     */
    List<Map<String, Object>> customMapList(@Param("paramCondition") OpcuaConfParam paramCondition);
    /**
     * èŽ·å–分页实体列表
     *
     * @author yyt
     * @Date 2023-10-17
     */
    Page<OpcuaConfResult> customPageList(@Param("page") Page page, @Param("paramCondition") OpcuaConfParam paramCondition);
    /**
     * èŽ·å–分页map列表
     *
     * @author yyt
     * @Date 2023-10-17
     */
    Page<Map<String, Object>> customPageMapList(@Param("page") Page page, @Param("paramCondition") OpcuaConfParam paramCondition);
}
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/mapper/mapping/OpcuaConfMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.stylefeng.guns.opcua.mapper.OpcuaConfMapper">
    <!-- é€šç”¨æŸ¥è¯¢æ˜ å°„结果 -->
    <resultMap id="BaseResultMap" type="cn.stylefeng.guns.opcua.entity.OpcuaConf">
        <id column="id" property="id" />
        <result column="module" property="module" />
        <result column="node" property="node" />
        <result column="length" property="length" />
        <result column="sys_types" property="sys_types" />
        <result column="functionality" property="functionality" />
        <result column="subscribe" property="subscribe" />
        <result column="r_module" property="rModule" />
        <result column="r_function" property="rFunction" />
        <result column="remarks" property="remarks" />
    </resultMap>
    <!-- é€šç”¨æŸ¥è¯¢ç»“果列 -->
    <sql id="Base_Column_List">
        id AS "id", module AS "module", node AS "node", length AS "length",sys_types AS "sys_types",functionality AS "functionality", subscribe AS "subscribe", r_module AS "rModule", r_function AS "rFunction", remarks AS "remarks"
    </sql>
    <select id="customList" resultType="cn.stylefeng.guns.opcua.model.result.OpcuaConfResult" parameterType="cn.stylefeng.guns.opcua.model.params.OpcuaConfParam">
        select
        <include refid="Base_Column_List"/>
        from sys_opcua_conf where 1 = 1
    </select>
    <select id="customMapList" resultType="map" parameterType="cn.stylefeng.guns.opcua.model.params.OpcuaConfParam">
        select
        <include refid="Base_Column_List"/>
        from sys_opcua_conf where 1 = 1
    </select>
    <select id="customPageList" resultType="cn.stylefeng.guns.opcua.model.result.OpcuaConfResult" parameterType="cn.stylefeng.guns.opcua.model.params.OpcuaConfParam">
        select
        <include refid="Base_Column_List"/>
        from sys_opcua_conf where 1 = 1
    </select>
    <select id="customPageMapList" resultType="map" parameterType="cn.stylefeng.guns.opcua.model.params.OpcuaConfParam">
        select
        <include refid="Base_Column_List"/>
        from sys_opcua_conf where 1 = 1
    </select>
</mapper>
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/model/params/OpcuaConfParam.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,78 @@
package cn.stylefeng.guns.opcua.model.params;
import lombok.Data;
import cn.stylefeng.roses.kernel.model.validator.BaseValidatingParam;
import java.util.Date;
import java.io.Serializable;
import java.math.BigDecimal;
/**
 * <p>
 *
 * </p>
 *
 * @author yyt
 * @since 2023-10-17
 */
@Data
public class OpcuaConfParam implements Serializable, BaseValidatingParam {
    private static final long serialVersionUID = 1L;
    /**
     * ID
     */
    private Long id;
    /**
     * æ‰€å±žæ¨¡å—
     */
    private String module;
    /**
     * èŠ‚点
     */
    private String node;
    /**
     * é•¿åº¦
     */
    private Integer length;
    /**
     * ç±»åž‹
     */
    private String sys_types;
    /**
     * åŠŸèƒ½è¯´æ˜Ž
     */
    private String functionality;
    /**
     * æ˜¯å¦è®¢é˜…
     */
    private Integer subscribe;
    /**
     * è®¢é˜…响应模块
     */
    private String rModule;
    /**
     * è®¢é˜…响应函数
     */
    private String rFunction;
    /**
     * å¤‡æ³¨
     */
    private String remarks;
    @Override
    public String checkParam() {
        return null;
    }
}
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/model/result/OpcuaConfResult.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,72 @@
package cn.stylefeng.guns.opcua.model.result;
import lombok.Data;
import java.util.Date;
import java.io.Serializable;
import java.math.BigDecimal;
/**
 * <p>
 *
 * </p>
 *
 * @author yyt
 * @since 2023-10-17
 */
@Data
public class OpcuaConfResult implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * ID
     */
    private Long id;
    /**
     * æ‰€å±žæ¨¡å—
     */
    private String module;
    /**
     * èŠ‚点
     */
    private String node;
    /**
     * é•¿åº¦
     */
    private Integer length;
    /**
     * ç±»åž‹
     */
    private String sys_types;
    /**
     * åŠŸèƒ½è¯´æ˜Ž
     */
    private String functionality;
    /**
     * æ˜¯å¦è®¢é˜…
     */
    private Integer subscribe;
    /**
     * è®¢é˜…响应模块
     */
    private String rModule;
    /**
     * è®¢é˜…响应函数
     */
    private String rFunction;
    /**
     * å¤‡æ³¨
     */
    private String remarks;
}
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/service/OpcuaConfService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
package cn.stylefeng.guns.opcua.service;
import cn.stylefeng.guns.base.pojo.page.LayuiPageInfo;
import cn.stylefeng.guns.opcua.entity.OpcuaConf;
import cn.stylefeng.guns.opcua.model.params.OpcuaConfParam;
import cn.stylefeng.guns.opcua.model.result.OpcuaConfResult;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
/**
 * <p>
 *  æœåŠ¡ç±»
 * </p>
 *
 * @author yyt
 * @since 2023-10-17
 */
public interface OpcuaConfService extends IService<OpcuaConf> {
    /**
     * æ–°å¢ž
     *
     * @author yyt
     * @Date 2023-10-17
     */
    void add(OpcuaConfParam param);
    /**
     * åˆ é™¤
     *
     * @author yyt
     * @Date 2023-10-17
     */
    void delete(OpcuaConfParam param);
    /**
     * æ›´æ–°
     *
     * @author yyt
     * @Date 2023-10-17
     */
    void update(OpcuaConfParam param);
    /**
     * æŸ¥è¯¢å•æ¡æ•°æ®ï¼ŒSpecification模式
     *
     * @author yyt
     * @Date 2023-10-17
     */
    OpcuaConfResult findBySpec(OpcuaConfParam param);
    /**
     * æŸ¥è¯¢åˆ—表,Specification模式
     *
     * @author yyt
     * @Date 2023-10-17
     */
    List<OpcuaConfResult> findListBySpec(OpcuaConfParam param);
    /**
     * æŸ¥è¯¢åˆ†é¡µæ•°æ®ï¼ŒSpecification模式
     *
     * @author yyt
     * @Date 2023-10-17
     */
     LayuiPageInfo findPageBySpec(OpcuaConfParam param);
}
guns-vip-main/src/main/java/cn/stylefeng/guns/opcua/service/impl/OpcuaConfServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,84 @@
package cn.stylefeng.guns.opcua.service.impl;
import cn.stylefeng.guns.base.pojo.page.LayuiPageFactory;
import cn.stylefeng.guns.base.pojo.page.LayuiPageInfo;
import cn.stylefeng.guns.opcua.entity.OpcuaConf;
import cn.stylefeng.guns.opcua.mapper.OpcuaConfMapper;
import cn.stylefeng.guns.opcua.model.params.OpcuaConfParam;
import cn.stylefeng.guns.opcua.model.result.OpcuaConfResult;
import  cn.stylefeng.guns.opcua.service.OpcuaConfService;
import cn.stylefeng.roses.core.util.ToolUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.io.Serializable;
import java.util.List;
/**
 * <p>
 *  æœåŠ¡å®žçŽ°ç±»
 * </p>
 *
 * @author yyt
 * @since 2023-10-17
 */
@Service
public class OpcuaConfServiceImpl extends ServiceImpl<OpcuaConfMapper, OpcuaConf> implements OpcuaConfService {
    @Override
    public void add(OpcuaConfParam param){
        OpcuaConf entity = getEntity(param);
        this.save(entity);
    }
    @Override
    public void delete(OpcuaConfParam param){
        this.removeById(getKey(param));
    }
    @Override
    public void update(OpcuaConfParam param){
        OpcuaConf oldEntity = getOldEntity(param);
        OpcuaConf newEntity = getEntity(param);
        ToolUtil.copyProperties(newEntity, oldEntity);
        this.updateById(newEntity);
    }
    @Override
    public OpcuaConfResult findBySpec(OpcuaConfParam param){
        return null;
    }
    @Override
    public List<OpcuaConfResult> findListBySpec(OpcuaConfParam param){
        return null;
    }
    @Override
    public LayuiPageInfo findPageBySpec(OpcuaConfParam param){
        Page pageContext = getPageContext();
        IPage page = this.baseMapper.customPageList(pageContext, param);
        return LayuiPageFactory.createPageInfo(page);
    }
    private Serializable getKey(OpcuaConfParam param){
        return param.getId();
    }
    private Page getPageContext() {
        return LayuiPageFactory.defaultPage();
    }
    private OpcuaConf getOldEntity(OpcuaConfParam param) {
        return this.getById(getKey(param));
    }
    private OpcuaConf getEntity(OpcuaConfParam param) {
        OpcuaConf entity = new OpcuaConf();
        ToolUtil.copyProperties(param, entity);
        return entity;
    }
}
guns-vip-main/src/main/resources/opcua.properties
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
#opcua?????
opcua.server.endpoint.url=opc.tcp://127.0.0.1:49320
opcua.server.idp.username=administrator
opcua.server.idp.password=yyt@8888888888
#opcua?????
opcua.client.app.name=plc
opcua.client.app.uri=urn:Yyt_PC:UnifiedAutomation:UaExpert
opcua.client.cert.path=C:/Users/30672/Desktop
opcua.client.cert.file=Yyt_PC-client.pfx
opcua.client.cert.alias=jlclient-ai
opcua.client.cert.common.name=UaClient@Jellyleo
opcua.client.cert.organization=JL
opcua.client.cert.organization.unit=per
opcua.client.cert.locality.name=jl
opcua.client.cert.state.name=JiangSu
opcua.client.cert.country.code=CN
opcua.client.cert.dns.name=Jellyleo
opcua.client.cert.ip.address=administrator
opcua.client.cert.keystore.password=yyt@8888888888
guns-vip-main/src/main/resources/opcua.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
#opcua服务端设置
opcua:
  server:
      endpoint:
          url: opc.tcp://127.0.0.1:49320
      idp:
          username: administrator
          password: yyt@8888888888
  #opcua客户端设置
  client:
      app:
          name: plc
          uri: urn:Yyt_PC:UnifiedAutomation:UaExpert
      cert:
          path: C:/Users/30672/Desktop
          file: Yyt_PC-client.pfx
          alias: jlclient-ai
          common:
              name: UaClient@Jellyleo
#opcua.client.cert.organization=JL
#opcua.client.cert.organization.unit=per
#opcua.client.cert.locality.name=jl
#opcua.client.cert.state.name=JiangSu
#opcua.client.cert.country.code=CN
#opcua.client.cert.dns.name=Jellyleo
#opcua.client.cert.ip.address=administrator
#opcua.client.cert.keystore.password=yyt@8888888888
guns-vip-main/src/main/webapp/assets/modular/bs/opcuaConf/opcuaConf.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,132 @@
layui.use(['table', 'admin', 'ax', 'func'], function () {
    var $ = layui.$;
    var table = layui.table;
    var $ax = layui.ax;
    var admin = layui.admin;
    var func = layui.func;
    /**
     * ç®¡ç†
     */
    var OpcuaConf = {
        tableId: "opcuaConfTable"
    };
    /**
     * åˆå§‹åŒ–表格的列
     */
    OpcuaConf.initColumn = function () {
        return [[
            {type: 'checkbox'},
            {field: 'id', hide: true, title: 'ID'},
            {field: 'module', sort: true, title: '所属模块'},
            {field: 'node', sort: true, title: '节点'},
            {field: 'length', sort: true, title: '长度'},
            {field: 'types', sort: true, title: '类型'},
            {field: 'functionality', sort: true, title: '功能说明'},
            {field: 'subscribe', sort: true, title: '是否订阅'},
            {field: 'rModule', sort: true, title: '订阅响应模块'},
            {field: 'rFunction', sort: true, title: '订阅响应函数'},
            {field: 'remarks', sort: true, title: '备注'},
            {fixed: 'right',width: 125, minWidth: 125, align: 'center', toolbar: '#tableBar', title: '操作'}
        ]];
    };
    /**
     * ç‚¹å‡»æŸ¥è¯¢æŒ‰é’®
     */
    OpcuaConf.search = function () {
        var queryData = {};
        table.reload(OpcuaConf.tableId, {
            where: queryData, page: {curr: 1}
        });
    };
    /**
     * è·³è½¬åˆ°æ·»åŠ é¡µé¢
     */
    OpcuaConf.jumpAddPage = function () {
        window.location.href = Feng.ctxPath + '/opcuaConf/add'
    };
    /**
    * è·³è½¬åˆ°ç¼–辑页面
    *
    * @param data ç‚¹å‡»æŒ‰é’®æ—¶å€™çš„行数据
    */
    OpcuaConf.jumpEditPage = function (data) {
        window.location.href = Feng.ctxPath + '/opcuaConf/edit?id=' + data.id
    };
    /**
     * å¯¼å‡ºexcel按钮
     */
    OpcuaConf.exportExcel = function () {
        var checkRows = table.checkStatus(OpcuaConf.tableId);
        if (checkRows.data.length === 0) {
            Feng.error("请选择要导出的数据");
        } else {
            table.exportFile(tableResult.config.id, checkRows.data, 'xls');
        }
    };
    /**
     * ç‚¹å‡»åˆ é™¤
     *
     * @param data ç‚¹å‡»æŒ‰é’®æ—¶å€™çš„行数据
     */
    OpcuaConf.onDeleteItem = function (data) {
        var operation = function () {
            var ajax = new $ax(Feng.ctxPath + "/opcuaConf/delete", function (data) {
                Feng.success("删除成功!");
                table.reload(OpcuaConf.tableId);
            }, function (data) {
                Feng.error("删除失败!" + data.responseJSON.message + "!");
            });
            ajax.set("id", data.id);
            ajax.start();
        };
        Feng.confirm("是否删除?", operation);
    };
    // æ¸²æŸ“表格
    var tableResult = table.render({
        elem: '#' + OpcuaConf.tableId,
        url: Feng.ctxPath + '/opcuaConf/list',
        page: true,
        height: "full-158",
        cellMinWidth: 100,
        cols: OpcuaConf.initColumn()
    });
    // æœç´¢æŒ‰é’®ç‚¹å‡»äº‹ä»¶
    $('#btnSearch').click(function () {
        OpcuaConf.search();
    });
    // æ·»åŠ æŒ‰é’®ç‚¹å‡»äº‹ä»¶
    $('#btnAdd').click(function () {
    OpcuaConf.jumpAddPage();
    });
    // å¯¼å‡ºexcel
    $('#btnExp').click(function () {
        OpcuaConf.exportExcel();
    });
    // å·¥å…·æ¡ç‚¹å‡»äº‹ä»¶
    table.on('tool(' + OpcuaConf.tableId + ')', function (obj) {
        var data = obj.data;
        var layEvent = obj.event;
        if (layEvent === 'edit') {
            OpcuaConf.jumpEditPage(data);
        } else if (layEvent === 'delete') {
            OpcuaConf.onDeleteItem(data);
        }
    });
});
guns-vip-main/src/main/webapp/assets/modular/bs/opcuaConf/opcuaConf_add.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
/**
 * æ·»åŠ æˆ–者修改页面
 */
var OpcuaConfInfoDlg = {
    data: {
        id: "",
        module: "",
        node: "",
        length: "",
        types: "",
        functionality: "",
        subscribe: "",
        rModule: "",
        rFunction: "",
        remarks: ""
    }
};
layui.use(['form', 'admin', 'ax','laydate','upload','formSelects'], function () {
    var $ = layui.jquery;
    var $ax = layui.ax;
    var form = layui.form;
    var admin = layui.admin;
    //表单提交事件
    form.on('submit(btnSubmit)', function (data) {
        var ajax = new $ax(Feng.ctxPath + "/opcuaConf/addItem", function (data) {
            Feng.success("添加成功!");
            window.location.href = Feng.ctxPath + '/opcuaConf'
        }, function (data) {
            Feng.error("添加失败!" + data.responseJSON.message)
        });
        ajax.set(data.field);
        ajax.start();
        return false;
    });
    $('#cancel').click(function(){
        window.location.href = Feng.ctxPath + '/opcuaConf'
    });
});
guns-vip-main/src/main/webapp/assets/modular/bs/opcuaConf/opcuaConf_edit.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
/**
 * è¯¦æƒ…对话框
 */
var OpcuaConfInfoDlg = {
    data: {
        id: "",
        module: "",
        node: "",
        length: "",
        types: "",
        functionality: "",
        subscribe: "",
        rModule: "",
        rFunction: "",
        remarks: ""
    }
};
layui.use(['form', 'admin', 'ax','laydate','upload','formSelects'], function () {
    var $ = layui.jquery;
    var $ax = layui.ax;
    var form = layui.form;
    var admin = layui.admin;
    //获取详情信息,填充表单
    var ajax = new $ax(Feng.ctxPath + "/opcuaConf/detail?id=" + Feng.getUrlParam("id"));
    var result = ajax.start();
    form.val('opcuaConfForm', result.data);
    //表单提交事件
    form.on('submit(btnSubmit)', function (data) {
        var ajax = new $ax(Feng.ctxPath + "/opcuaConf/editItem", function (data) {
            Feng.success("更新成功!");
            window.location.href = Feng.ctxPath + '/opcuaConf'
        }, function (data) {
            Feng.error("更新失败!" + data.responseJSON.message)
        });
        ajax.set(data.field);
        ajax.start();
        return false;
    });
    $('#cancel').click(function(){
        window.location.href = Feng.ctxPath + '/opcuaConf'
    });
});
guns-vip-main/src/main/webapp/pages/modular/bs/opcuaConf/opcuaConf.html
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
@layout("/common/_container.html",{js:["/assets/modular/bs/opcuaConf/opcuaConf.js"]}){
<div class="layui-body-header">
    <span class="layui-body-header-title">管理</span>
</div>
<div class="layui-fluid">
    <div class="layui-row layui-col-space15">
        <div class="layui-col-sm12 layui-col-md12 layui-col-lg12">
            <div class="layui-card">
                <div class="layui-card-body">
                    <div class="layui-form toolbar">
                        <div class="layui-form-item">
                            <div class="layui-inline">
                                <button id="btnSearch" class="layui-btn icon-btn"><i class="layui-icon">&#xe615;</i>搜索</button>
                                <button id="btnAdd" class="layui-btn icon-btn"><i class="layui-icon">&#xe654;</i>添加</button>
                                <button id="btnExp" class="layui-btn icon-btn"><i class="layui-icon">&#xe67d;</i>导出</button>
                            </div>
                        </div>
                    </div>
                    <table class="layui-table" id="opcuaConfTable" lay-filter="opcuaConfTable"></table>
                </div>
            </div>
        </div>
    </div>
</div>
<script type="text/html" id="tableBar">
    <a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="edit">修改</a>
    <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="delete">删除</a>
</script>
@}
guns-vip-main/src/main/webapp/pages/modular/bs/opcuaConf/opcuaConf_add.html
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,86 @@
@layout("/common/_form.html",{js:["/assets/modular/bs/opcuaConf/opcuaConf_add.js"],css:["/assets/expand/module/formSelects/formSelects-v4.css"]}){
<form class="layui-form" id="opcuaConfForm" lay-filter="opcuaConfForm">
    <div class="layui-fluid" style="padding-bottom: 75px;">
        <div class="layui-card">
            <div class="layui-card-header">基本信息</div>
            <div class="layui-card-body">
                <div class="layui-form-item layui-row">
                    <input name="id" type="hidden"/>
                    <div class="layui-inline layui-col-md12">
                        <label class="layui-form-label">所属模块<span style="color: red;">*</span></label>
                        <div class="layui-input-block">
                            <input id="module" name="module" placeholder="请输入所属模块" type="text" class="layui-input" lay-verify="required" required/>
                        </div>
                    </div>
                    <div class="layui-inline layui-col-md12">
                        <label class="layui-form-label">节点<span style="color: red;">*</span></label>
                        <div class="layui-input-block">
                            <input id="node" name="node" placeholder="请输入节点" type="text" class="layui-input" lay-verify="required" required/>
                        </div>
                    </div>
                    <div class="layui-inline layui-col-md12">
                        <label class="layui-form-label">长度<span style="color: red;">*</span></label>
                        <div class="layui-input-block">
                            <input id="length" name="length" placeholder="请输入长度" type="text" class="layui-input" lay-verify="required" required/>
                        </div>
                    </div>
                    <div class="layui-inline layui-col-md12">
                        <label class="layui-form-label">类型<span style="color: red;">*</span></label>
                        <div class="layui-input-block">
                            <input id="
types" name="
types" placeholder="请输入类型" type="text" class="layui-input" lay-verify="required" required/>
                        </div>
                    </div>
                    <div class="layui-inline layui-col-md12">
                        <label class="layui-form-label">功能说明<span style="color: red;">*</span></label>
                        <div class="layui-input-block">
                            <input id="
functionality" name="
functionality" placeholder="请输入功能说明" type="text" class="layui-input" lay-verify="required" required/>
                        </div>
                    </div>
                    <div class="layui-inline layui-col-md12">
                        <label class="layui-form-label">是否订阅<span style="color: red;">*</span></label>
                        <div class="layui-input-block">
                            <input id="subscribe" name="subscribe" placeholder="请输入是否订阅" type="text" class="layui-input" lay-verify="required" required/>
                        </div>
                    </div>
                    <div class="layui-inline layui-col-md12">
                        <label class="layui-form-label">订阅响应模块<span style="color: red;">*</span></label>
                        <div class="layui-input-block">
                            <input id="rModule" name="rModule" placeholder="请输入订阅响应模块" type="text" class="layui-input" lay-verify="required" required/>
                        </div>
                    </div>
                    <div class="layui-inline layui-col-md12">
                        <label class="layui-form-label">订阅响应函数<span style="color: red;">*</span></label>
                        <div class="layui-input-block">
                            <input id="rFunction" name="rFunction" placeholder="请输入订阅响应函数" type="text" class="layui-input" lay-verify="required" required/>
                        </div>
                    </div>
                    <div class="layui-inline layui-col-md12">
                        <label class="layui-form-label">备注<span style="color: red;">*</span></label>
                        <div class="layui-input-block">
                            <input id="remarks" name="remarks" placeholder="请输入备注" type="text" class="layui-input" lay-verify="required" required/>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="form-group-bottom text-center">
        <button class="layui-btn" lay-filter="btnSubmit" lay-submit>&emsp;提交&emsp;</button>
        <button type="reset" class="layui-btn layui-btn-primary" id="cancel">&emsp;取消&emsp;</button>
    </div>
</form>
@}
guns-vip-main/src/main/webapp/pages/modular/bs/opcuaConf/opcuaConf_edit.html
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,86 @@
@layout("/common/_form.html",{js:["/assets/modular/bs/opcuaConf/opcuaConf_edit.js"]}){
<form class="layui-form" id="opcuaConfForm" lay-filter="opcuaConfForm">
    <div class="layui-fluid" style="padding-bottom: 75px;">
        <div class="layui-card">
            <div class="layui-card-header">基本信息</div>
            <div class="layui-card-body">
                <div class="layui-form-item layui-row">
                    <input name="id" type="hidden"/>
                    <div class="layui-inline layui-col-md12">
                        <label class="layui-form-label">所属模块<span style="color: red;">*</span></label>
                        <div class="layui-input-block">
                            <input id="module" name="module" placeholder="请输入所属模块" type="text" class="layui-input" lay-verify="required" required/>
                        </div>
                    </div>
                    <div class="layui-inline layui-col-md12">
                        <label class="layui-form-label">节点<span style="color: red;">*</span></label>
                        <div class="layui-input-block">
                            <input id="node" name="node" placeholder="请输入节点" type="text" class="layui-input" lay-verify="required" required/>
                        </div>
                    </div>
                    <div class="layui-inline layui-col-md12">
                        <label class="layui-form-label">长度<span style="color: red;">*</span></label>
                        <div class="layui-input-block">
                            <input id="length" name="length" placeholder="请输入长度" type="text" class="layui-input" lay-verify="required" required/>
                        </div>
                    </div>
                    <div class="layui-inline layui-col-md12">
                        <label class="layui-form-label">类型<span style="color: red;">*</span></label>
                        <div class="layui-input-block">
                            <input id="
types" name="
types" placeholder="请输入类型" type="text" class="layui-input" lay-verify="required" required/>
                        </div>
                    </div>
                    <div class="layui-inline layui-col-md12">
                        <label class="layui-form-label">功能说明<span style="color: red;">*</span></label>
                        <div class="layui-input-block">
                            <input id="
functionality" name="
functionality" placeholder="请输入功能说明" type="text" class="layui-input" lay-verify="required" required/>
                        </div>
                    </div>
                    <div class="layui-inline layui-col-md12">
                        <label class="layui-form-label">是否订阅<span style="color: red;">*</span></label>
                        <div class="layui-input-block">
                            <input id="subscribe" name="subscribe" placeholder="请输入是否订阅" type="text" class="layui-input" lay-verify="required" required/>
                        </div>
                    </div>
                    <div class="layui-inline layui-col-md12">
                        <label class="layui-form-label">订阅响应模块<span style="color: red;">*</span></label>
                        <div class="layui-input-block">
                            <input id="rModule" name="rModule" placeholder="请输入订阅响应模块" type="text" class="layui-input" lay-verify="required" required/>
                        </div>
                    </div>
                    <div class="layui-inline layui-col-md12">
                        <label class="layui-form-label">订阅响应函数<span style="color: red;">*</span></label>
                        <div class="layui-input-block">
                            <input id="rFunction" name="rFunction" placeholder="请输入订阅响应函数" type="text" class="layui-input" lay-verify="required" required/>
                        </div>
                    </div>
                    <div class="layui-inline layui-col-md12">
                        <label class="layui-form-label">备注<span style="color: red;">*</span></label>
                        <div class="layui-input-block">
                            <input id="remarks" name="remarks" placeholder="请输入备注" type="text" class="layui-input" lay-verify="required" required/>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="form-group-bottom text-center">
        <button class="layui-btn" lay-filter="btnSubmit" lay-submit>&emsp;提交&emsp;</button>
        <button type="reset" class="layui-btn layui-btn-primary" id="cancel">&emsp;取消&emsp;</button>
    </div>
</form>
@}