Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

node-red-contrib-omron-fins

Package Overview
Dependencies
Maintainers
1
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

node-red-contrib-omron-fins - npm Package Compare versions

Comparing version 1.0.0-beta-0 to 1.0.0-beta-1

examples/C-MODE Demo (hostlink serial protocol).json

149

lib/_cmode.js

@@ -60,6 +60,8 @@ (function (exports) {

}
const uint16array = new Uint16Array(
buffer.buffer,
buffer.byteOffset,
buffer.length / Uint16Array.BYTES_PER_ELEMENT);
const uint16array = []
for (let index = 0; index < buffer.length; index+=2) {
const element = buffer.readUInt16BE(index)
uint16array.push(element)
}
buffer.readUInt16BE()
return uint16array

@@ -71,12 +73,3 @@ }

// debugger
let buffer;
if (typeof buf === "string") {
buffer = Buffer.from(buf, "hex");
} else {
buffer = Buffer.from(buf)
}
const uint16array = new Uint16Array(
buffer.buffer,
buffer.byteOffset,
buffer.length / Uint16Array.BYTES_PER_ELEMENT);
const uint16array = to_uint16_parser(buf)
return uint16array.map(e => number2bcd(e))

@@ -139,4 +132,4 @@ }

const to_string_parser = (buf) => {
return Buffer.from(buf, "hex").toString();
const to_string_parser = (buf, enc) => {
return Buffer.from(buf, enc || "hex").toString();
}

@@ -429,3 +422,3 @@

response: [
{ name: "Data", type: "uint[]", min: 1, max: 6144, required: true, hint: "Data read from PLC", parser: to_uint16_parser }
{ name: "Data", type: "uint[]", required: true, hint: "Data read from PLC", parser: to_uint16_parser }
],

@@ -442,3 +435,3 @@ },

response: [
{ name: "Data", type: "uint[]", min: 1, max: 6144, required: true, hint: "Data read from PLC", parser: to_uint16_parser }
{ name: "Data", type: "uint[]", required: true, hint: "Data read from PLC", parser: to_uint16_parser }
],

@@ -453,3 +446,3 @@ },

{ name: "Address", type: "bcd4", min: 0, max: 2555, required: true, hint: "Beginning Word 0 to 2555" },
{ name: "Data", type: "uint[]", min: 1, max: 2556, required: true, hint: "Write data (single or array)" }
{ name: "Data", type: "uint[]", required: true, hint: "Write data (single UINT or array or UINT). \nNOTE: If the value is a string it must be JSON e.g. 1234 or [123,555,66]" }
],

@@ -464,3 +457,3 @@ response: [],

{ name: "Address", type: "bcd4", min: 0, max: 63, required: true, hint: "Beginning Word 0 to 63" },
{ name: "Data", type: "uint[]", min: 1, max: 64, required: true, hint: "Write data (single or array)" }
{ name: "Data", type: "uint[]", required: true, hint: "Write data (single UINT or array or UINT). \nNOTE: If the value is a string it must be JSON e.g. 1234 or [123,555,66]" }
],

@@ -475,3 +468,3 @@ response: [],

{ name: "Address", type: "bcd4", min: 0, max: 99, required: true, hint: "Beginning Word 0 to 99" },
{ name: "Data", type: "uint[]", min: 1, max: 100, required: true, hint: "Write data (single or array)" }
{ name: "Data", type: "uint[]", required: true, hint: "Write data (single UINT or array or UINT). \nNOTE: If the value is a string it must be JSON e.g. 1234 or [123,555,66]" }
],

@@ -486,3 +479,3 @@ "response": [],

{ name: "Address", type: "bcd4", min: 0, max: 99, required: true, hint: "Beginning timer/counter" },
{ name: "Data", type: "uint[]", min: 1, max: 100, required: true, hint: "Write data (single or array)" }
{ name: "Data", type: "uint[]", required: true, hint: "Write data (single UINT or array or UINT). \nNOTE: If the value is a string it must be JSON e.g. 1234 or [123,555,66]" }
],

@@ -497,3 +490,3 @@ "response": [],

