node-red-contrib-opcua-multiple
Version:
multiple client <-> multiple server to read based on node-opcua library
399 lines (375 loc) • 16.1 kB
JavaScript
module.exports = function (RED) {
"use strict";
let chalk = require("chalk");
let opcua = require('node-opcua');
// let opcuaBasics = require('./opcua-basics');
// let crypto_utils = opcua.crypto_utils;
// let fileTransfer = require("node-opcua-file-transfer");
let async = require("async");
let fs = require("fs");
let os = require("os");
let cloneDeep = require('lodash.clonedeep');
let DataType = opcua.DataType;
let AttributeIds = opcua.AttributeIds;
let TimestampsToReturn = opcua.TimestampsToReturn;
// const { createClientCertificateManager } = require("./utils");
function opcuaZclient(n) {
RED.nodes.createNode(this, n);
this.name = n.name;
let node = this;
//客户端存储
let zclientsTot = {};
let zclients = {};
let zsessions = {};
let zclientRetry = {};
let zclientFail = {};
let zlog = {
error: function (msg) {
console.log("zlog:", msg);
node.error(chalk.red(msg));
},
warn: function (msg) {
console.log("zlog:", msg);
node.warn(chalk.yellow(msg));
},
log: function (msg) {
console.log("zlog:", msg);
node.log(chalk.blue(msg));
},
statusShow: function (msg) {
let text = `
客户端数量: ${Object.keys(zclientsTot).length}
会话数量: ${Object.keys(zsessions).length}
尝试连接: ${Object.keys(zclientRetry).length}
失败连接: ${Object.keys(zclientFail).length}
`;
node.status({ fill: "green", shape: "dot", text: text });
}
}
function zclientOutput(o1 = null, o2 = null, o3 = null) {
node.send([o1, o2, o3]);
}
function createClient(msg) {
let defaultOptions = {
endpointUrl: "opc.tcp://0.0.0.0:4840",
// securityMode: opcua.MessageSecurityMode.None,
// securityPolicy: opcua.MessageSecurityMode.None,
endpointMustExist: false,
defaultSecureTokenLifetime: 40000 * 5,
connectionStrategy: {
maxRetry: 10512000, // Limited to max 10 ~5min // 10512000, // 10 years should be enough. No infinite parameter for backoff.
initialDelay: 5000, // 5s
maxDelay: 30 * 1000 // 30s
},
clientName: "clientName", // Fix for #664 sessionName
keepSessionAlive: true,
requestedSessionTimeout: 60000 * 5, // 5min, default 1min
automaticallyAcceptUnknownCertificate: true,
// transportSettings: transportSettings // Some
};
let opts = Object.assign({}, defaultOptions, msg.optuaConfig);
if (opts.endpointUrl.indexOf("opc.tcp://0.0.0.0") === 0) {
zlog.error("Error: endpointUrl is not set");
return;
}
if(zclientsTot[opts.endpointUrl] == 0){
zlog.warn(`warn: ${opts.endpointUrl} client 正在创建,请稍等。`);
return;
}
if(zclientsTot[opts.endpointUrl] == undefined){
zclientsTot[opts.endpointUrl] = 0;
zlog.log(`=========正在初始化`);
}
// if (msg.retryFail && opts.connectionStrategy) {
// opts.connectionStrategy.maxRetry = 0;
// }
console.log("===zclients====:", zclients[opts.endpointUrl]);
if (zclients[opts.endpointUrl]) {
// zlog.warn(`warn: opts.endpointUrl } client already exists`);
if (zsessions[opts.endpointUrl]) {
readMultiple(opts.endpointUrl, msg);
return;
} else {
// removeClient(opts);
//TODO reConnect
}
}else{
_createClient(opts, msg);
}
zlog.statusShow();
}
function _createClient(opts, msg) {
let client = null;
try {
client = opcua.OPCUAClient.create(opts);
zclients[opts.endpointUrl] = client;
} catch (error) {
zlog.error(`Error creating OPCUA client[${opts.endpointUrl}]: ` + error.message);
addToFailList(opts.endpointUrl);
}
if (client) {
addToList(opts.endpointUrl);
initClientEvent(client, opts, msg);
zlog.log(`${opts.endpointUrl} Client created`);
connectClient(client, opts, msg);
}
return client;
}
function addToFailList(endpointUrl) {
delete zclientRetry[endpointUrl];
delete zclients[endpointUrl];
zclientFail[endpointUrl] = endpointUrl;
zlog.statusShow();
}
function addToRetryList(endpointUrl) {
delete zclientFail[endpointUrl];
delete zclients[endpointUrl];
closeSession(zsessions[endpointUrl], endpointUrl);
zclientRetry[endpointUrl] = endpointUrl;
zlog.statusShow();
}
function addToList(endpointUrl) {
delete zclientFail[endpointUrl];
delete zclientRetry[endpointUrl];
zclients[endpointUrl] = endpointUrl;
zlog.statusShow();
}
function connectClient(client, opts, msg) {
client.connect(opts.endpointUrl, function (err) {
if (err) {
zlog.error(`Error connectClient [${opts.endpointUrl}]: ` + err.message);
addToFailList(opts.endpointUrl);
return;
}
createSession(client, opts, msg);
});
}
function createSession(client, opts, msg) {
const userIdentity = Object.assign(
{ type: opcua.UserTokenType.Anonymous },
msg.userIdentity || {}
);
//创建会话
client.createSession(userIdentity, function (err, session) {
if (err) {
zlog.error(`Error creating OPC UA session[${opts.endpointUrl}]: ` + err);
return;
}
initSessionEvent(session, opts, msg);
zsessions[opts.endpointUrl] = session;
zlog.log(`${opts.endpointUrl} Session created`);
// Read multiple, payload contains all nodeIds that will be read
readMultiple(opts.endpointUrl, msg);
delete zclientRetry[opts.endpointUrl];
delete zclientFail[opts.endpointUrl];
zlog.statusShow();
});
}
function readMultiple(endpointUrl, msg) {
zlog.log("Reading multiple nodes");
if (!msg.nodeIds || msg.nodeIds.length == 0) {
zlog.error("Error: No nodeIds found in msg");
return;
}
let nodeIds = msg.nodeIds;
let session = zsessions[endpointUrl];
if (!session) {
zlog.error("Error: No session found for endpointUrl: " + endpointUrl);
return;
}
let nodesToRead = nodeIds.map((nodeId) => ({
nodeId: nodeId,
attributeId: AttributeIds.Value,
TimestampsToReturn: opcua.TimestampsToReturn.Both
}));
session.read(nodesToRead, function (err, dataValues, diagnostics) {
if (err) {
if (diagnostics) {
zlog.error("Error reading nodes diagnostics: " + diagnostics);
}
zlog.error("Error reading nodes: " + err.message);
let payload = {
error: err.message,
endpoint: endpointUrl,
}
closeSession(session, endpointUrl);
zclientOutput(null, copyNewMsg(msg, payload), null);
return;
}
for (let i = 0; i < dataValues.length; i++) {
let dataValue = dataValues[i];
if (dataValue) {
try {
let serverTs = dataValue.serverTimestamp;
let sourceTs = dataValue.sourceTimestamp;
if (serverTs === null) {
serverTs = new Date();
}
if (sourceTs === null) {
sourceTs = new Date();
}
let value = dataValue.value.dataType === opcua.DataType.ExtensionObject
? JSON.parse(JSON.stringify(dataValue.value.value))
: dataValue.value.value;
let payload = {
nodeId: nodeIds[i],
value: value,
statusCode: dataValue.statusCode,
serverTimestamp: serverTs,
sourceTimestamp: sourceTs
};
zclientOutput(copyNewMsg(msg, payload), null, null);
} catch (error) {
let payload = {
error: error.message,
endpoint: endpointUrl,
}
zclientOutput(null, copyNewMsg(msg, payload), null);
return;
}
}
};
zlog.log("Read multiple nodes successfully");
msg.payload = dataValues;
zclientOutput(null, null, msg);
zlog.statusShow();
});
zlog.statusShow();
}
function copyNewMsg(msg, payload) {
let newMsg = cloneDeep(msg);
newMsg.payload = payload;
return newMsg;
}
function initSessionEvent(session, opts, msg) {
session.on("session_closed", function (err) {
closeSession(session, opts.endpointUrl);
});
session.on("keepalive_failure", function (err) {
zlog.log(`Session[${session.name}] keepalive`);
});
}
function removeClient(opts) {
let client = zclients[opts.endpointUrl];
if (client) {
delete zclients[opts.endpointUrl];
client.removeAllListeners();
let session = zsessions[opts.endpointUrl];
if (session) {
session.removeAllListeners();
session.close(function (err) {
if (err) {
zlog.error(`Error closing OPC UA session[${opts.endpointUrl}]: ` + err);
}
delete zsessions[opts.endpointUrl];
client.disconnect(function (err) {
if (err) {
zlog.error(`Error disconnecting OPC UA client[${opts.endpointUrl}]: ` + err);
}
})
});
}
}
}
function initClientEvent(client, opts, msg) {
client.removeAllListeners();
let endpointUrl = opts.endpointUrl;
//当初始连接成功时触发此事件。
client.on("connected", function () {
zclientsTot[endpointUrl] = 1;
zlog.log(`Client[${opts.endpointUrl}] connected to OPC UA server`);
addToList(opts.endpointUrl);
})
client.on("connection_failed", function (err) {
zclientsTot[endpointUrl] = -1;
zlog.error(`Client[${opts.endpointUrl}] connection failed: ` + err.message);
addToFailList(opts.endpointUrl);
});
client.on("start_reconnection", function () {
zlog.log(`Client[${opts.endpointUrl}] start reconnection`);
addToList(opts.endpointUrl);
});
client.on("connection_lost", function (err) {
zclientsTot[endpointUrl] = -1;
zlog.error(`Client[${opts.endpointUrl}] connection lost: ` + client);
addToFailList(opts.endpointUrl);
});
client.on("backoff", function (retry, delay) {
zclientsTot[endpointUrl] = 0;
zlog.log(`Client[${opts.endpointUrl}] backoff: retry=` + retry + ", delay=" + delay);
addToRetryList(opts.endpointUrl);
});
client.on("closed", function () {
zclientsTot[endpointUrl] = -1;
zlog.log(`Client[${opts.endpointUrl}] closed`);
addToFailList(opts.endpointUrl);
});
client.on("abort", function () {
zclientsTot[endpointUrl] = -1;
zlog.log(`Client[${opts.endpointUrl}] abort`);
addToFailList(opts.endpointUrl);
});
client.on("connection_reestablished", function () {
zlog.log(`Client[${opts.endpointUrl}] connection reestablished`);
addToList(opts.endpointUrl);
});
client.on("timed_out_request", function (request) {
zclientsTot[endpointUrl] = -1;
zlog.error(`Client[${opts.endpointUrl}] timed out request: ` + request.requestHeader.requestHandle);
addToFailList(opts.endpointUrl);
});
}
function onInput(msg) {
if (!msg.optuaConfig || !msg.optuaConfig.endpointUrl) {
zlog.error("Error: No optuaConfig found in msg or optuaConfig.endpointUrl is empty");
return;
}
// if(isConnects){
// zlog.warn("Warning: Clients is connected, please wait for a while");
// return;
// }
// isConnects = true;
createClient(msg);
}
function onClose(done) {
zclientsTot = {};
zclients = {};
zclientRetry = {};
zclientFail = {};
zlog.statusShow();
if (Object.keys(zsessions).length === 0) {
zsessions = {};
done();
} else {
Object.keys(zsessions).forEach((endpointUrl) => {
let session = zsessions[endpointUrl];
closeSession(session, endpointUrl);
done();
})
}
}
function closeSession(session, endpointUrl, callback) {
if (session) {
session.close(function (err) {
if (err) {
zlog.error("Error closing session: " + err.message);
} else {
zlog.log("Session closed");
}
delete zsessions[endpointUrl];
callback && callback();
});
} else {
delete zsessions[endpointUrl];
}
addToFailList(endpointUrl);
}
function onError(msg) {
zlog.error("Error: " + msg.error);
}
node.on("input", onInput);
node.on("close", onClose);
node.on("error", onError);
}
RED.nodes.registerType("OpcUa-Zclient", opcuaZclient);
}