Java模拟OPC Server服务器并创建节点代码实现

news2024/11/15 11:27:29

前言

模拟OPC Server服务器的方法除了使用KEPServerEX6软件以外,还可以使用java代码模拟启动一个opc server。下文详细讲解,如何使用java代码,实现模拟一个或者多个opc server服务器。

引入依赖

首先在Maven项目的pom.xml文件中引入所需的依赖

		<dependency>
			<groupId>org.eclipse.milo</groupId>
			<artifactId>sdk-server</artifactId>
			<version>0.6.9</version>
		</dependency>
		<dependency>
			<groupId>org.eclipse.milo</groupId>
			<artifactId>dictionary-manager</artifactId>
			<version>0.6.9</version>
		</dependency>

创建Server

创建opc server代码实现:

   /**
     * 方法描述: 创建opcUaServer
     *
     * @param port 端口
     * @return {@link OpcUaServer}
     * @throws
     */
    private OpcUaServer startServer(int port){
        Set<EndpointConfiguration> endpointConfigurations=new HashSet<>();
        EndpointConfiguration endpointConfiguration=EndpointConfiguration.newBuilder().setBindPort(port).build();
        endpointConfigurations.add(endpointConfiguration);
        System.out.println(endpointConfiguration.getEndpointUrl());
        OpcUaServerConfig serverConfig = OpcUaServerConfig.builder()
                .setApplicationName(LocalizedText.english("Server Application"))
                .setApplicationUri("urn:eclipse:milo:examples:server")
                .setProductUri("urn:eclipse:milo:examples:server")
                .setEndpoints(endpointConfigurations)
                .build();
        OpcUaServer server = new OpcUaServer(serverConfig);
        server.startup();
        return server;
    }

在EndpointConfiguration的newBuilder方法中,我看可以知道,如果我们不设置端口,默认就是 12685.
在这里插入图片描述

创建自定义Namespace

创建TestNamespace类,继承org.eclipse.milo sdk-server 中的ManagedNamespaceWithLifecycle类,并声明构造器,代码如下:

public static final String NAMESPACE_URI = "urn:eclipse:milo:opc";


    private final Logger logger = LoggerFactory.getLogger(getClass());

    private volatile Thread eventThread;
    private volatile boolean keepPostingEvents = true;

    private final DataTypeDictionaryManager dictionaryManager;

    private final SubscriptionModel subscriptionModel;

    public TestNamespace(OpcUaServer server) {
        super(server, NAMESPACE_URI);

        subscriptionModel = new SubscriptionModel(server, this);
        dictionaryManager = new DataTypeDictionaryManager(getNodeContext(), NAMESPACE_URI);

        getLifecycleManager().addLifecycle(dictionaryManager);
        getLifecycleManager().addLifecycle(subscriptionModel);

        getLifecycleManager().addLifecycle(new Lifecycle() {
            @Override
            public void startup() {
                startBogusEventNotifier();
            }

            @Override
            public void shutdown() {
                try {
                    keepPostingEvents = false;
                    eventThread.interrupt();
                    eventThread.join();
                } catch (InterruptedException ignored) {
                    // ignored
                }
            }
        });
    }

重载Lifecycle的方法

重载org.eclipse.milo.opcua.sdk.server.Lifecycle 的 startup方法和shutdown方法,启动时,创建事件通知器。代码如下:

    private void startBogusEventNotifier() {
        // Set the EventNotifier bit on Server Node for Events.
        UaNode serverNode = getServer()
                .getAddressSpaceManager()
                .getManagedNode(Identifiers.Server)
                .orElse(null);

        if (serverNode instanceof ServerTypeNode) {
            ((ServerTypeNode) serverNode).setEventNotifier(ubyte(1));

            // Post a bogus Event every couple seconds
            eventThread = new Thread(() -> {
                while (keepPostingEvents) {
                    try {
                        BaseEventTypeNode eventNode = getServer().getEventFactory().createEvent(
                                newNodeId(UUID.randomUUID()),
                                Identifiers.BaseEventType
                        );
                        eventNode.setBrowseName(new QualifiedName(1, "foo"));
                        eventNode.setDisplayName(LocalizedText.english("foo"));
                        eventNode.setEventId(ByteString.of(new byte[]{0, 1, 2, 3}));
                        eventNode.setEventType(Identifiers.BaseEventType);
                        eventNode.setSourceNode(serverNode.getNodeId());
                        eventNode.setSourceName(serverNode.getDisplayName().getText());
                        eventNode.setTime(DateTime.now());
                        eventNode.setReceiveTime(DateTime.NULL_VALUE);
                        eventNode.setMessage(LocalizedText.english("event message!"));
                        eventNode.setSeverity(ushort(2));
                        //noinspection UnstableApiUsage
                        getServer().getEventBus().post(eventNode);
                        eventNode.delete();
                    } catch (Throwable e) {
                        logger.error("Error creating EventNode: {}", e.getMessage(), e);
                    }

                    try {
                        //noinspection BusyWait
                        Thread.sleep(2_000);
                    } catch (InterruptedException ignored) {
                        // ignored
                    }
                }
            }, "bogus-event-poster");
            eventThread.start();
        }
    }

