Node-OPCUA 入门(1)-创建一个简单的OPC UA服务器

1.0 node-opcua简介

node-opcua是一个完全用Typescript为NodeJS编写的OPC UA栈的实现。不依赖于外部第三方库。它依赖于一些仅在服务器端可用的javascript模块,如fs (FileSystem)或net(Socket)。

2.0 创建一个简单的node-opcua 服务器

在这个示例中,我们创建一个OPC UA服务器,它公开3个读/写变量,服务器将在名为"我的设备"的新对象下公开这些变量。

Plain 复制代码
    + 跟文件夹
        + 项目
            + 我的设备
                + 变量1
                + 变量2
2.1 首先创建一个项目:
bash 复制代码
$ mkdir myserver
$ cd myserver
$ npm init                      # 初始化项目创建一个package.json文件
$ npm install node-opcua --save # 添加依赖node-opcua模块
2.2 编辑项目

在项目目录项创建并编辑一个sample_server.js文件

2.2.1 通过'require'语句导入node-opcua模块:

javascript 复制代码
const { OPCUAServer, Variant, DataType, StatusCodes} = require("node-opcua");

2.2.2 服务器实例化

创建一个OPCUAServer实例。可以将选项传递给OPCUAServer来定制行为。对于简单的服务器,您只需要指定一个TCP端口。

javascript 复制代码
 // 创建一个OPCUAServer
    const server = new OPCUAServer({
        port: 4334, // 服务器套接字的监听端口
        resourcePath: "/UA/MyLittleServer", //此路径将添加到端点资源名称中
        buildInfo: {
            productName: "我的示例服务器1",
            buildNumber: "7658",
            buildDate: new Date(2014, 5, 2)
        }
    });

资源路径将用于构造服务器的端点统一资源标识符(url)。在我们的例子中,服务器的端点urn将是:

javascript 复制代码
opc.tcp://<hostname>:4334/UA/MyLittleServer

hostname替换为您的计算机名称或完全合格的域名。 客户端必须使用这个URN连接到服务器。

设置服务器信息,可以在这个阶段设置其他信息,例如:

javascript 复制代码
 buildInfo: {
            productName: "我的示例服务器1",
            buildNumber: "7658",
            buildDate: new Date(2022, 9, 8)
        }

2.2.3 服务器初始化

一旦创建,服务器将被初始化。在初始化期间,服务器将加载其默认节点集,并准备所有标准OPC UA变量的绑定。initialize方法是一个异步操作,需要一个"回调"函数,该函数将在初始化过程完成时执行。

javascript 复制代码
await server.initialize();
    console.log("已初始化");

2.2.4 初始化后

让我们创建一个函数,它将使用一些我们想要公开的变量来扩展服务器默认地址空间。这个函数将在初始化回调函数中调用。

addressSpace用于定制我们的服务器将向外部世界公开的对象模型。

javascript 复制代码
  const addressSpace = server.engine.addressSpace;
  const namespace = addressSpace.getOwnNamespace();

2.2.5 在objects文件夹中添加一个新对象

javascript 复制代码
    // 声明一个新对象
    const device = namespace.addObject({
        organizedBy: addressSpace.rootFolder.objects,
        browseName: "我的设备"
    });

2.2.6 添加一些变量

在服务器名称空间中添加变量1, 模拟"变量1"每500毫秒改变一次。

javascript 复制代码
    // 将"变量1"添加到新创建的文件夹"我的设备"中

    const variable1 = namespace.addVariable({
        componentOf: device,
        browseName: "变量1",
        dataType: "Double",
        minimumSamplingInterval: 100,
    });
    let counter = 0;
    // 模拟"变量1"每500毫秒改变一次
    const timerId = setInterval(() => { counter += 1; variable1.setValueFromSource({ dataType: DataType.Double, value: counter }) }, 500);

    addressSpace.registerShutdownTask(() => { clearInterval(timerId); });

注意,上面的例子,我们没有为变量指定NodeId。服务器会自动为我们分配一个新的nodeId。

添加变量2,这个示例我们指定节点ID:

javascript 复制代码
  // 在新创建的文件夹我的设备中添加一个名为"变量2"的变量

    namespace.addVariable({

        componentOf: device,
        nodeId: "ns=1;b=1020FFAA", // 命名空间中的节点ID
        browseName: "变量2",
        dataType: "Double",
        minimumSamplingInterval: 0, // 这个变量受事件驱动
    });

下面再创建一个变量,来公开运行中的机器上的可用内存百分比。 我们写一个函数来计算这个值。

javascript 复制代码
const os = require("os");
    /**
     * 返回运行中的机器上的空闲内存百分比
     * @return {double}
     */
    function available_memory() {
        // var value = process.memoryUsage().heapUsed / 1000000;
        const percentageMemUsed = os.freemem() / os.totalmem() * 100.0;
        return percentageMemUsed;
    }
    namespace.addVariable({

        componentOf: device,

        nodeId: "s=free_memory", // 一个字符串节点ID
        browseName: "空闲内存",
        dataType: "Double",
        minimumSamplingInterval: 1234,
        value: {
            get: () => new Variant({ dataType: DataType.Double, value: available_memory() })
        }
    });