{ name: "Address", type: "bcd4", required: true, hint: "Beginning Word" },
{ name: "Data", type: "uint[]", required: true, hint: "Write data (single or array)" }
{ name: "Data", type: "uint[]", required: true, hint: "Write data (single UINT or array or UINT). \nNOTE: If the value is a string it must be JSON e.g. 1234 or [123,555,66]" }
],

@@ -508,3 +501,3 @@ response: [],

{ name: "Address", type: "bcd4", required: true, hint: "Beginning Word" },
{ name: "Data", type: "uint[]", required: true, hint: "Write data (single or array)" }
{ name: "Data", type: "uint[]", required: true, hint: "Write data (single UINT or array or UINT). \nNOTE: If the value is a string it must be JSON e.g. 1234 or [123,555,66]" }
],

@@ -606,3 +599,3 @@ response: [],

],
response: [{ name: "Data", type: "uint[]", min: 1, max: 6144, required: true, hint: "Data read from PLC", parser: to_uint16_parser }],
response: [{ name: "Data", type: "uint[]", required: true, hint: "Data read from PLC", parser: to_uint16_parser }],
},

@@ -617,3 +610,3 @@ {

],
response: [{ name: "Data", type: "uint[]", min: 1, max: 200, required: true, hint: "Data read from PLC", parser: to_uint16_parser }],
response: [{ name: "Data", type: "uint[]", required: true, hint: "Data read from PLC", parser: to_uint16_parser }],
},

@@ -628,3 +621,3 @@ {

],
response: [{ name: "Data", type: "uint[]", min: 1, max: 200, required: true, hint: "Data read from PLC", parser: to_uint16_parser }],
response: [{ name: "Data", type: "uint[]", required: true, hint: "Data read from PLC", parser: to_uint16_parser }],
},

@@ -659,3 +652,3 @@ {

],
response: [{ name: "Data", type: "uint[]", min: 1, max: 6144, required: true, hint: "Data read from PLC", parser: to_uint16_parser }],
response: [{ name: "Data", type: "uint[]", required: true, hint: "Data read from PLC", parser: to_uint16_parser }],
},

@@ -671,3 +664,3 @@ {

],
response: [{ name: "Data", type: "uint[]", min: 1, max: 6144, required: true, hint: "Data read from PLC", parser: to_uint16_parser }],
response: [{ name: "Data", type: "uint[]", required: true, hint: "Data read from PLC", parser: to_uint16_parser }],
},