创建opc ua 节点方法

   /**
     * 方法描述: 创建节点方法
     *
     * @param keys 节点名称集合
     * @return
     * @throws
     */
    public void addNodes(Set<String> keys) {
        // Create a "opc" folder and add it to the node manager
        NodeId folderNodeId = newNodeId("opc");
        UaFolderNode folderNode = new UaFolderNode(
                getNodeContext(),
                folderNodeId,
                newQualifiedName("opc"),
                LocalizedText.english("opc")
        );
        getNodeManager().addNode(folderNode);
        // Make sure our new folder shows up under the server's Objects folder.
        folderNode.addReference(new Reference(
                folderNode.getNodeId(),
                Identifiers.Organizes,
                Identifiers.ObjectsFolder.expanded(),
                false
        ));
        for (String key : keys) {
            NodeId typeId = Identifiers.Double;
            Variant variant = new Variant(0d);
            UaVariableNode node = new UaVariableNode.UaVariableNodeBuilder(getNodeContext())
                    .setNodeId(newNodeId(key))
                    .setAccessLevel(AccessLevel.READ_WRITE)
                    .setUserAccessLevel(AccessLevel.READ_WRITE)
                    .setBrowseName(newQualifiedName(key))
                    .setDisplayName(LocalizedText.english(key))
                    .setDataType(typeId)
                    .setTypeDefinition(Identifiers.BaseDataVariableType)
                    .build();
            node.setValue(new DataValue(variant));
            getNodeManager().addNode(node);
            folderNode.addOrganizes(node);
        }
    }

先创建一个“opc”文件夹并将其添加到节点管理器中,然后根据传入的节点名称,循环遍历,创建到“opc”文件夹下,生成变量类型的节点。

重载ManagedNamespaceWithLifecycle虚拟方法

重载继承类ManagedNamespaceWithLifecycle的虚拟方法
代码如下:


    @Override
    public void onDataItemsCreated(List<DataItem> dataItems) {
        subscriptionModel.onDataItemsCreated(dataItems);
    }

    @Override
    public void onDataItemsModified(List<DataItem> dataItems) {
        subscriptionModel.onDataItemsModified(dataItems);
    }

    @Override
    public void onDataItemsDeleted(List<DataItem> dataItems) {
        subscriptionModel.onDataItemsDeleted(dataItems);
    }

    @Override
    public void onMonitoringModeChanged(List<MonitoredItem> monitoredItems) {
        subscriptionModel.onMonitoringModeChanged(monitoredItems);
    }

完整代码实现:


import org.eclipse.milo.opcua.sdk.core.AccessLevel;
import org.eclipse.milo.opcua.sdk.core.Reference;
import org.eclipse.milo.opcua.sdk.server.Lifecycle;
import org.eclipse.milo.opcua.sdk.server.OpcUaServer;
import org.eclipse.milo.opcua.sdk.server.api.DataItem;
import org.eclipse.milo.opcua.sdk.server.api.ManagedNamespaceWithLifecycle;
import org.eclipse.milo.opcua.sdk.server.api.MonitoredItem;
import org.eclipse.milo.opcua.sdk.server.dtd.DataTypeDictionaryManager;
import org.eclipse.milo.opcua.sdk.server.model.nodes.objects.BaseEventTypeNode;
import org.eclipse.milo.opcua.sdk.server.model.nodes.objects.ServerTypeNode;
import org.eclipse.milo.opcua.sdk.server.nodes.UaFolderNode;
import org.eclipse.milo.opcua.sdk.server.nodes.UaNode;
import org.eclipse.milo.opcua.sdk.server.nodes.UaVariableNode;
import org.eclipse.milo.opcua.sdk.server.util.SubscriptionModel;
import org.eclipse.milo.opcua.stack.core.Identifiers;
import org.eclipse.milo.opcua.stack.core.types.builtin.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.ubyte;
import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.ushort;

