IIoT 边缘开发——使用 OPC UA 协议
编者注:工业物联网 (IIoT) 承诺提供对工业运营的深入洞察,并提高互联机器和系统的效率。大规模 IIoT 应用程序依赖分层架构从各种传感器收集数据,将数据可靠、安全地移动到云端,并执行提供洞察力和效率所需的分析。在工业互联网应用程序开发中,作者详细介绍了 IIoT 架构,并讨论了满足与这些系统相关的广泛要求的方法。
改编自工业互联网应用开发,作者:Alena Traukina、Jayant Thomas、Prashant Tyagi、Kishore Reddipalli。
第 3 章 IIoT 边缘开发(续)
作者:Alena Traukina、Jayant Thomas、Prashant Tyagi、Kishore Reddipalli
工业 M2M 协议 – OPC UA
在本节中,我们将尝试构建一个简单的 IoT 应用程序,用于使用 Raspberry Pi 集线器和 OPC UA 协议将数据从传感器模拟器模块发送到接收器设备(PC 或云):
从传感器模拟器到接收器设备的数据流
OPC UA 协议类似于 Modbus,但适用于更多的数据类型,并且没有严重的限制,同时提供安全、压缩和低延迟。
该协议由 OPC 基金会开发,作为工业机器对机器通信协议。 OPC UA(统一架构)是开放平台通信的改进版本 (OPC ) 协议,主要变化之一是新协议可以免费使用,没有任何限制。
在下表中,您可以找到该协议的更详细说明,以了解它是否适合您的需求:
键 价值 开源是OSI层传输或应用数据类型整数、浮点数、字符串、布尔值、日期、时间等限制不适用于复杂架构可能的操作读/写/监控/查询变量LatencyLowUsageIIoTSecurityYesCompression是表 5:OPC UA 协议规范
为了构建应用程序,我们需要以下内容:
- 所需软件
- Node.js 6+ (https:/ / nodejs. org/ en/ download/ )
- PostgreSQL (https:/ / www. postgresql. org/ download/ )
- Cloud Foundry CLI (https:/ / github. com/ cloudfoundry/cli#downloads)
- 请求(https:// / www. npmjs. com/ package/ request)
- NodeOPCUA (https:// / www. npmjs. com/ package/ node- opcua)
- 异步(https:// / www. npmjs. com/ package/ async)
- Docker (https:/ / docs. docker. com/ engine/ installation/ )
- 所需硬件
- 树莓派 3(B 型)
- 一个电源适配器 (2A/5V)
- 一张 microSD 卡 (8 GB+) 和一个 SD 适配器
- 用于有线网络连接的以太网电缆
准备一张 SD 卡
要准备 SD 卡,请按照所述的操作顺序进行操作:
下载最新的 Raspbian LITE 镜像(可在 https://raspberrypi.org/downloads/raspbian/ 获得)。
将您的 SD 卡连接到计算机并使用 Etcher (https://io/) 将 Raspbian .img 文件刷入 SD卡。
启用 SSH:
<字体样式="字体大小:13px;" face ="Courier New"> cd /Volumes/boot touch ssh
要启用 Wi-Fi,请使用以下内容创建 conf:
<字体样式="字体大小:13px;" face ="Courier New"> network={
ssid=”YOUR_SSID”
psk=”YOUR_WIFI_PASSWORD”
}
创建/home/pi/hub
使用以下内容创建 /home/pi/hub/package.json 文件:
<字体样式="字体大小:13px;" face ="Courier New"> {
“name”:“hub”,
“version”:“1.0.0”,
“description”:“”,
“ main”:“index.js”,“scripts”:{
“start”:“node index.js”,
“test”:“echo“错误:未指定测试”&&退出1“
},
“作者”:“”,
“许可证”:“ISC”,“依赖项”:{
“异步”:“^2.4.0”,
“node-opcua”:“0.0.64”,
“请求”:“^2.81.0”
}
}
使用以下内容创建/home/pi/hub/index.js 文件,替换
<字体样式="字体大小:13px;" face ="Courier New"> REMOTE-SERVER-ADDRESS.com 和 REMOTE-SENSOR-ADDRESS 具有实际值:
var opcua =require(“node-opcua”); var async =require(“async”);
var request =require(“request”);
var session, subscription;
var client =new opcua.OPCUAClient();
var 传感器 =“opc.tcp://REMOTE-SENSOR-ADDRESS:4334/UA/resourcePath”;
var 接收器 =“http://REMOTE-SERVER-ADDRESS.com:8080”;
async.series( [
// 建立连接函数 (cb) {
client.connect(sensor, function (err) {
if (err) {
console.log (“连接到”+传感器+“失败”);
} else {
console.log(“连接成功”);
}
cb(err);
});
},
// 启动会话函数 (cb) {
client.createSession(function (err, res) {
if (!err) session =res;
cb(err);
});
},
// 读取值
function (cb) {
session.readVariableValue(“ ns=1;s=Variable1”, function (err, dataValue) {
if (!err) console.log(“Variable1 =“, dataValue.value.value);
cb(err);
});
},
// 写入值
function (cb) {
session.writeSingleNode(“ns=1;s=Variable1”, new opcua.Variant({
dataType:opcua.DataType.Double, value:100
}), function (err) {
cb(err);
});
},
// 订阅更改
function (cb) {
subscription =new opcua.ClientSubscription(session, {
maxNotificationsPerPublish:5,
priority:5,
publishingEnabled:true,
requestedLifetimeCount:5,
requestedMaxKeepAliveCount:3,
requestedPublishingInterval:500,
});
subscription.on(“started”, function () {
console.log("subscription id:",
subscription.subscriptionId);
}).on("terminated", function () {
cb();
});
setTimeout(function () {
subscription.terminate();
}, 5000);
// 安装被监控的项目
var monitor =subscription .monitor({
attributeId:opcua. AttributeIds.Value,
nodeId:opcua.resolveNodeId(“ns=1;s=Variable1”),
},
{
discardOldest:true,
采样间隔: 50,
queueSize:5,
},
opcua.read_service.TimestampsToReturn.Both
);
monitor.on(“changed”, function (dataValue) {
console.log("Variable1 =", dataValue.value.value);
// 发送到接收器
var data ={
device:“sensor1”,
时间戳:Date.now(),
变量1:dataValue.value.value
};
request.post({url:receiver, form:data}, function (err) {
if (err) console.log(“Failed to send ” +
JSON.stringify(data) + ” to ” + receiver);
});
});
},
// 关闭会话
function (cb) {
session.close(function (err) {
if (err) console.log(“Failed to闭会”); cb();
});
}
],
function (err) { if (err) {
console.log(“Failed with error:” , err);
} else {
console.log(“成功完成”);
}
client.disconnect(function () {
});
}
);
使用以下内容创建 /home/pi/hub/Dockerfile 文件:
<字体样式="字体大小:13px;" face ="Courier New"> FROM hypriot/rpi-node:boron-onbuild
创建 /home/pi/sensor
使用以下内容创建 /home/pi/sensor/package.json 文件:
<字体样式="字体大小:13px;" face ="Courier New"> {
“name”:“sensor”,
“version”:“1.0.0”,
“description”:“”,
“ main”:“index.js”,
“scripts”:{
“start”:“node index.js”,
“test”:“echo”错误:未指定测试” &&exit 1″
},
“作者”:“”,
“许可证”:“ISC”,
“依赖项”:{
“node-opcua ”:“0.0.64”
}
}
使用以下内容创建 /home/pi/sensor/index.js 文件:
<字体样式="字体大小:13px;" face="Courier New"> var opcua =require(“node-opcua”);
var min =1;
var max =100;
var host =new opcua.OPCUAServer({ buildInfo:{
buildDate:new Date(2018, 8, 8),
buildNumber:“1234”,
productName:“productName”,
},
port :4334,
resourcePath:“UA/resourcePath”,
});
host.initialize(function () {
var space =host.engine.addressSpace;
var componentOf =space.addObject({
browseName:“browseName”,
organizedBy:space.rootFolder.objects,
});
var variable1 =0;
// 生成新值
setInterval(function () {
variable1 =Math.floor(max – Math.random() * (max – min));
}, 500);
space.addVariable({
browseName:“browseName”,
componentOf:componentOf,
dataType:“Double”,
nodeId:“ns=1;s=Variable1”, // 一个字符串 nodeID
value:{
get:function () {
return new opcu a.Variant({dataType:opcua.DataType.Double, value:variable1});
},
set:function (variant) {
variable1 =parseFloat(variant.value);
return opcua.StatusCodes.Good;
}
}
});
host.start(function () {
var endpoint =
host.endpoints[0].endpointDescriptions()[0].endpointUrl; console.log("Endpoint:", endpoint);
});
});
在开头配置最小值和最大值
<字体样式="字体大小:13px;" face ="Courier New"> /home/pi/sensor/index.js 文件。
使用以下内容创建 /home/pi/sensor/Dockerfile 文件:
<字体样式="字体大小:13px;" face ="Courier New"> FROM hypriot/rpi-node:boron-onbuild
物联网技术