@@ -682,6 +675,4 @@ {

],
response: [{ name: "Data", type: "uint[]", min: 1, max: 6144, required: true, hint: "Data read from PLC", parser: to_uint16_parser }],
response: [{ name: "Data", type: "uint[]", required: true, hint: "Data read from PLC", parser: to_uint16_parser }],
},
{

@@ -693,9 +684,59 @@ headerCode: "WR",

{ name: "Address", type: "bcd4", min: 0, max: 6143, required: true, hint: "Beginning Word 0 to 6143" },
{ name: "Data", type: "uint[]", min: 1, max: 6144, required: true, hint: "Write data (single or array)" }
{ name: "Data", type: "uint[]", required: true, hint: "Write data (single UINT or array or UINT). \nNOTE: If the value is a string it must be JSON e.g. 1234 or [123,555,66]" }
],
response: [],
},
{
headerCode: "WL",
name: "LR AREA WRITE - WL",
hint: "Writes data to the Link Area (CIO 1000 to CIO 1199) starting from the specified word. Writing is done in word units",
request: [
{ name: "Address", type: "bcd4", min: 0, max: 199, required: true, hint: "Beginning Word 0 to 199" },
{ name: "Data", type: "uint[]", required: true, hint: "Write data (single UINT or array or UINT). \nNOTE: If the value is a string it must be JSON e.g. 1234 or [123,555,66]" }
],
response: [],
},
{
headerCode: "WH",
name: "HR AREA WRITE - WH",
hint: "Writes data to the HR Area (H000 to H511) starting from the specified word. Writing is done in word units",
request: [
{ name: "Address", type: "bcd4", min: 0, max: 511, required: true, hint: "Beginning Word 0 to 511" },
{ name: "Data", type: "uint[]", required: true, hint: "Write data (single UINT or array or UINT). \nNOTE: If the value is a string it must be JSON e.g. 1234 or [123,555,66]" }
],
response: [],
},
// TODO: Impilment T/C PV Write
// {
// headerCode: "WC",
// name: "TIMER/COUNTER PV WRITE - WC",
// hint: "Writes the PVs (present values T0000 to T2047 or C0000 to C2047) of timers/counters starting from the specified word",
// request: [
// { name: "Address", type: "bcd4", min: 0, max: 6143, required: true, hint: "Beginning Word 0 to 6143" },
// { name: "Data", type: "uint[]", required: true, hint: "Write data (single UINT or array or UINT). \nNOTE: If the value is a string it must be JSON e.g. 1234 or [123,555,66]" }
// ],
// response: [],
// },
{
headerCode: "WD",
name: "DM AREA WRITE - WD",
hint: "Writes data to the DM Area starting from the specified word (D00000 to D09999). Writing is done in word units",
request: [
{ name: "Address", type: "bcd4", min: 0, max: 9999, required: true, hint: "Beginning Word 0 to 9999" },
{ name: "Data", type: "uint[]", required: true, hint: "Write data (single UINT or array or UINT). \nNOTE: If the value is a string it must be JSON e.g. 1234 or [123,555,66]" }
],
response: [],
},
{
headerCode: "WJ",
name: "AR AREA WRITE - WJ",
hint: "Writes data to the Auxiliary Area (A448 to A959) starting from the specified word. Writing is done in word units",
request: [
{ name: "Address", type: "bcd4", min: 448, max: 959, required: true, hint: "Beginning Word 448 to 959" },
{ name: "Data", type: "uint[]", required: true, hint: "Write data (single UINT or array or UINT). \nNOTE: If the value is a string it must be JSON e.g. 1234 or [123,555,66]" }
],
response: [],
},
{

@@ -709,3 +750,3 @@ headerCode: "TS",

name: "Data", type: "str", min: 1, max: 118, required: true, hint: "The same characters specified in the command will be returned unaltered if the test is successful",
parser: to_string_parser
//parser: to_string_parser
}

@@ -872,23 +913,30 @@ ],

const ep = expectedParams[index];
let min = ep.min
let max = ep.max
let pv = params[index];
let v = pv;
if (!Array.isArray(v)) { v = [v]; }
switch (ep.type) {
case "bcd4":
//TODO: min/max/isNumeric tests
v = f(v, 4, "0");
case "bcd4[]":
min = min || 0
max = max || 9999
v = v.map(e => f(v, 4, "0"));
break;
case "uint":
//TODO: min/max/isNumeric tests
v = f(parseInt(v).toString(16), 4, "0");
break;
case "uint[]":
//TODO: min/max/isNumeric tests
if (!Array.isArray(v)) { v = [v]; }
min = min || 0
max = max || 65535
v = v.map(e => f(parseInt(e).toString(16), 4, "0"));
v = v.join("");
break;
default:
break;
}
if (min != null && v.some((e) => parseInt(e) < min)) {
throw new Error(`A value in parameter '${ep.name}' is less than min (${min})`)
}
if (max != null && v.some((e) => parseInt(e) > max)) {
throw new Error(`A value in parameter '${ep.name}' is greater than max (${max})`)
}
v = v.join("");
commandParamsArr.push(v);

@@ -900,7 +948,7 @@ }

const commandLength = command.length;
const macLenFF = this.frame.firstFrame.maxLength;
const macLenIF = this.frame.intermediateFrame.maxLength;
const maxLenFF = this.frame.firstFrame.maxLength;
const maxLenIF = this.frame.intermediateFrame.maxLength;
*/
const FCS = CModeHelper.prototype.calculateFCS(command);
let payload = command + FCS + terminator;
const CRC = f(CModeHelper.calculateFCS(command).toString(16).toUpperCase(), 2, '0');
let payload = command + CRC + terminator;
return payload;