/**
 * @author Lenovo
 */
public class TestNamespace extends ManagedNamespaceWithLifecycle {

    public static final String NAMESPACE_URI = "urn:eclipse:milo:opc";


    private final Logger logger = LoggerFactory.getLogger(getClass());

    private volatile Thread eventThread;
    private volatile boolean keepPostingEvents = true;

    private final DataTypeDictionaryManager dictionaryManager;

    private final SubscriptionModel subscriptionModel;

    public TestNamespace(OpcUaServer server) {
        super(server, NAMESPACE_URI);

        subscriptionModel = new SubscriptionModel(server, this);
        dictionaryManager = new DataTypeDictionaryManager(getNodeContext(), NAMESPACE_URI);

        getLifecycleManager().addLifecycle(dictionaryManager);
        getLifecycleManager().addLifecycle(subscriptionModel);

        getLifecycleManager().addLifecycle(new Lifecycle() {
            @Override
            public void startup() {
                startBogusEventNotifier();
            }

            @Override
            public void shutdown() {
                try {
                    keepPostingEvents = false;
                    eventThread.interrupt();
                    eventThread.join();
                } catch (InterruptedException ignored) {
                    // ignored
                }
            }
        });
    }

    /**
     * 方法描述: 创建节点方法
     *
     * @param keys
     * @return
     * @throws
     */
    public void addNodes(Set<String> keys) {
        // Create a "HelloWorld" folder and add it to the node manager
        NodeId folderNodeId = newNodeId("opc");
        UaFolderNode folderNode = new UaFolderNode(
                getNodeContext(),
                folderNodeId,
                newQualifiedName("opc"),
                LocalizedText.english("opc")
        );
        getNodeManager().addNode(folderNode);
        // Make sure our new folder shows up under the server's Objects folder.
        folderNode.addReference(new Reference(
                folderNode.getNodeId(),
                Identifiers.Organizes,
                Identifiers.ObjectsFolder.expanded(),
                false
        ));
        for (String key : keys) {
            NodeId typeId = Identifiers.Double;
            Variant variant = new Variant(0d);
            UaVariableNode node = new UaVariableNode.UaVariableNodeBuilder(getNodeContext())
                    .setNodeId(newNodeId(key))
                    .setAccessLevel(AccessLevel.READ_WRITE)
                    .setUserAccessLevel(AccessLevel.READ_WRITE)
                    .setBrowseName(newQualifiedName(key))
                    .setDisplayName(LocalizedText.english(key))
                    .setDataType(typeId)
                    .setTypeDefinition(Identifiers.BaseDataVariableType)
                    .build();
            node.setValue(new DataValue(variant));
            getNodeManager().addNode(node);
            folderNode.addOrganizes(node);
        }
    }

    private void startBogusEventNotifier() {
        // Set the EventNotifier bit on Server Node for Events.
        UaNode serverNode = getServer()
                .getAddressSpaceManager()
                .getManagedNode(Identifiers.Server)
                .orElse(null);

        if (serverNode instanceof ServerTypeNode) {
            ((ServerTypeNode) serverNode).setEventNotifier(ubyte(1));

            // Post a bogus Event every couple seconds
            eventThread = new Thread(() -> {
                while (keepPostingEvents) {
                    try {
                        BaseEventTypeNode eventNode = getServer().getEventFactory().createEvent(
                                newNodeId(UUID.randomUUID()),
                                Identifiers.BaseEventType
                        );
                        eventNode.setBrowseName(new QualifiedName(1, "foo"));
                        eventNode.setDisplayName(LocalizedText.english("foo"));
                        eventNode.setEventId(ByteString.of(new byte[]{0, 1, 2, 3}));
                        eventNode.setEventType(Identifiers.BaseEventType);
                        eventNode.setSourceNode(serverNode.getNodeId());
                        eventNode.setSourceName(serverNode.getDisplayName().getText());
                        eventNode.setTime(DateTime.now());
                        eventNode.setReceiveTime(DateTime.NULL_VALUE);
                        eventNode.setMessage(LocalizedText.english("event message!"));
                        eventNode.setSeverity(ushort(2));
                        //noinspection UnstableApiUsage
                        getServer().getEventBus().post(eventNode);
                        eventNode.delete();
                    } catch (Throwable e) {
                        logger.error("Error creating EventNode: {}", e.getMessage(), e);
                    }

                    try {
                        //noinspection BusyWait
                        Thread.sleep(2_000);
                    } catch (InterruptedException ignored) {
                        // ignored
                    }
                }
            }, "bogus-event-poster");
            eventThread.start();
        }
    }


