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

modbus-serial

Package Overview
Dependencies
Maintainers
1
Versions
123
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

modbus-serial - npm Package Compare versions

Comparing version 8.0.0 to 8.0.1-no-serial-port

319

index.js

@@ -24,2 +24,6 @@ "use strict";

var util = require("util");
var events = require("events");
var EventEmitter = events.EventEmitter || events;
var PORT_NOT_OPEN_MESSAGE = "Port Not Open";

@@ -210,7 +214,14 @@ var PORT_NOT_OPEN_ERRNO = "ECONNREFUSED";

this._port.write(buffer);
if (transaction) {
transaction._timeoutFired = false;
transaction._timeoutHandle = _startTimeout(this._timeout, transaction);
// If in debug mode, stash a copy of the request payload
if (this._debugEnabled) {
transaction.request = Uint8Array.prototype.slice.call(buffer);
transaction.responses = [];
}
}
this._port.write(buffer);
}

@@ -233,3 +244,8 @@

if (transaction.next) {
transaction.next(new TransactionTimedOutError());
var err = new TransactionTimedOutError();
if (transaction.request && transaction.responses) {
err.modbusRequest = transaction.request;
err.modbusResponses = transaction.responses;
}
transaction.next(err);
}

@@ -250,2 +266,153 @@ }, duration);

/**
* Handle incoming data from the Modbus port.
*
* @param {Buffer} data The data received
* @private
*/
function _onReceive(data) {
var modbus = this;
var error;
// set locale helpers variables
var transaction = modbus._transactions[modbus._port._transactionIdRead];
// the _transactionIdRead can be missing, ignore wrong transaction it's
if (!transaction) {
return;
}
if (transaction.responses) {
/* Stash what we received */
transaction.responses.push(Uint8Array.prototype.slice.call(data));
}
/* What do we do next? */
var next = function(err, res) {
if (transaction.next) {
/* Include request/response data if enabled */
if (transaction.request && transaction.responses) {
if (err) {
err.modbusRequest = transaction.request;
err.modbusResponses = transaction.responses;
}
if (res) {
res.request = transaction.request;
res.responses = transaction.responses;
}
}
/* Pass the data on */
return transaction.next(err, res);
}
};
/* cancel the timeout */
_cancelTimeout(transaction._timeoutHandle);
transaction._timeoutHandle = undefined;
/* check if the timeout fired */
if (transaction._timeoutFired === true) {
// we have already called back with an error, so don't generate a new callback
return;
}
/* check incoming data
*/
/* check minimal length
*/
if (!transaction.lengthUnknown && data.length < 5) {
error = "Data length error, expected " +
transaction.nextLength + " got " + data.length;
next(new Error(error));
return;
}
/* check message CRC
* if CRC is bad raise an error
*/
var crcIn = data.readUInt16LE(data.length - 2);
if (crcIn !== crc16(data.slice(0, -2))) {
error = "CRC error";
next(new Error(error));
return;
}
// if crc is OK, read address and function code
var address = data.readUInt8(0);
var code = data.readUInt8(1);
/* check for modbus exception
*/
if (data.length >= 5 &&
code === (0x80 | transaction.nextCode)) {
var errorCode = data.readUInt8(2);
if (transaction.next) {
error = new Error("Modbus exception " + errorCode + ": " + (modbusErrorMessages[errorCode] || "Unknown error"));
error.modbusCode = errorCode;
next(error);
}
return;
}
/* check message length
* if we do not expect this data
* raise an error
*/
if (!transaction.lengthUnknown && data.length !== transaction.nextLength) {
error = "Data length error, expected " +
transaction.nextLength + " got " + data.length;
next(new Error(error));
return;
}
/* check message address and code
* if we do not expect this message
* raise an error
*/
if (address !== transaction.nextAddress || code !== transaction.nextCode) {
error = "Unexpected data error, expected " +
transaction.nextAddress + " got " + address;
next(new Error(error));
return;
}
/* parse incoming data
*/
switch (code) {
case 1:
case 2:
// Read Coil Status (FC=01)
// Read Input Status (FC=02)
_readFC2(data, next);
break;
case 3:
case 4:
// Read Input Registers (FC=04)
// Read Holding Registers (FC=03)
_readFC4(data, next);
break;
case 5:
// Force Single Coil
_readFC5(data, next);
break;
case 6:
// Preset Single Register
_readFC6(data, next);
break;
case 15:
case 16:
// Force Multiple Coils
// Preset Multiple Registers
_readFC16(data, next);
break;
case 43:
// read device identification
_readFC43(data, modbus, next);
}
}
/**
* Class making ModbusRTU calls fun and easy.

@@ -263,3 +430,12 @@ *

this._unitID = 1;
// Flag to indicate whether debug mode (pass-through of raw
// request/response) is enabled.
this._debugEnabled = false;
this._onReceive = _onReceive.bind(this);
EventEmitter.call(this);
};
util.inherits(ModbusRTU, EventEmitter);

@@ -288,124 +464,10 @@ /**

/* On serial port success
* register the modbus parser functions
* (re-)register the modbus parser functions
*/
modbus._port.on("data", function(data) {
// set locale helpers variables
var transaction = modbus._transactions[modbus._port._transactionIdRead];
modbus._port.removeListener("data", modbus._onReceive);
modbus._port.on("data", modbus._onReceive);
// the _transactionIdRead can be missing, ignore wrong transaction it's
if (!transaction) {
return;
}
/* Hook the close event so we can relay it to our callers. */
modbus._port.once("close", modbus.emit.bind(modbus, "close"));
/* cancel the timeout */
_cancelTimeout(transaction._timeoutHandle);
transaction._timeoutHandle = undefined;
/* check if the timeout fired */
if (transaction._timeoutFired === true) {
// we have already called back with an error, so don't generate a new callback
return;
}
/* check incoming data
*/
/* check minimal length
*/
if (!transaction.lengthUnknown && data.length < 5) {
error = "Data length error, expected " +
transaction.nextLength + " got " + data.length;
if (transaction.next)
transaction.next(new Error(error));
return;
}
/* check message CRC
* if CRC is bad raise an error
*/
var crcIn = data.readUInt16LE(data.length - 2);
if (crcIn !== crc16(data.slice(0, -2))) {
error = "CRC error";
if (transaction.next)
transaction.next(new Error(error));
return;
}
// if crc is OK, read address and function code
var address = data.readUInt8(0);
var code = data.readUInt8(1);
/* check for modbus exception
*/
if (data.length >= 5 &&
code === (0x80 | transaction.nextCode)) {
var errorCode = data.readUInt8(2);
if (transaction.next) {
error = new Error("Modbus exception " + errorCode + ": " + (modbusErrorMessages[errorCode] || "Unknown error"));
error.modbusCode = errorCode;
transaction.next(error);
}
return;
}
/* check message length
* if we do not expect this data
* raise an error
*/
if (!transaction.lengthUnknown && data.length !== transaction.nextLength) {
error = "Data length error, expected " +
transaction.nextLength + " got " + data.length;
if (transaction.next)
transaction.next(new Error(error));
return;
}
/* check message address and code
* if we do not expect this message
* raise an error
*/
if (address !== transaction.nextAddress || code !== transaction.nextCode) {
error = "Unexpected data error, expected " +
transaction.nextAddress + " got " + address;
if (transaction.next)
transaction.next(new Error(error));
return;
}
/* parse incoming data
*/
switch (code) {
case 1:
case 2:
// Read Coil Status (FC=01)
// Read Input Status (FC=02)
_readFC2(data, transaction.next);
break;
case 3:
case 4:
// Read Input Registers (FC=04)
// Read Holding Registers (FC=03)
_readFC4(data, transaction.next);
break;
case 5:
// Force Single Coil
_readFC5(data, transaction.next);
break;
case 6:
// Preset Single Register
_readFC6(data, transaction.next);
break;
case 15:
case 16:
// Force Multiple Coils
// Preset Multiple Registers
_readFC16(data, transaction.next);
break;
case 43:
// read device identification
_readFC43(data, modbus, transaction.next);
}
});
/* On serial port open OK call next function with no error */

