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 小时前
你所不知道的前端知识,html篇(更新中)
前端·javascript·面试
sophie旭3 小时前
内存泄露排查之我的微感受
前端·javascript·性能优化
Hilaku4 小时前
我用 Gemini 3 Pro 手搓了一个并发邮件群发神器(附源码)
前端·javascript·github
全栈前端老曹4 小时前
【包管理】npm init 项目名后底层发生了什么的完整逻辑
前端·javascript·npm·node.js·json·包管理·底层原理
HHHHHY4 小时前
mathjs简单实现一个数学计算公式及校验组件
前端·javascript·vue.js
iReachers4 小时前
HTML打包APK(安卓APP)中下载功能常见问题和详细介绍
前端·javascript·html·html打包apk·网页打包app·下载功能
愈努力俞幸运4 小时前
vue3 demo教程(Vue Devtools)
前端·javascript·vue.js
持续前行4 小时前
在 Vue3 中使用 LogicFlow 更新节点名称
前端·javascript·vue.js
Anita_Sun4 小时前
Underscore.js 整体设计思路与架构分析
前端·javascript