    @Override
    public void onDataItemsCreated(List<DataItem> dataItems) {
        subscriptionModel.onDataItemsCreated(dataItems);
    }

    @Override
    public void onDataItemsModified(List<DataItem> dataItems) {
        subscriptionModel.onDataItemsModified(dataItems);
    }

    @Override
    public void onDataItemsDeleted(List<DataItem> dataItems) {
        subscriptionModel.onDataItemsDeleted(dataItems);
    }

    @Override
    public void onMonitoringModeChanged(List<MonitoredItem> monitoredItems) {
        subscriptionModel.onMonitoringModeChanged(monitoredItems);
    }

}

创建OpcServerTest类,进行使用测试:


import org.eclipse.milo.opcua.sdk.server.OpcUaServer;
import org.eclipse.milo.opcua.sdk.server.api.config.OpcUaServerConfig;
import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
import org.eclipse.milo.opcua.stack.server.EndpointConfiguration;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Set;

/**
 * @author tarzaqn
 */
public class OpcServerTest {

    public static void main(String[] args) {
        OpcUaServer server=startServer(12688);
        TestNamespace namespace=new TestNamespace(server);
        Set<String> keys=getAllKeys("ehc.txt");
        namespace.addNodes(keys);
        namespace.startup();
    }


    /**
     * 方法描述: 创建opcUaServer
     *
     * @param port 端口
     * @return {@link OpcUaServer}
     * @throws
     */
    private static OpcUaServer startServer(int port){
        Set<EndpointConfiguration> endpointConfigurations=new HashSet<>();
        EndpointConfiguration endpointConfiguration=EndpointConfiguration.newBuilder().setBindPort(port).build();
        endpointConfigurations.add(endpointConfiguration);
        System.out.println(endpointConfiguration.getEndpointUrl());
        OpcUaServerConfig serverConfig = OpcUaServerConfig.builder()
                .setApplicationName(LocalizedText.english("Server Application"))
                .setApplicationUri("urn:eclipse:milo:examples:server")
                .setProductUri("urn:eclipse:milo:examples:server")
                .setEndpoints(endpointConfigurations)
                .build();
        OpcUaServer server = new OpcUaServer(serverConfig);
        server.startup();
        return server;
    }

    private static Set<String> getAllKeys(String fileName){
        Set<String> keys=new HashSet<>(50);
        try {
            InputStream is=  OpcServerTest.class.getResourceAsStream("/points/"+fileName);
            InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8);
            BufferedReader in = new BufferedReader(reader);
            String line;
            while ((line = in.readLine()) != null) {
                keys.add(line);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return  keys;
    }
}

我把需要创建的点位都放在java maven项目的resources文件下的ehc.txt文件中,通过getAllKeys方法拿到所有的需要创建的点位集合。
在这里插入图片描述
在这里插入图片描述
启动main方法,控制台输出如下:
在这里插入图片描述
在这里插入图片描述

我们可以看到控制台输出的 opc.tcp://localhost:12688 就是我们使用java启动opc ua server的连接地址,上面的启动server的代码中,没有设置用户,密码登录,我们在使用opc ua 客户端的时候,可以使用匿名登录访问。

OPC UA 客户端连接测试

使用java 代码连接我们刚才创建的 Opc Ua server,尝试读取我们创建的节点名称,代码如下:


import org.eclipse.milo.opcua.sdk.client.OpcUaClient;

/**
 * @author tarzan
 */
public class OpcUaClientTest {
    public static void main(String[] args) throws Exception {
        String endPointUrl="opc.tcp://localhost:12688";
        OpcUaClient client=OpcUaUtil.createClient(endPointUrl,null,null);
        OpcUaUtil.browse(null,client);
        Thread.sleep(Integer.MAX_VALUE);
    }
}

经测试,连接读取节点名称成功,控制台输出如下:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/712408.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

量水堰计的作用是什么有什么特点

水库大坝安全监测中&#xff0c;渗流监测是一个重要的组成部分。量水堰计作为一种测量流量的仪器&#xff0c;在渗流监测中起着至关重要的作用。其工作原理是&#xff0c;在进水口处设置量水堰&#xff0c;量水堰上安装有磁致伸缩液位传感器&#xff0c;可以实时记录通过量水堰…

【ARM】-IRQ 和 FIQ 异常中断处理程序的返回

文章目录 处理流程示例代码实现 处理流程 通常处理器执行完当前指令后&#xff0c;查询 IRQ 中断引脚及 FIQ 中断引脚&#xff0c;并且查看系统是否允许 IRQ 中断及 FIQ中断。 如果有中断引脚有效&#xff0c;并且系统允许该中断产生&#xff0c;处理器将产生 IRQ 异常中断或 …

自动化测试selenium篇(一)

一、自动化测试 1.1自动化测试介绍 自动化测试就相当于将人工测试手段进行转换&#xff0c;让代码去执行 1.2 自动化测试的分类 单元测试、接口测试、UI自动化测试 二、selenium介绍 2.1 selenium是什么 Selenium是web应用中基于UI的自动化测试框架 2.2 selenium的特点…

Redis的主从复制模式

主从复制就是Redis集群的策略 配从库不配主库&#xff1a;小弟可以选择谁是大哥&#xff0c;但大哥没有权利去选择小弟 读写分离&#xff1a;主机写&#xff0c;从机读 集群配置和搭建 1.将虚拟机中的CentOS(192.168.40.100)重新复制两份 2.将新复制的静态ip改一下,192.168.40.…

https 证书到期,手动更新

记录一下&#xff0c;因为 3 个月后还会用到的。。 1. 验证域名所有权&#xff08;在某个目录下放置指定文件验证&#xff09; http://172.245.xxx.xxx/.well-known/pki-validation/3C42D2093F4B0237224A95xxxxxxx.txt 2. 替换下载下来的证书文件 3. 检查&#x1f9d0; 喜欢或…

JMeter正则表达式提取器实践

目录 前言&#xff1a; apply to&#xff1a; 要检查的响应字段&#xff1a;样本数据源 引用名称&#xff1a; 正则表达式&#xff1a; 模板&#xff1a; 匹配数字&#xff1a; 缺省值&#xff1a; 前言&#xff1a; JMeter是一个流行的性能测试工具&#xff0c;它提供…

《Effective C++》 全书内容提炼总结

个人博客地址: https://cxx001.gitee.io 本文阅读说明 孔子云&#xff1a;“取乎其上&#xff0c;得乎其中&#xff1b;取乎其中&#xff0c;得乎其下&#xff1b;取乎其下&#xff0c;则无所得矣”。 对于读书求知而言&#xff0c;这句古训教我们去读好书&#xff0c;最好是…

【基础算法】穷举法

穷举法Exhaustive method是使用最广泛、设计最简单&#xff0c;同时最耗时的算法&#xff0c;也被称为暴力法、蛮力法Brute force method。 两数之和 给定一个整数数组array和一个目标值target&#xff0c;请在数组中找出和为目标值target的两个整数&#xff0c;并输出它们在数…

【PHP语言】医院安全(不良)事件报告系统源码

一、系统概述&#xff1a; 医院安全&#xff08;不良&#xff09;事件报告系统是一种用于医院管理和监管的工具&#xff0c;旨在帮助医疗机构识别、跟踪、分析和解决各种医疗安全事件&#xff0c;提高医疗质量和患者安全。 医院安全&#xff08;不良&#xff09;事件报告系统采…

Linux:nginx基础搭建(源码包)

安装基础环境 准备一个nginx源码包 yum -y install pcre-devel zlib-devel gcc-c useradd -M -s /sbin/nologin nginx tar xf nginx-1.12.0.tar.gz -C /usr/src/ cd /usr/src/nginx-1.12.0/ ./configure --prefix/usr/local/nginx --usernginx --groupnginx --with-http_st…

【unity】RenderFeature的应用(生成水平面的网格线)

【unity】RenderFeature的应用&#xff08;生成水平面的网格线&#xff09; 在URP里RenderFeature是用于后处理效果上的&#xff0c;也还可以实现一些特殊的效果&#xff0c;比如生成网格线。我们可以使用 CommandBuffer来创建地面网格&#xff0c;这样的话可以通过调整 Comman…

【NOSQL】redis哨兵模式、集群搭建

目录 一、redis高可用一、Redis主从复制1.1主从复制的作用1.2主从复制流程 二、搭建Rdeis主从复制2.1安装redis2.1.1环境准备2.1.2安装redis2.1.3设置环境变量2.1.4定义systemd服务管理脚本 2.2修改 Redis 配置文件&#xff08;Master节点操作2.3修改 Redis 配置文件&#xff0…

【SpringMVC】| 异常处理器、基于全注解配置SpringMVC

目录 异常处理器 1. 基于配置的异常处理 2. 基于注解的异常处理&#xff08;用类代替xml配置&#xff09; 基于全注解配置SpringMVC 1. 创建初始化类&#xff0c;代替web.xml 2. 创建SpringConfig配置类&#xff0c;代替spring的配置文件 3. 创建WebConfig配置类&#…

哈夫曼树的原理及构造方法

目录 1. 什么是哈夫曼树 2. 为什么有哈夫曼树 3. 哈夫曼树的原理 3.1 哈夫曼树的构造方法 3.2 哈夫曼解码 3.3 几种定义 4. 哈夫曼二叉树的特点 5. 关于哈夫曼树的代码 1. 什么是哈夫曼树 哈夫曼树解决的是编码问题&#xff0c;给定N个权值作为N个叶子结点&#xff0c;构…

Springboot tomcat bean 默认作用域 singleton 情况下模拟线程不安全情况 设置多例方式 prototype

目录 写一个控制层的类 验证方法 ​编辑 分别执行如下请求&#xff0c;先执行等待时间久的 日志结果 结论 配置多例模式 配置文件application.properties 类加注解 配置类方式 写一个控制层的类 package com.example.ctr;import lombok.extern.slf4j.Slf4j; import …

【ISE】PlanAhead report_timing报VIOLATED问题

这里写自定义目录标题 背景ISE PR结果PlanAhead结果使用ISE twx使用PlanAhead综合、实现 问题 分析Timer Settings 结论&#xff1f; 背景 最近调试一个老型号FPGA&#xff0c;时序问题分析&#xff0c;方便以后参考。 ISE PR结果 在下图可以看到All Constraints Met&#x…

iOS iPadOS safari 独立Web应用屏幕旋转的时候 onresize window.innerHeight 数值不对。

iOS iPadOS safari 独立Web应用屏幕旋转的时候 onresize window.innerHeight 数值不对 一、问题描述 我有一个日记应用&#xff0c;是可以作为独立 Web 应用运行的那种&#xff0c;但在旋转屏幕的时候获取到的 window.innerHeight 和 window.innerWidth 就不对了&#xff0c;…

【C#】使用System.Data.SqlClient 进行简单批量操作

在实际项目开发中&#xff0c;可能会在定时任务里进行批量添加的操作&#xff0c;或者需要写一些小工具进行批量添加测试。 此篇文章就是使用System.Data.SqlClient 进行简单批量操作。 目录 1、批量插入数据1.1、示例代码1.2、列映射1.3、是否需要映射列 2、批量更新数据3、链…

ZigBee组网-基于协议栈的UART实验(实现收发)(保姆级)

目录 基于协议栈的UART实验 前言 协议栈中的TI自带UART的使用实验 UART配置基本步骤 串口初始化 串口发送 串口接收回显 实验效果 拓展 移植我们自己UART串口 移植配置过程 实验效果 基于协议栈的UART实验 前言 与之前的Zigbee裸机实验不同&#xff0c;我们既可以…

神奇的MATLAB解密工具,让你轻松解密.p文件!

大家都知道&#xff0c;MATLAB是一款功能强大的数学软件&#xff0c;但是在进行代码保护和共享方面&#xff0c;却存在一些困难。这时候&#xff0c;一款优秀的MATLAB解密工具就显得尤为重要。它可以帮助我们解决诸多问题&#xff0c;比如.p文件解密、p文件转m代码等。接下来&a…