@@ -420,2 +482,17 @@ if (callback)

/**
* Check if port debug mode is enabled
*/
Object.defineProperty(ModbusRTU.prototype, "isDebugEnabled", {
enumerable: true,
get: function() {
return this._debugEnabled;
},
set: function(enable) {
enable = Boolean(enable);
this._debugEnabled = enable;
}
});
/**
* Check if port is open

@@ -422,0 +499,0 @@ */

{
"name": "modbus-serial",
"version": "8.0.0",
"version": "8.0.1-no-serial-port",
"description": "A pure JavaScript implemetation of MODBUS-RTU (Serial and TCP) for NodeJS.",

@@ -40,5 +40,4 @@ "main": "index.js",

"dependencies": {
"debug": "^4.1.1",
"serialport": "^9.0.0"
"debug": "^4.1.1"
}
}

@@ -12,3 +12,40 @@ "use strict";

var MAX_BUFFER_LENGTH = 256;
var CRC_LENGTH = 2;
var READ_DEVICE_IDENTIFICATION_FUNCTION_CODE = 43;
var LENGTH_UNKNOWN = "unknown";
var BITS_TO_NUM_OF_OBJECTS = 7;
// Helper function -> Bool
// BIT | TYPE
// 8 | OBJECTID
// 9 | length of OBJECTID
// 10 -> n | the object
// 10 + n + 1 | new object id
var calculateFC43Length = function(buffer, numObjects, i, bufferLength) {
var result = { hasAllData: true };
var currentByte = 8 + i; // current byte starts at object id.
if (numObjects > 0) {
for (var j = 0; j < numObjects; j++) {
if (bufferLength < currentByte) {
result.hasAllData = false;
break;
}
var objLength = buffer[currentByte + 1];
if (!objLength) {
result.hasAllData = false;
break;
}
currentByte += 2 + objLength;
}
}
if (currentByte + CRC_LENGTH > bufferLength) {
// still waiting on the CRC!
result.hasAllData = false;
}
if (result.hasAllData) {
result.bufLength = currentByte + CRC_LENGTH;
}
return result;
};
/**

@@ -44,2 +81,4 @@ * Simulate a modbus-RTU port using buffered serial connection.

modbusSerialDebug({ action: "receive serial rtu buffered port", data: data, buffer: self._buffer });
// check if buffer include a complete modbus answer

@@ -49,6 +88,8 @@ var expectedLength = self._length;

modbusSerialDebug({ action: "receive serial rtu buffered port", data: data, buffer: self._buffer });
// check data length
if (expectedLength < MIN_DATA_LENGTH || bufferLength < EXCEPTION_LENGTH) return;
if (expectedLength !== LENGTH_UNKNOWN &&
expectedLength < MIN_DATA_LENGTH ||
bufferLength < EXCEPTION_LENGTH
) { return; }

@@ -63,2 +104,3 @@ // check buffer size for MAX_BUFFER_SIZE

var maxOffset = bufferLength - EXCEPTION_LENGTH;
for (var i = 0; i <= maxOffset; i++) {

@@ -70,10 +112,22 @@ var unitId = self._buffer[i];

if (functionCode === self._cmd && i + expectedLength <= bufferLength) {
self._emitData(i, expectedLength);
return;
if (functionCode === self._cmd && functionCode === READ_DEVICE_IDENTIFICATION_FUNCTION_CODE) {
if (bufferLength <= BITS_TO_NUM_OF_OBJECTS + i) {
return;
}
var numObjects = self._buffer[7 + i];
var result = calculateFC43Length(self._buffer, numObjects, i, bufferLength);
if (result.hasAllData) {
self._emitData(i, result.bufLength);
return;
}
} else {
if (functionCode === self._cmd && i + expectedLength <= bufferLength) {
self._emitData(i, expectedLength);
return;
}
if (functionCode === (0x80 | self._cmd) && i + EXCEPTION_LENGTH <= bufferLength) {
self._emitData(i, EXCEPTION_LENGTH);
return;
}
}
if (functionCode === (0x80 | self._cmd) && i + EXCEPTION_LENGTH <= bufferLength) {
self._emitData(i, EXCEPTION_LENGTH);
return;
}

@@ -110,5 +164,3 @@ // frame header matches, but still missing bytes pending

var buffer = this._buffer.slice(start, start + length);
modbusSerialDebug({ action: "emit data serial rtu buffered port", buffer: buffer });
this.emit("data", buffer);

@@ -172,4 +224,7 @@ this._buffer = this._buffer.slice(start + length);

case 43:
modbusSerialDebug("RTUBuffered F43 not supported");
this._length = 0;
// this function is super special
// you know the format of the code response
// and you need to continuously check that all of the data has arrived before emitting
// see onData for more info.
this._length = LENGTH_UNKNOWN;
break;

@@ -189,3 +244,4 @@ default:

unitid: this._id,
functionCode: this._cmd
functionCode: this._cmd,
length: this._length
});

@@ -192,0 +248,0 @@ };

@@ -97,2 +97,3 @@ "use strict";

handleCallback(had_error);
modbus.emit("close");
});

@@ -99,0 +100,0 @@

@@ -124,2 +124,3 @@ "use strict";

handleCallback(had_error);
modbus.emit("close");
});

@@ -126,0 +127,0 @@

@@ -117,2 +117,3 @@ "use strict";

handleCallback(had_error);
self.emit("close");
});

@@ -119,0 +120,0 @@

@@ -298,2 +298,43 @@ "use strict";

});
it("should include raw payload data in response if debug enabled", function(done) {
/* This feature is common to _all_ function handlers. */
modbusRTU.isDebugEnabled = true;
modbusRTU.writeFC3(1, 8, 3, function(err, data) {
modbusRTU.isDebugEnabled = false;
expect(err).to.be.a("null");
expect(data).to.have.property("request").with.length(8);
expect(data.request.toString("hex")).to.equal("0103000800038409");
expect(data).to.have.property("responses").with.length(1);
expect(data.responses[0]).to.be.instanceof(Buffer).with.length(11);
expect(data.responses[0].toString("hex")).to.equal("010306002a00800005f958");
done();
});
});
it("should include raw payload data in exception if debug enabled", function(done) {
/* This feature is common to _all_ function handlers. */
modbusRTU.isDebugEnabled = true;
// Pretend Unit 3 sends buggy CRCs apparently.
modbusRTU.writeFC3(3, 8, 3, function(err, data) {
modbusRTU.isDebugEnabled = false;
expect(err).to.not.be.a("null");
expect(err).to.have.property("modbusRequest").with.length(8);
expect(err.modbusRequest.toString("hex")).to.equal("03030008000385eb");
expect(err).to.have.property("modbusResponses").with.length(1);
expect(err.modbusResponses[0]).to.be.instanceof(Buffer).with.length(11);
expect(err.modbusResponses[0].toString("hex")).to.equal("030306002a00800005e138");
expect(data).to.be.undefined;
done();
});
});
});

@@ -300,0 +341,0 @@

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