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 7.2.3 to 7.3.0

.npmignore

8

examples/async_server.js

@@ -26,2 +26,10 @@ /* eslint-disable no-console, no-unused-vars, spaced-comment */

},
getDiscreteInput: function(addr, unitID) {
// Asynchronous handling (with Promises, async/await supported)
return new Promise(function(resolve) {
setTimeout(function() {
resolve(addr % 2 === 0);
}, 10);
});
},
setRegister: function(addr, value, unitID) {

@@ -28,0 +36,0 @@ // Asynchronous handling supported also here

@@ -9,2 +9,16 @@ /* eslint-disable no-console, no-unused-vars, spaced-comment */

getHoldingRegister: function(addr) { return addr + 8000; },
getMultipleInputRegisters: function(startAddr, length) {
var values = [];
for (var i = 0; i < length; i++) {
values[i] = startAddr + i;
}
return values;
},
getMultipleHoldingRegisters: function(startAddr, length) {
var values = [];
for (var i = 0; i < length; i++) {
values[i] = startAddr + i + 8000;
}
return values;
},
getCoil: function(addr) { return (addr % 2) === 0; },

@@ -11,0 +25,0 @@ setRegister: function(addr, value) { console.log("set register", addr, value); return; },

21

index.d.ts

@@ -19,9 +19,16 @@ declare namespace ModbusRTU {

// Connection shorthand API
connectRTU(path: string, options: SerialPortOptions, next: Function): Promise<void>;
connectTCP(ip: string, options: TcpPortOptions, next: Function): Promise<void>;
connectTcpRTUBuffered(ip: string, options: TcpRTUPortOptions, next: Function): Promise<void>;
connectTelnet(ip: string, options: TelnetPortOptions, next: Function): Promise<void>;
connectC701(ip: string, options: C701PortOptions, next: Function): Promise<void>;
connectRTUBuffered(path: string, options: SerialPortOptions, next: Function): Promise<void>;
connectAsciiSerial(path: string, options: SerialPortOptions, next: Function): Promise<void>;
connectRTU(path: string, options: SerialPortOptions, next: Function): void;
connectRTU(path: string, options: SerialPortOptions): Promise<void>;
connectTCP(ip: string, options: TcpPortOptions, next: Function): void;
connectTCP(ip: string, options: TcpPortOptions): Promise<void>;
connectTcpRTUBuffered(ip: string, options: TcpRTUPortOptions, next: Function): void;
connectTcpRTUBuffered(ip: string, options: TcpRTUPortOptions): Promise<void>;
connectTelnet(ip: string, options: TelnetPortOptions, next: Function): void;
connectTelnet(ip: string, options: TelnetPortOptions): Promise<void>;
connectC701(ip: string, options: C701PortOptions, next: Function): void;
connectC701(ip: string, options: C701PortOptions): Promise<void>;
connectRTUBuffered(path: string, options: SerialPortOptions, next: Function): void;
connectRTUBuffered(path: string, options: SerialPortOptions): Promise<void>;
connectAsciiSerial(path: string, options: SerialPortOptions, next: Function): void;
connectAsciiSerial(path: string, options: SerialPortOptions): Promise<void>;

@@ -28,0 +35,0 @@ // Promise API

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

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -24,4 +24,11 @@ "use strict";

var HOST = "127.0.0.1";
var UNIT_ID = 255; // listen to all adresses
var MODBUS_PORT = 502;
// Not really its official length, but we parse UnitID as part of PDU
const MBAP_LEN = 6;
/* Get Handlers
*/
var handlers = require("./servertcp_handler");
/* Add bit operation functions to Buffer

@@ -33,36 +40,52 @@ */

/**
* Parse a ModbusRTU buffer and return an answer buffer.
* Helper function for sending debug objects.
*
* @param {string} text - text of message, an error or an action
* @param {int} unitID - Id of the requesting unit
* @param {int} functionCode - a modbus function code.
* @param {Buffer} requestBuffer - request Buffer from client
* @param {object} vector - vector of functions for read and write
* @param {function} callback - callback to be invoked passing {Buffer} response
* @returns undefined
* @private
*/
function _parseModbusBuffer(requestBuffer, vector, callback) {
var unitID = requestBuffer[0];
var functionCode = requestBuffer[1];
var crc = requestBuffer[requestBuffer.length - 2] + requestBuffer[requestBuffer.length - 1] * 0x100;
function _serverDebug(text, unitID, functionCode, responseBuffer) {
// If no responseBuffer, then assume this is an error
// o/w assume an action
if (typeof responseBuffer === "undefined") {
modbusSerialDebug({
error: text,
unitID: unitID,
functionCode: functionCode
});
// if crc is bad, ignore message
if (crc !== crc16(requestBuffer.slice(0, -2))) {
modbusSerialDebug("wrong CRC of request Buffer");
return;
} else {
modbusSerialDebug({
action: text,
unitID: unitID,
functionCode: functionCode,
responseBuffer: responseBuffer.toString("hex")
});
}
}
modbusSerialDebug("request for function code " + functionCode);
/**
* Helper function for creating callback functions.
*
* @param {int} unitID - Id of the requesting unit
* @param {int} functionCode - a modbus function code
* @param {function} sockWriter - write buffer (or error) to tcp socket
* @returns {function} - a callback function
* @private
*/
function _callbackFactory(unitID, functionCode, sockWriter) {
return function cb(err, responseBuffer) {
var crc;
var cb = function(err, responseBuffer) {
// If we have an error.
if (err) {
modbusSerialDebug({
error: "error processing response",
unitID: unitID,
functionCode: functionCode
});
var errorCode = 0x04; // slave device failure
if (!isNaN(err.modbusErrorCode))
if (!isNaN(err.modbusErrorCode)) {
errorCode = err.modbusErrorCode;
}
// set an error response
// Set an error response
functionCode = parseInt(functionCode) | 0x80;

@@ -72,63 +95,84 @@ responseBuffer = Buffer.alloc(3 + 2);

cb(null, responseBuffer);
return;
_serverDebug("error processing response", unitID, functionCode);
}
// add unit-id, function code and crc
if (responseBuffer) {
// add unit number and function code
responseBuffer.writeUInt8(unitID, 0);
responseBuffer.writeUInt8(functionCode, 1);
// If we do not have a responseBuffer
if (!responseBuffer) {
_serverDebug("no response buffer", unitID, functionCode);
return sockWriter(null, responseBuffer);
}
// add crc
crc = crc16(responseBuffer.slice(0, -2));
responseBuffer.writeUInt16LE(crc, responseBuffer.length - 2);
// add unit number and function code
responseBuffer.writeUInt8(unitID, 0);
responseBuffer.writeUInt8(functionCode, 1);
modbusSerialDebug({
action: "server response",
unitID: unitID,
functionCode: functionCode,
responseBuffer: responseBuffer.toString("hex")
});
}
else {
modbusSerialDebug({
error: "no response buffer",
unitID: unitID,
functionCode: functionCode
});
}
// Add crc
crc = crc16(responseBuffer.slice(0, -2));
responseBuffer.writeUInt16LE(crc, responseBuffer.length - 2);
modbusSerialDebug({
action: "server response",
unitID: unitID,
functionCode: functionCode,
responseBuffer: responseBuffer.toString("hex")
});
callback(null, responseBuffer);
// Call callback function
_serverDebug("server response", unitID, functionCode, responseBuffer);
return sockWriter(null, responseBuffer);
};
}
/**
* Parse a ModbusRTU buffer and return an answer buffer.
*
* @param {Buffer} requestBuffer - request Buffer from client
* @param {object} vector - vector of functions for read and write
* @param {function} callback - callback to be invoked passing {Buffer} response
* @returns undefined
* @private
*/
function _parseModbusBuffer(requestBuffer, vector, serverUnitID, sockWriter) {
var cb;
// Check requestBuffer length
if (!requestBuffer || requestBuffer.length < MBAP_LEN) {
modbusSerialDebug("wrong size of request Buffer " + requestBuffer.length);
return;
}
var unitID = requestBuffer[0];
var functionCode = requestBuffer[1];
var crc = requestBuffer[requestBuffer.length - 2] + requestBuffer[requestBuffer.length - 1] * 0x100;
// if crc is bad, ignore message
if (crc !== crc16(requestBuffer.slice(0, -2))) {
modbusSerialDebug("wrong CRC of request Buffer");
return;
}
// if crc is bad, ignore message
if (serverUnitID !== 255 && serverUnitID !== unitID) {
modbusSerialDebug("wrong unitID");
return;
}
modbusSerialDebug("request for function code " + functionCode);
cb = _callbackFactory(unitID, functionCode, sockWriter);
switch (parseInt(functionCode)) {
case 1:
case 2:
_handleReadCoilsOrInputDiscretes(requestBuffer, vector, unitID, cb);
handlers.readCoilsOrInputDiscretes(requestBuffer, vector, unitID, cb, functionCode);
break;
case 3:
_handleReadMultipleRegisters(requestBuffer, vector, unitID, cb);
handlers.readMultipleRegisters(requestBuffer, vector, unitID, cb);
break;
case 4:
_handleReadInputRegisters(requestBuffer, vector, unitID, cb);
handlers.readInputRegisters(requestBuffer, vector, unitID, cb);
break;
case 5:
_handleWriteCoil(requestBuffer, vector, unitID, cb);
handlers.writeCoil(requestBuffer, vector, unitID, cb);
break;
case 6:
_handleWriteSingleRegister(requestBuffer, vector, unitID, cb);
handlers.writeSingleRegister(requestBuffer, vector, unitID, cb);
break;
case 15:
_handleForceMultipleCoils(requestBuffer, vector, unitID, cb);
handlers.forceMultipleCoils(requestBuffer, vector, unitID, cb);
break;
case 16:
_handleWriteMultipleRegisters(requestBuffer, vector, unitID, cb);
handlers.writeMultipleRegisters(requestBuffer, vector, unitID, cb);
break;

@@ -148,541 +192,6 @@ default:

cb(null, responseBuffer);
cb({ modbusErrorCode: errorCode }, responseBuffer);
}
}
/**
* Check the length of request Buffer for length of 8.
*
* @param requestBuffer - request Buffer from client
* @returns {boolean} - if error it is true, otherwise false
* @private
*/
function _errorRequestBufferLength(requestBuffer) {
if (requestBuffer.length !== 8) {
modbusSerialDebug("request Buffer length " + requestBuffer.length + " is wrong - has to be >= 8");
return true;
}
return false; // length is okay - no error
}
/**
* Handle the callback invocation for Promises or synchronous values
*
* @param promiseOrValue - the Promise to be resolved or value to be returned
* @param cb - the callback to be invoked
* @returns undefined
* @private
*/
function _handlePromiseOrValue(promiseOrValue, cb) {
if (promiseOrValue && promiseOrValue.then && typeof promiseOrValue.then === "function") {
promiseOrValue.then(function(value) {
cb(null, value);
});
if (promiseOrValue.catch && typeof promiseOrValue.catch === "function") {
promiseOrValue.catch(function(err) {
cb(err);
});
}
}
else {
cb(null, promiseOrValue);
}
}
/**
* Function to handle FC1 or FC2 request.
*
* @param requestBuffer - request Buffer from client
* @param vector - vector of functions for read and write
* @param unitID - Id of the requesting unit
* @param {function} callback - callback to be invoked passing {Buffer} response
* @returns undefined
* @private
*/
function _handleReadCoilsOrInputDiscretes(requestBuffer, vector, unitID, callback) {
var address = requestBuffer.readUInt16BE(2);
var length = requestBuffer.readUInt16BE(4);
if (_errorRequestBufferLength(requestBuffer)) {
return;
}
// build answer
var dataBytes = parseInt((length - 1) / 8 + 1);
var responseBuffer = Buffer.alloc(3 + dataBytes + 2);
responseBuffer.writeUInt8(dataBytes, 2);
// read coils
if (vector.getCoil) {
var callbackInvoked = false;
var cbCount = 0;
var buildCb = function(i) {
return function(err, value) {
if (err) {
if (!callbackInvoked) {
callbackInvoked = true;
callback(err);
}
return;
}
cbCount = cbCount + 1;
responseBuffer.writeBit(value, i % 8, 3 + parseInt(i / 8));
if (cbCount === length && !callbackInvoked) {
modbusSerialDebug({ action: "FC1/2 response", responseBuffer: responseBuffer });
callbackInvoked = true;
callback(null, responseBuffer);
}
};
};
if (length === 0)
callback({
modbusErrorCode: 0x02, // Illegal address
msg: "Invalid length"
});
for (var i = 0; i < length; i++) {
var cb = buildCb(i);
try {
if (vector.getCoil.length === 3) {
vector.getCoil(address + i, unitID, cb);
}
else {
var promiseOrValue = vector.getCoil(address + i, unitID);
_handlePromiseOrValue(promiseOrValue, cb);
}
}
catch(err) {
cb(err);
}
}
}
}
/**
* Function to handle FC3 request.
*
* @param requestBuffer - request Buffer from client
* @param vector - vector of functions for read and write
* @param unitID - Id of the requesting unit
* @param {function} callback - callback to be invoked passing {Buffer} response
* @returns undefined
* @private
*/
function _handleReadMultipleRegisters(requestBuffer, vector, unitID, callback) {
var address = requestBuffer.readUInt16BE(2);
var length = requestBuffer.readUInt16BE(4);
if (_errorRequestBufferLength(requestBuffer)) {
return;
}
// build answer
var responseBuffer = Buffer.alloc(3 + length * 2 + 2);
responseBuffer.writeUInt8(length * 2, 2);
// read registers
if (vector.getHoldingRegister) {
var callbackInvoked = false;
var cbCount = 0;
var buildCb = function(i) {
return function(err, value) {
if (err) {
if (!callbackInvoked) {
callbackInvoked = true;
callback(err);
}
return;
}
cbCount = cbCount + 1;
responseBuffer.writeUInt16BE(value, 3 + i * 2);
if (cbCount === length && !callbackInvoked) {
modbusSerialDebug({ action: "FC3 response", responseBuffer: responseBuffer });
callbackInvoked = true;
callback(null, responseBuffer);
}
};
};
if (length === 0)
callback({
modbusErrorCode: 0x02, // Illegal address
msg: "Invalid length"
});
for (var i = 0; i < length; i++) {
var cb = buildCb(i);
try {
if (vector.getHoldingRegister.length === 3) {
vector.getHoldingRegister(address + i, unitID, cb);
}
else {
var promiseOrValue = vector.getHoldingRegister(address + i, unitID);
_handlePromiseOrValue(promiseOrValue, cb);
}
}
catch(err) {
cb(err);
}
}
}
}
/**
* Function to handle FC4 request.
*
* @param requestBuffer - request Buffer from client
* @param vector - vector of functions for read and write
* @param unitID - Id of the requesting unit
* @param {function} callback - callback to be invoked passing {Buffer} response
* @returns undefined
* @private
*/
function _handleReadInputRegisters(requestBuffer, vector, unitID, callback) {
var address = requestBuffer.readUInt16BE(2);
var length = requestBuffer.readUInt16BE(4);
if (_errorRequestBufferLength(requestBuffer)) {
return;
}
// build answer
var responseBuffer = Buffer.alloc(3 + length * 2 + 2);
responseBuffer.writeUInt8(length * 2, 2);
if (vector.getInputRegister) {
var callbackInvoked = false;
var cbCount = 0;
var buildCb = function(i) {
return function(err, value) {
if (err) {
if (!callbackInvoked) {
callbackInvoked = true;
callback(err);
}
return;
}
cbCount = cbCount + 1;
responseBuffer.writeUInt16BE(value, 3 + i * 2);
if (cbCount === length && !callbackInvoked) {
modbusSerialDebug({ action: "FC4 response", responseBuffer: responseBuffer });
callbackInvoked = true;
callback(null, responseBuffer);
}
};
};
if (length === 0)
callback({
modbusErrorCode: 0x02, // Illegal address
msg: "Invalid length"
});
for (var i = 0; i < length; i++) {
var cb = buildCb(i);
try {
if (vector.getInputRegister.length === 3) {
vector.getInputRegister(address + i, unitID, cb);
}
else {
var promiseOrValue = vector.getInputRegister(address + i, unitID);
_handlePromiseOrValue(promiseOrValue, cb);
}
}
catch(err) {
cb(err);
}
}
}
}
/**
* Function to handle FC5 request.
*
* @param requestBuffer - request Buffer from client
* @param vector - vector of functions for read and write
* @param unitID - Id of the requesting unit
* @param {function} callback - callback to be invoked passing {Buffer} response
* @returns undefined
* @private
*/
function _handleWriteCoil(requestBuffer, vector, unitID, callback) {
var address = requestBuffer.readUInt16BE(2);
var state = requestBuffer.readUInt16BE(4);
if (_errorRequestBufferLength(requestBuffer)) {
return;
}
// build answer
var responseBuffer = Buffer.alloc(8);
responseBuffer.writeUInt16BE(address, 2);
responseBuffer.writeUInt16BE(state, 4);
if (vector.setCoil) {
var callbackInvoked = false;
var cb = function(err) {
if (err) {
if (!callbackInvoked) {
callbackInvoked = true;
callback(err);
}
return;
}
if (!callbackInvoked) {
modbusSerialDebug({ action: "FC5 response", responseBuffer: responseBuffer });
callbackInvoked = true;
callback(null, responseBuffer);
}
};
try {
if (vector.setCoil.length === 4) {
vector.setCoil(address, state === 0xff00, unitID, cb);
}
else {
var promiseOrValue = vector.setCoil(address, state === 0xff00, unitID);
_handlePromiseOrValue(promiseOrValue, cb);
}
}
catch(err) {
cb(err);
}
}
}
/**
* Function to handle FC6 request.
*
* @param requestBuffer - request Buffer from client
* @param vector - vector of functions for read and write
* @param unitID - Id of the requesting unit
* @param {function} callback - callback to be invoked passing {Buffer} response
* @returns undefined
* @private
*/
function _handleWriteSingleRegister(requestBuffer, vector, unitID, callback) {
var address = requestBuffer.readUInt16BE(2);
var value = requestBuffer.readUInt16BE(4);
if (_errorRequestBufferLength(requestBuffer)) {
return;
}
// build answer
var responseBuffer = Buffer.alloc(8);
responseBuffer.writeUInt16BE(address, 2);
responseBuffer.writeUInt16BE(value, 4);
if (vector.setRegister) {
var callbackInvoked = false;
var cb = function(err) {
if (err) {
if (!callbackInvoked) {
callbackInvoked = true;
callback(err);
}
return;
}
if (!callbackInvoked) {
modbusSerialDebug({ action: "FC6 response", responseBuffer: responseBuffer });
callbackInvoked = true;
callback(null, responseBuffer);
}
};
try {
if (vector.setRegister.length === 4) {
vector.setRegister(address, value, unitID, cb);
}
else {
var promiseOrValue = vector.setRegister(address, value, unitID);
_handlePromiseOrValue(promiseOrValue, cb);
}
} catch(err) {
cb(err);
}
}
}
/**
* Function to handle FC15 request.
*
* @param requestBuffer - request Buffer from client
* @param vector - vector of functions for read and write
* @param unitID - Id of the requesting unit
* @param {function} callback - callback to be invoked passing {Buffer} response
* @returns undefined
* @private
*/
function _handleForceMultipleCoils(requestBuffer, vector, unitID, callback) {
var address = requestBuffer.readUInt16BE(2);
var length = requestBuffer.readUInt16BE(4);
// if length is bad, ignore message
if (requestBuffer.length !== 7 + Math.ceil(length / 8) + 2) {
return;
}
// build answer
var responseBuffer = Buffer.alloc(8);
responseBuffer.writeUInt16BE(address, 2);
responseBuffer.writeUInt16BE(length, 4);
if (vector.setCoil) {
var callbackInvoked = false;
var cbCount = 0;
var buildCb = function(/* i - not used at the moment */) {
return function(err) {
if (err) {
if (!callbackInvoked) {
callbackInvoked = true;
callback(err);
}
return;
}
cbCount = cbCount + 1;
if (cbCount === length && !callbackInvoked) {
modbusSerialDebug({ action: "FC15 response", responseBuffer: responseBuffer });
callbackInvoked = true;
callback(null, responseBuffer);
}
};
};
if (length === 0)
callback({
modbusErrorCode: 0x02, // Illegal address
msg: "Invalid length"
});
var state;
for (var i = 0; i < length; i++) {
var cb = buildCb(i);
state = requestBuffer.readBit(i, 7);
try {
if (vector.setCoil.length === 4) {
vector.setCoil(address + i, state !== false, unitID, cb);
}
else {
var promiseOrValue = vector.setCoil(address + i, state !== false, unitID);
_handlePromiseOrValue(promiseOrValue, cb);
}
}
catch(err) {
cb(err);
}
}
}
}
/**
* Function to handle FC16 request.
*
* @param requestBuffer - request Buffer from client
* @param vector - vector of functions for read and write
* @param unitID - Id of the requesting unit
* @param {function} callback - callback to be invoked passing {Buffer} response
* @returns undefined
* @private
*/
function _handleWriteMultipleRegisters(requestBuffer, vector, unitID, callback) {
var address = requestBuffer.readUInt16BE(2);
var length = requestBuffer.readUInt16BE(4);
// if length is bad, ignore message
if (requestBuffer.length !== (7 + length * 2 + 2)) {
return;
}
// build answer
var responseBuffer = Buffer.alloc(8);
responseBuffer.writeUInt16BE(address, 2);
responseBuffer.writeUInt16BE(length, 4);
// write registers
if (vector.setRegister) {
var callbackInvoked = false;
var cbCount = 0;
var buildCb = function(/* i - not used at the moment */) {
return function(err) {
if (err) {
if (!callbackInvoked) {
callbackInvoked = true;
callback(err);
}
return;
}
cbCount = cbCount + 1;
if (cbCount === length && !callbackInvoked) {
modbusSerialDebug({ action: "FC16 response", responseBuffer: responseBuffer });
callbackInvoked = true;
callback(null, responseBuffer);
}
};
};
if (length === 0)
callback({
modbusErrorCode: 0x02, // Illegal address
msg: "Invalid length"
});
var value;
for (var i = 0; i < length; i++) {
var cb = buildCb(i);
value = requestBuffer.readUInt16BE(7 + i * 2);
try {
if (vector.setRegister.length === 4) {
vector.setRegister(address + i, value, unitID, cb);
}
else {
var promiseOrValue = vector.setRegister(address + i, value, unitID);
_handlePromiseOrValue(promiseOrValue, cb);
}
}
catch(err) {
cb(err);
}
}
}
}
/**
* Class making ModbusTCP server.

@@ -697,2 +206,3 @@ *

options = options || {};
var recvBuffer = Buffer.from([]);

@@ -703,2 +213,5 @@ // create a tcp server

// create a server unit id
var serverUnitID = options.unitID || UNIT_ID;
modbus._server.on("connection", function(sock) {

@@ -714,45 +227,59 @@ modbusSerialDebug({

modbusSerialDebug({ action: "socket data", data: data });
recvBuffer = Buffer.concat([recvBuffer, data], recvBuffer.length + data.length);
// remove mbap and add crc16
var requestBuffer = Buffer.alloc(data.length - 6 + 2);
data.copy(requestBuffer, 0, 6);
var crc = crc16(requestBuffer.slice(0, -2));
requestBuffer.writeUInt16LE(crc, requestBuffer.length - 2);
while(recvBuffer.length > MBAP_LEN) {
const transactionsId = recvBuffer.readUInt16BE(0);
var pduLen = recvBuffer.readUInt16BE(4);
modbusSerialDebug({ action: "receive", data: requestBuffer, requestBufferLength: requestBuffer.length });
modbusSerialDebug(JSON.stringify({ action: "receive", data: requestBuffer }));
// Check the presence of the full request (MBAP + PDU)
if(recvBuffer.length - MBAP_LEN < pduLen)
break;
// if length is too short, ignore message
if (requestBuffer.length < 8) {
return;
}
// remove mbap and add crc16
var requestBuffer = Buffer.alloc(pduLen + 2);
recvBuffer.copy(requestBuffer, 0, MBAP_LEN, MBAP_LEN + pduLen);
var cb = function(err, responseBuffer) {
if (err) {
modbus.emit("error", err);
return;
}
// Move receive buffer on
recvBuffer = recvBuffer.slice(MBAP_LEN + pduLen);
// send data back
if (responseBuffer) {
// get transaction id
var transactionsId = data.readUInt16BE(0);
var crc = crc16(requestBuffer.slice(0, -2));
requestBuffer.writeUInt16LE(crc, requestBuffer.length - 2);
// remove crc and add mbap
var outTcp = Buffer.alloc(responseBuffer.length + 6 - 2);
outTcp.writeUInt16BE(transactionsId, 0);
outTcp.writeUInt16BE(0, 2);
outTcp.writeUInt16BE(responseBuffer.length - 2, 4);
responseBuffer.copy(outTcp, 6);
modbusSerialDebug({ action: "receive", data: requestBuffer, requestBufferLength: requestBuffer.length });
modbusSerialDebug(JSON.stringify({ action: "receive", data: requestBuffer }));
modbusSerialDebug({ action: "send", data: responseBuffer });
modbusSerialDebug(JSON.stringify({ action: "send string", data: responseBuffer }));
var sockWriter = function(err, responseBuffer) {
if (err) {
modbus.emit("error", err);
return;
}
// write to port
sock.write(outTcp);
}
};
// send data back
if (responseBuffer) {
// remove crc and add mbap
var outTcp = Buffer.alloc(responseBuffer.length + 6 - 2);
outTcp.writeUInt16BE(transactionsId, 0);
outTcp.writeUInt16BE(0, 2);
outTcp.writeUInt16BE(responseBuffer.length - 2, 4);
responseBuffer.copy(outTcp, 6);
// parse the modbusRTU buffer
_parseModbusBuffer(requestBuffer, vector, cb);
modbusSerialDebug({ action: "send", data: responseBuffer });
modbusSerialDebug(JSON.stringify({ action: "send string", data: responseBuffer }));
// write to port
sock.write(outTcp);
}
};
// parse the modbusRTU buffer
setTimeout(
_parseModbusBuffer.bind(this,
requestBuffer,
vector,
serverUnitID,
sockWriter
),
0
);
}
});

@@ -759,0 +286,0 @@

@@ -8,3 +8,3 @@ "use strict";

describe("Modbus TCP Server", function() {
describe("Modbus TCP Server (no serverID)", function() {
var serverTCP; // eslint-disable-line no-unused-vars

@@ -38,5 +38,9 @@

};
serverTCP = new TcpServer(vector, { host: "0.0.0.0", port: 8512, debug: true, unitID: 1 });
serverTCP = new TcpServer(vector, { host: "0.0.0.0", port: 8512, debug: true });
});
after(function() {
serverTCP.close();
});
describe("function code handler", function() {

@@ -106,1 +110,73 @@ it("should receive a valid Modbus TCP message", function(done) {

});
describe("Modbus TCP Server (serverID = requestID)", function() {
var serverTCP; // eslint-disable-line no-unused-vars
before(function() {
var vector = {
setCoil: function(addr, value) {
console.log("set coil", addr, value);
return;
}
};
serverTCP = new TcpServer(vector, { host: "0.0.0.0", port: 8512, debug: true, unitID: 0x04 });
});
after(function() {
serverTCP.close();
});
describe("function code handler", function() {
it("should receive a valid Modbus TCP message", function(done) {
const client = net.connect({ host: "0.0.0.0", port: 8512 }, function() {
// FC05 - force single coil, to on 0xff00
client.write(Buffer.from("00010000000604050005ff00", "hex"));
});
client.once("data", function(data) {
// FC05 - valid responce
expect(data.toString("hex")).to.equal("00010000000604050005ff00");
done();
});
});
});
});
describe("Modbus TCP Server (serverID != requestID)", function() {
var serverTCP; // eslint-disable-line no-unused-vars
before(function() {
var vector = {
setCoil: function(addr, value) {
console.log("set coil", addr, value);
return;
}
};
serverTCP = new TcpServer(vector, { host: "0.0.0.0", port: 8512, debug: true, unitID: 0x04 });
});
after(function() {
serverTCP.close();
});
describe("function code handler", function() {
it("should receive a no Modbus TCP message for wrong unitID", function(done) {
var timeout;
this.timeout(1000 + 100);
const client = net.connect({ host: "0.0.0.0", port: 8512 }, function() {
// FC05 - force single coil, to on 0xff00
client.write(Buffer.from("00010000000603050005ff00", "hex"));
timeout = setTimeout(done, 1000);
});
client.once("data", function(data) {
clearTimeout(timeout);
// FC05 - we expect no data for wrong unitID
expect(data.toString("hex")).to.equal("NO DATA");
done();
});
});
});
});

@@ -13,3 +13,3 @@ "use strict";

var vector = {
getInputRegister: function(addr, callback) {
getInputRegister: function(addr, unit, callback) {
setTimeout(function() {

@@ -19,6 +19,8 @@ callback(null, addr);

},
getHoldingRegister: function(addr, callback) {
getHoldingRegister: function(addr, unit, callback) {
setTimeout(function() {
if (addr === 62)
return callback(new Error());
if (addr === 0x003E) {
callback(new Error());
return;
}

@@ -28,3 +30,3 @@ callback(null, addr + 8000);

},
getCoil: function(addr, callback) {
getCoil: function(addr, unit, callback) {
setTimeout(function() {

@@ -49,2 +51,6 @@ callback(null, (addr % 2) === 0);

after(function() {
serverTCP.close();
});
describe("function code handler", function() {

@@ -82,3 +88,3 @@ it("should receive a valid Modbus TCP message", function(done) {

it("should receive a valid slave failure Modbus TCP message", function(done) {
const client = net.connect({ host: "0.0.0.0", port: 8512 }, function() {
const client = net.connect({ host: "0.0.0.0", port: 8513 }, function() {
// FC03 to error triggering address

@@ -85,0 +91,0 @@ client.write(Buffer.from("0001000000060103003E0001", "hex"));

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

after(function() {
serverTCP.close();
});
describe("function code handler", function() {

@@ -90,3 +94,3 @@ it("should receive a valid Modbus TCP message", function(done) {

it("should receive a valid slave failure Modbus TCP message", function(done) {
const client = net.connect({ host: "0.0.0.0", port: 8512 }, function() {
const client = net.connect({ host: "0.0.0.0", port: 8514 }, function() {
// FC03 to error triggering address

@@ -93,0 +97,0 @@ client.write(Buffer.from("0001000000060103003E0001", "hex"));

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