UNPKG

node-red-contrib-opcua-multiple

Version:

multiple client <-> multiple server to read based on node-opcua library

399 lines (375 loc) 16.1 kB
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); }