2.2.7 启动服务器 创建并初始化服务器之后,我们使用start异步方法让服务器初始化它的所有端点并开始监听客户端。显示端点url

javascript 复制代码
  await server.start();
    console.log("服务器正在监听 ... ( 按 CTRL+C 停止)");
    console.log("端口 ", server.endpoints[0].port);
    console.log(" 主服务器端点url是 ", server.getEndpointUrl());

2.2.8 下面,是完整sample_server.js文件

javascript 复制代码
const os = require("os");
const { OPCUAServer, Variant, DataType, StatusCodes } = require("node-opcua");


(async () => {

    // 创建一个OPCUAServer
    const server = new OPCUAServer({
        port: 4334, // 服务器套接字的监听端口
        resourcePath: "/UA/MyLittleServer", //此路径将添加到端点资源名称中
        buildInfo: {
            productName: "我的示例服务器1",
            buildNumber: "7658",
            buildDate: new Date(2022, 9, 8)
        }
    });
    await server.initialize();
    console.log("已初始化");

    const addressSpace = server.engine.addressSpace;
    const namespace = addressSpace.getOwnNamespace();

    // 声明一个新对象
    const device = namespace.addObject({
        organizedBy: addressSpace.rootFolder.objects,
        browseName: "我的设备"
    });

    // 添加一些变量
    // 将"变量1"添加到新创建的文件夹"我的设备"中

    const variable1 = namespace.addVariable({
        componentOf: device,
        browseName: "变量1",
        dataType: "Double",
        minimumSamplingInterval: 100,
    });

    let counter = 0;
    // 模拟"变量1"每500毫秒改变一次
    const timerId = setInterval(() => { counter += 1; variable1.setValueFromSource({ dataType: DataType.Double, value: counter }) }, 500);

    addressSpace.registerShutdownTask(() => { clearInterval(timerId); });

    // 在新创建的文件夹我的设备中添加一个名为"变量2"的变量

    namespace.addVariable({

        componentOf: device,
        nodeId: "ns=1;b=1020FFAA", // 命名空间中的节点ID
        browseName: "变量2",
        dataType: "Double",
        minimumSamplingInterval: 0, // 这个变量受事件驱动
    });

    /**
     * 返回运行中的机器上的空闲内存百分比
     * @return {double}
     */
    function available_memory() {
        // var value = process.memoryUsage().heapUsed / 1000000;
        const percentageMemUsed = os.freemem() / os.totalmem() * 100.0;
        return percentageMemUsed;
    }
    namespace.addVariable({

        componentOf: device,

        nodeId: "s=free_memory", // 一个字符串节点ID
        browseName: "空闲内存",
        dataType: "Double",
        minimumSamplingInterval: 1234,
        value: {
            get: () => new Variant({ dataType: DataType.Double, value: available_memory() })
        }
    });

    await server.start();
    console.log("服务器正在监听 ... ( 按 CTRL+C 停止)");
    console.log("端口 ", server.endpoints[0].port);
    console.log(" 主服务器端点url是 ", server.getEndpointUrl());

    process.once("SIGINT", async () => {
        console.log("正在关闭");
        await server.shutdown();
    });

})();

2.2.9 执行并测试服务器

javascript 复制代码
$ node sample_server.js

3.0 使用UaExpert测试服务

3.1 打开UaExpert,右键选择"Project|Server"在上下文菜单点击"Add..."添加

3.2 打开添加服务器窗口,添加服务器

3.3 添加服务器URL地址,本例中为opc.tcp://DESKTOP-MGFMSQP:4334/UA/MyLittleServer 3.4 服务器被找到,选择加密方式,确认添加 3.5 在Servers文件夹下,选择新添加的服务器NodeOPCUA,右键上下文菜单点击Connect连接服务器。 3.6 在下面的Adddress Space(地址空间),列出了我的设备和变量 3.7 把变量拖入Data Access View(数据访问视图),可以看到变量值的变化。

相关推荐
如果你好2 小时前
一文搞懂事件冒泡与阻止方法:event.stopPropagation() 实战指南
前端·javascript
www_stdio2 小时前
我的猫终于打上冰球了——一个 Vue3 + Coze AI 项目的完整落地手记
javascript·vue.js·coze
MediaTea2 小时前
思考与练习(第十章 文件与数据格式化)
java·linux·服务器·前端·javascript
江公望2 小时前
Vue3的 nextTick API 5分钟讲清楚
前端·javascript·vue.js
咸鱼加辣3 小时前
【python面试】Python 的 lambda
javascript·python·算法
晓得迷路了3 小时前
栗子前端技术周刊第 111 期 - Next.js 16.1、pnpm 10.26、Bun 1.3.5...
前端·javascript·bun
shangxianjiao3 小时前
vue前端项目介绍项目结构
前端·javascript·vue.js
asdfg12589634 小时前
数组去重(JS)
java·前端·javascript
鹏多多4 小时前
前端大数字精度解决:big.js的教程和原理解析
前端·javascript·vue.js