@@ -910,3 +958,3 @@ }

}
CModeHelper.prototype.calculateFCS = function (data) {
CModeHelper.calculateFCS = function (data) {
let CRC = 0;

@@ -917,4 +965,3 @@ const dataLen = data.length;

}
const FCS = CRC.toString(16).toUpperCase();
return FCS;
return CRC;
}

@@ -921,0 +968,0 @@

@@ -42,4 +42,3 @@ module.exports = function (RED) {

const paramName = "p" + paramNo;
const paramValue = getParamValue(paramNo, msg);
cModeCommand[paramName] = paramValue;
let paramValue = getParamValue(paramNo, msg);
if(param.required && paramValue == null) {

@@ -51,2 +50,6 @@ throw new Error(`Param ${paramName} (${param.name}) is required`)

}
if(param.type === 'uint[]' && typeof paramValue === "string") {
paramValue = JSON.parse(paramValue)
}
cModeCommand[paramName] = paramValue;
param.value = paramValue;

@@ -53,0 +56,0 @@ cModeCommand.params.push(param);

@@ -20,6 +20,20 @@ module.exports = function (RED) {

try {
const response = (msg.payload || '').trim();//remove CR etc
const startMarkOK = response.startsWith("@");//get startMark
const endMarkOK = response.endsWith("*");//get endMark
const endCode = response.substr(5, 2);
const response = (msg.payload ? msg.payload.toString() : '').trim()
msg.cModeResponse = {timestamp: Date.now(), response}
const startMark = response.slice(0,1)
const endMark = response.slice(-1)
const startMarkOK = startMark === "@";
const endMarkOK = endMark === "*";//TODO: Support frames
const hostNumber = response.substr(1,2);
const headerCode = response.substr(3,2);
let endCode = response.substr(5, 2);
let dataStart = 7;
msg.cModeResponse.startMark = startMark
msg.cModeResponse.endMark = endMark
msg.cModeResponse.hostNumber = hostNumber
msg.cModeResponse.headerCode = headerCode
msg.cModeResponse.endCode = endCode
if(!startMarkOK) {

@@ -39,2 +53,9 @@ throw new Error("Invalid C-Mode Response. Expected a string beginning with @")

}
if(headerCode === 'TS') {
if(response === (msg.request_payload || '').trim()) {
endCode = '00'
msg.cModeResponse.endCode = endCode
dataStart -= 2
}
}
if (endCode !== '00') {

@@ -46,16 +67,11 @@ const ec = cmode.parseEndCode(endCode);

//TODO: move the parsing into C_MODE
const dataStart = 7;
const totalLength = response.length;
const dataLength = totalLength - 10;
const hostNumber = response.substr(1,2);
const headerCode = response.substr(3,2);
const dataLength = totalLength - dataStart - 3 /* 3 = 2 for CRC and 1 for asterix */;
const data = response.substr(dataStart, dataLength);
const CRCin = parseInt(response.substr(dataStart+dataLength, 2), 16);
let CRC = 0;
for (let ch = 0; ch <= (totalLength - 4); ch++) {
CRC = response.charCodeAt(ch) ^ CRC;
const CRC = parseInt(response.substr(dataStart + dataLength, 2), 16)
const CRCcalc = C_MODE.CModeHelper.calculateFCS(response.slice(0, -3))
if(CRC !== CRCcalc) {
throw new Error(`Invalid frame check sequence. Received CRC ${CRC.toString(16)}, expected ${CRCcalc.toString(16)}`)
}
if(CRCin !== CRC) {
throw new Error(`Invalid frame check sequence. Received CRC ${CRCin.toString(16)}, expected ${CRC.toString(16)}`)
}
if(msg.cModeCommand.hostNumber != hostNumber) {

@@ -67,12 +83,7 @@ throw new Error(`Unexpected host number. Received ${hostNumber}, expected ${msg.cModeCommand.hostNumber}`)

}
msg.cModeResponse.data = data
msg.cModeResponse.CRC = CRC
msg.cModeResponse.CRCCalc = CRCcalc
const command = cmode.getCommand(headerCode);
msg.cModeResponse = {
timestamp: Date.now(),
response,
hostNumber,
headerCode,
data,
CRC: CRCin,
CRCCalc: CRC
}
if(!command) {

@@ -86,17 +97,22 @@ //command not explicitly supported, lets just return the data

msg.cModeResponse.params = [];
msg.payload = {};
for (let index = 0; index < command.response.length; index++) {
const param = { ...command.response[index] };
const paramNo = index + 1;
const paramName = param.name;
const paramType = param.type;
const paramHint = param.hint;
const parser = param.parser || (e => e);
const paramValue = parser(data);
msg.cModeResponse.params.push({
paramNo, paramName, paramType, paramHint, paramValue
});
msg.payload[paramName] = paramValue;
msg.payload.buffer = Buffer.from(data,"hex");
if(command.response.length === 0) {
msg.payload = true
node.send(msg);
} else {
msg.payload = {};
for (let index = 0; index < command.response.length; index++) {
const param = { ...command.response[index] };
const paramNo = index + 1;
const paramName = param.name;
const paramType = param.type;
const paramHint = param.hint;
const parser = param.parser || (e => e);
const paramValue = parser(data);
msg.cModeResponse.params.push({
paramNo, paramName, paramType, paramHint, paramValue
});
msg.payload[paramName] = paramValue;
msg.payload.buffer = Buffer.from(data,"hex");
node.send(msg);
}
}

@@ -103,0 +119,0 @@ }

{
"name": "node-red-contrib-omron-fins",
"version": "1.0.0-beta-0",
"version": "1.0.0-beta-1",
"author": {

@@ -18,2 +18,3 @@ "name": "Steve-Mcl",

"node-red": {
"version": ">=1.0.0",
"nodes": {

@@ -42,2 +43,5 @@ "omronRead": "nodes/read.js",

},
"engines": {
"node": ">=8"
},
"homepage": "https://github.com/steve-mcl/node-red-contrib-omron-fins#readme",

@@ -44,0 +48,0 @@ "repository": {

@@ -5,3 +5,3 @@ node-red-contrib-omron-fins

## Overview
This is a Node-RED node module to directly interface with OMRON PLCs over FINS Ethernet protocol.
This is a Node-RED node module to directly interface with OMRON PLCs over Ethernet (FINS protocol) or Serial (Hostlink protocol).
Tested on CV, CP, CS, CJ, NJ and NX PLCs (the ones with FINS support)

@@ -27,13 +27,10 @@

* Set Clock
* C-Mode Command - A node to build a C-Mode command that can be transmitted to a serial port (hostlink protocol)
* C-Mode Response - A node to decode the C-Mode command response (hostlink protocol)
## Version Update Notes
This release (and possibly future releases up to V1.0.0) has breaking changes.
Where possible, I make every attempt to keep things compatible, but as node-red improves (typedInput widgets for example) I too improve this node to make things easier or better. And sometimes, it becomes plain obvious a wrong decision was made that needs to be rectified before it becomes too late to change - it happens :)
Semantic Versioning 2.0.0 will be followed after V1 however for now, where you see `V0.x.y`...
* `x` = major / minor change
* `y` = patch / bugfix
## Tips
* On a reasonable VM, I have managed to achieve polling speeds less than 10ms (100+ reads per second) HOWEVER this really taxes NODE and Node-red. Through usage and testing, this node works very well polling 10 times per second (100ms poll time). This is often more than enough for UI type applications
* Where possible, group your items together in the PLC and do 1 large read as opposed to multiple small reads. Reading 20 WDs from 1 location is much faster than reading 20 single items. An additional benefit of reading multiple items in one poll is the data is consistent (i.e. all values were read at on the same PLC poll)
* On a reasonable VM, I have managed to achieve polling over UDP at speeds less than 10ms (100+ reads per second)
* HOWEVER this really taxes NODE and Node-red. Through usage and testing, this node works very well polling 10 times per second (100ms poll time). This is often more than enough for UI type applications
* Where possible, group your items together in the PLC and do 1 large read as opposed to multiple small reads.
* Reading 20 WDs from 1 location is much faster than reading 20 single items. An additional benefit of reading multiple items in one poll is the data is consistent (i.e. all values were read at on the same PLC poll)

@@ -40,0 +37,0 @@ ## Prerequisites

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc