simple-modbus
Version:
A simple library for working with Modbus with Typescript bindings.
845 lines (828 loc) • 40.4 kB
JavaScript
import net from 'net';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/* global Reflect, Promise */
var extendStatics = function(d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
function __extends(d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
/**
* This code is borrowed from Basarat Ali Syed from his Typescript Gitbook.
* Licensed under Creative Commons https://creativecommons.org/licenses/by/4.0/
*
* Passes through events as they happen. You will not get events from before you start listening
*/
var TypedEvent = /** @class */ (function () {
function TypedEvent() {
var _this = this;
this.listeners = [];
this.listenersOncer = [];
this.on = function (listener) {
_this.listeners.push(listener);
return {
dispose: function () { return _this.off(listener); }
};
};
this.once = function (listener) {
_this.listenersOncer.push(listener);
};
this.off = function (listener) {
var callbackIndex = _this.listeners.indexOf(listener);
if (callbackIndex > -1)
_this.listeners.splice(callbackIndex, 1);
};
this.emit = function (event) {
/** Update any general listeners */
_this.listeners.forEach(function (listener) { return listener(event); });
/** Clear the `once` queue */
_this.listenersOncer.forEach(function (listener) { return listener(event); });
_this.listenersOncer = [];
};
this.pipe = function (te) {
return _this.on(function (e) { return te.emit(e); });
};
}
return TypedEvent;
}());
/* istanbul ignore next */
var ModbusServer = /** @class */ (function () {
function ModbusServer() {
this.onReadCoilStatus = new TypedEvent();
this.onReadInputStatus = new TypedEvent();
this.onReadHoldingRegisters = new TypedEvent();
this.onReadInputRegisters = new TypedEvent();
this.onForceSingleCoil = new TypedEvent();
this.onPresetSingleRegister = new TypedEvent();
this.onForceMultipleCoils = new TypedEvent();
this.onPresetMultipleRegisters = new TypedEvent();
this.onCommandError = new TypedEvent();
this.onServerError = new TypedEvent();
}
return ModbusServer;
}());
// export class ModbusServerError extends Error {
var ModbusCommandError = /** @class */ (function (_super) {
__extends(ModbusCommandError, _super);
// Impossible to get Jest to see super branch as covered, have to ignore whole constructor
/* istanbul ignore next */
function ModbusCommandError(message, requestBytes) {
var _this = _super.call(this, message) || this;
_this.message = message;
_this.name = 'ModbusCommandError';
_this.message = message;
_this.stack = (new Error()).stack;
_this.requestBytes = requestBytes;
Object.setPrototypeOf(_this, ModbusCommandError.prototype);
return _this;
}
ModbusCommandError.prototype.toString = function () {
return this.name + ': ' + this.message;
};
return ModbusCommandError;
}(Error));
var ModbusFunctionCode;
(function (ModbusFunctionCode) {
ModbusFunctionCode[ModbusFunctionCode["READ_COIL_STATUS"] = 1] = "READ_COIL_STATUS";
ModbusFunctionCode[ModbusFunctionCode["READ_INPUT_STATUS"] = 2] = "READ_INPUT_STATUS";
ModbusFunctionCode[ModbusFunctionCode["READ_HOLDING_REGISTERS"] = 3] = "READ_HOLDING_REGISTERS";
ModbusFunctionCode[ModbusFunctionCode["READ_INPUT_REGISTERS"] = 4] = "READ_INPUT_REGISTERS";
ModbusFunctionCode[ModbusFunctionCode["FORCE_SINGLE_COIL"] = 5] = "FORCE_SINGLE_COIL";
ModbusFunctionCode[ModbusFunctionCode["PRESET_SINGLE_REGISTER"] = 6] = "PRESET_SINGLE_REGISTER";
ModbusFunctionCode[ModbusFunctionCode["FORCE_MULTIPLE_COILS"] = 15] = "FORCE_MULTIPLE_COILS";
ModbusFunctionCode[ModbusFunctionCode["PRESET_MULTIPLE_REGISTERS"] = 16] = "PRESET_MULTIPLE_REGISTERS";
})(ModbusFunctionCode || (ModbusFunctionCode = {}));
var ModbusCommandException;
(function (ModbusCommandException) {
ModbusCommandException[ModbusCommandException["ILLEGAL_FUNCTION"] = 1] = "ILLEGAL_FUNCTION";
ModbusCommandException[ModbusCommandException["ILLEGAL_DATA_ADDRESS"] = 2] = "ILLEGAL_DATA_ADDRESS";
ModbusCommandException[ModbusCommandException["ILLEGAL_DATA_VALUE"] = 3] = "ILLEGAL_DATA_VALUE";
ModbusCommandException[ModbusCommandException["SERVER_DEVICE_FAILURE"] = 4] = "SERVER_DEVICE_FAILURE";
ModbusCommandException[ModbusCommandException["ACKNOWLEDGE"] = 5] = "ACKNOWLEDGE";
ModbusCommandException[ModbusCommandException["SERVER_DEVICE_BUSY"] = 6] = "SERVER_DEVICE_BUSY";
ModbusCommandException[ModbusCommandException["NEGATIVE_ACKNOWLEDGE"] = 7] = "NEGATIVE_ACKNOWLEDGE";
ModbusCommandException[ModbusCommandException["MEMORY_PARITY_ERROR"] = 8] = "MEMORY_PARITY_ERROR";
ModbusCommandException[ModbusCommandException["GATEWAY_PATH_UNAVAILABLE"] = 10] = "GATEWAY_PATH_UNAVAILABLE";
ModbusCommandException[ModbusCommandException["GATEWAY_TARGET_FAILED_TO_RESPOND"] = 11] = "GATEWAY_TARGET_FAILED_TO_RESPOND";
})(ModbusCommandException || (ModbusCommandException = {}));
var CoilStatus;
(function (CoilStatus) {
CoilStatus[CoilStatus["ON"] = 0] = "ON";
CoilStatus[CoilStatus["OFF"] = 1] = "OFF";
})(CoilStatus || (CoilStatus = {}));
var ModbusCommand = /** @class */ (function () {
function ModbusCommand(rawPacket, unitIdGetter, functionCodeGetter, successGetter, failureGetter) {
/**
* Fires on either success or failure, with the response bytes. Mainly used by the server to send a response.
*/
this.onComplete = new TypedEvent();
/**
* Fires on a call of the success method.
*/
this.onSuccess = new TypedEvent();
/**
* Fires on a call of the fail method.
*/
this.onFailure = new TypedEvent();
this._rawPacket = rawPacket;
this._unitIdGetter = unitIdGetter;
this._functionCodeGetter = functionCodeGetter;
this._successGetter = successGetter;
this._failureGetter = failureGetter;
}
Object.defineProperty(ModbusCommand.prototype, "unitId", {
/**
* If RTU, unitId is equivalent to slaveId
*/
get: function () {
return this._unitIdGetter(this._rawPacket);
},
enumerable: true,
configurable: true
});
Object.defineProperty(ModbusCommand.prototype, "functionCode", {
/**
* Modbus function code
*/
get: function () {
return this._functionCodeGetter(this._rawPacket);
},
enumerable: true,
configurable: true
});
Object.defineProperty(ModbusCommand.prototype, "responsePacket", {
/**
* This function will give you the response packet bytes that will be sent on the emitting server. Before calling this function, the `success` or `fail` method must be called in order to set the response.
*
* @returns A buffer of the bytes representing the response to the server
* @throws ModbusCommandError if success or fail hasn't been called yet
*/
get: function () {
if (!this._responsePacket) {
throw new ModbusCommandError('Tried to read response packet, but success or fail has not been called.', this._rawPacket);
}
return this._responsePacket;
},
enumerable: true,
configurable: true
});
/**
* Set a failure on this command to return an exception response to the emitting server.
*
* @param exception - The reason for the failure
*/
ModbusCommand.prototype.fail = function (exception) {
this._responsePacket = this._failureGetter(this._rawPacket, exception);
this.onComplete.emit(this);
this.onFailure.emit(this);
};
return ModbusCommand;
}());
var ReadCoilStatusCommand = /** @class */ (function (_super) {
__extends(ReadCoilStatusCommand, _super);
/**
* @hidden
*/
function ReadCoilStatusCommand(rawPacket, unitIdGetter, functionCodeGetter, successGetter, failureGetter, coilAddressGetter, coilLengthGetter) {
var _this = _super.call(this, rawPacket, unitIdGetter, functionCodeGetter, successGetter, failureGetter) || this;
_this._coilAddressGetter = coilAddressGetter;
_this._coilLengthGetter = coilLengthGetter;
return _this;
}
Object.defineProperty(ReadCoilStatusCommand.prototype, "coilStartAddress", {
get: function () {
return this._coilAddressGetter(this._rawPacket);
},
enumerable: true,
configurable: true
});
Object.defineProperty(ReadCoilStatusCommand.prototype, "numberOfCoils", {
get: function () {
return this._coilLengthGetter(this._rawPacket);
},
enumerable: true,
configurable: true
});
/**
* Set success on this command to return a valid response to the emitting server.
*
* @param data - Boolean coil data, starting at `coilStartAddress`, of length `numberOfCoils`.
*/
ReadCoilStatusCommand.prototype.success = function (data) {
// TODO: Throw error here if data length doesn't equal requested length
this._responsePacket = this._successGetter(this._rawPacket, data);
this.onComplete.emit(this);
this.onSuccess.emit(this);
};
return ReadCoilStatusCommand;
}(ModbusCommand));
var ReadInputStatusCommand = /** @class */ (function (_super) {
__extends(ReadInputStatusCommand, _super);
/**
* @hidden
*/
function ReadInputStatusCommand(rawPacket, unitIdGetter, functionCodeGetter, successGetter, failureGetter, inputAddressGetter, inputLengthGetter) {
var _this = _super.call(this, rawPacket, unitIdGetter, functionCodeGetter, successGetter, failureGetter) || this;
_this._inputAddressGetter = inputAddressGetter;
_this._inputLengthGetter = inputLengthGetter;
return _this;
}
Object.defineProperty(ReadInputStatusCommand.prototype, "inputStartAddress", {
get: function () {
return this._inputAddressGetter(this._rawPacket);
},
enumerable: true,
configurable: true
});
Object.defineProperty(ReadInputStatusCommand.prototype, "numberOfInputs", {
get: function () {
return this._inputLengthGetter(this._rawPacket);
},
enumerable: true,
configurable: true
});
/**
* Set success on this command to return a valid response to the emitting server.
*
* @param data - Input status data of requested discrete inputs. `true` = ON, `false` = off
*/
ReadInputStatusCommand.prototype.success = function (data) {
// TODO: Throw error here if data length doesn't equal requested length
this._responsePacket = this._successGetter(this._rawPacket, data);
this.onComplete.emit(this);
this.onSuccess.emit(this);
};
return ReadInputStatusCommand;
}(ModbusCommand));
var ReadHoldingRegistersCommand = /** @class */ (function (_super) {
__extends(ReadHoldingRegistersCommand, _super);
/**
* @hidden
*/
function ReadHoldingRegistersCommand(rawPacket, unitIdGetter, functionCodeGetter, successGetter, failureGetter, registerAddressGetter, registerLengthGetter) {
var _this = _super.call(this, rawPacket, unitIdGetter, functionCodeGetter, successGetter, failureGetter) || this;
_this._registerAddressGetter = registerAddressGetter;
_this._registerLengthGetter = registerLengthGetter;
return _this;
}
Object.defineProperty(ReadHoldingRegistersCommand.prototype, "registerStartAddress", {
get: function () {
return this._registerAddressGetter(this._rawPacket);
},
enumerable: true,
configurable: true
});
Object.defineProperty(ReadHoldingRegistersCommand.prototype, "registerLength", {
get: function () {
return this._registerLengthGetter(this._rawPacket);
},
enumerable: true,
configurable: true
});
/**
* Set success on this command to return a valid response to the emitting server.
*
* @param data - Array of values of the requested holding registers. Register values are 16 bits. Array length must equal `registerLength`. `data[0]` should be the value of the register at `registerStartAddress`.
*/
ReadHoldingRegistersCommand.prototype.success = function (data) {
// TODO: Throw error here if data length doesn't equal requested length
this._responsePacket = this._successGetter(this._rawPacket, data);
this.onComplete.emit(this);
this.onSuccess.emit(this);
};
return ReadHoldingRegistersCommand;
}(ModbusCommand));
var ReadInputRegistersCommand = /** @class */ (function (_super) {
__extends(ReadInputRegistersCommand, _super);
/**
* @hidden
*/
function ReadInputRegistersCommand(rawPacket, unitIdGetter, functionCodeGetter, successGetter, failureGetter, registerAddressGetter, registerLengthGetter) {
var _this = _super.call(this, rawPacket, unitIdGetter, functionCodeGetter, successGetter, failureGetter) || this;
_this._registerAddressGetter = registerAddressGetter;
_this._registerLengthGetter = registerLengthGetter;
return _this;
}
Object.defineProperty(ReadInputRegistersCommand.prototype, "registerStartAddress", {
get: function () {
return this._registerAddressGetter(this._rawPacket);
},
enumerable: true,
configurable: true
});
Object.defineProperty(ReadInputRegistersCommand.prototype, "registerLength", {
get: function () {
return this._registerLengthGetter(this._rawPacket);
},
enumerable: true,
configurable: true
});
/**
* Set success on this command to return a valid response to the emitting server.
*
* @param data - Array of values of the requested input registers. Register values are 16 bits. Array length must equal `registerLength`. `data[0]` should be the value of the register at `registerStartAddress`.
*/
ReadInputRegistersCommand.prototype.success = function (data) {
// TODO: Throw error here if data length doesn't equal requested length
this._responsePacket = this._successGetter(this._rawPacket, data);
this.onComplete.emit(this);
this.onSuccess.emit(this);
};
return ReadInputRegistersCommand;
}(ModbusCommand));
var ForceSingleCoilCommand = /** @class */ (function (_super) {
__extends(ForceSingleCoilCommand, _super);
/**
* @hidden
*/
function ForceSingleCoilCommand(rawPacket, unitIdGetter, functionCodeGetter, successGetter, failureGetter, coilAddressGetter, coilStatusGetter) {
var _this = _super.call(this, rawPacket, unitIdGetter, functionCodeGetter, successGetter, failureGetter) || this;
_this._coilAddressGetter = coilAddressGetter;
_this._coilStatusGetter = coilStatusGetter;
return _this;
}
Object.defineProperty(ForceSingleCoilCommand.prototype, "coilAddress", {
get: function () {
return this._coilAddressGetter(this._rawPacket);
},
enumerable: true,
configurable: true
});
Object.defineProperty(ForceSingleCoilCommand.prototype, "coilStatus", {
get: function () {
return this._coilStatusGetter(this._rawPacket);
},
enumerable: true,
configurable: true
});
Object.defineProperty(ForceSingleCoilCommand.prototype, "coilStatusAsCoilStatus", {
get: function () {
return this.coilStatus === true ? CoilStatus.ON : CoilStatus.OFF;
},
enumerable: true,
configurable: true
});
/**
* Set success on this command to return a valid response to the emitting server.
*/
ForceSingleCoilCommand.prototype.success = function () {
this._responsePacket = this._successGetter(this._rawPacket);
this.onComplete.emit(this);
this.onSuccess.emit(this);
};
return ForceSingleCoilCommand;
}(ModbusCommand));
var PresetSingleRegisterCommand = /** @class */ (function (_super) {
__extends(PresetSingleRegisterCommand, _super);
/**
* @hidden
*/
function PresetSingleRegisterCommand(rawPacket, unitIdGetter, functionCodeGetter, successGetter, failureGetter, registerAddressGetter, registerValueGetter) {
var _this = _super.call(this, rawPacket, unitIdGetter, functionCodeGetter, successGetter, failureGetter) || this;
_this._registerAddressGetter = registerAddressGetter;
_this._registerValueGetter = registerValueGetter;
return _this;
}
Object.defineProperty(PresetSingleRegisterCommand.prototype, "registerAddress", {
get: function () {
return this._registerAddressGetter(this._rawPacket);
},
enumerable: true,
configurable: true
});
Object.defineProperty(PresetSingleRegisterCommand.prototype, "registerValue", {
get: function () {
return this._registerValueGetter(this._rawPacket);
},
enumerable: true,
configurable: true
});
/**
* Set success on this command to return a valid response to the emitting server.
*/
PresetSingleRegisterCommand.prototype.success = function () {
this._responsePacket = this._successGetter(this._rawPacket);
this.onComplete.emit(this);
this.onSuccess.emit(this);
};
return PresetSingleRegisterCommand;
}(ModbusCommand));
var ForceMultipleCoilsCommand = /** @class */ (function (_super) {
__extends(ForceMultipleCoilsCommand, _super);
/**
* @hidden
*/
function ForceMultipleCoilsCommand(rawPacket, unitIdGetter, functionCodeGetter, successGetter, failureGetter, coilAddressGetter, coilLengthGetter, coilStatusesGetter) {
var _this = _super.call(this, rawPacket, unitIdGetter, functionCodeGetter, successGetter, failureGetter) || this;
_this._coilAddressGetter = coilAddressGetter;
_this._coilLengthGetter = coilLengthGetter;
_this._coilStatusesGetter = coilStatusesGetter;
return _this;
}
Object.defineProperty(ForceMultipleCoilsCommand.prototype, "coilStartAddress", {
get: function () {
return this._coilAddressGetter(this._rawPacket);
},
enumerable: true,
configurable: true
});
Object.defineProperty(ForceMultipleCoilsCommand.prototype, "coilLength", {
get: function () {
return this._coilLengthGetter(this._rawPacket);
},
enumerable: true,
configurable: true
});
Object.defineProperty(ForceMultipleCoilsCommand.prototype, "coilStatuses", {
get: function () {
return this._coilStatusesGetter(this._rawPacket);
},
enumerable: true,
configurable: true
});
Object.defineProperty(ForceMultipleCoilsCommand.prototype, "coilStatusesAsCoilStatusArray", {
get: function () {
return this.coilStatuses.map(function (x) { return (x === true ? CoilStatus.ON : CoilStatus.OFF); });
},
enumerable: true,
configurable: true
});
/**
* Set success on this command to return a valid response to the emitting server.
*/
ForceMultipleCoilsCommand.prototype.success = function () {
this._responsePacket = this._successGetter(this._rawPacket);
this.onComplete.emit(this);
this.onSuccess.emit(this);
};
return ForceMultipleCoilsCommand;
}(ModbusCommand));
var PresetMultipleRegistersCommand = /** @class */ (function (_super) {
__extends(PresetMultipleRegistersCommand, _super);
/**
* @hidden
*/
function PresetMultipleRegistersCommand(rawPacket, unitIdGetter, functionCodeGetter, successGetter, failureGetter, registerAddressGetter, registerLengthGetter, registerValuesGetter) {
var _this = _super.call(this, rawPacket, unitIdGetter, functionCodeGetter, successGetter, failureGetter) || this;
_this._registerAddressGetter = registerAddressGetter;
_this._registerLengthGetter = registerLengthGetter;
_this._registerValuesGetter = registerValuesGetter;
return _this;
}
Object.defineProperty(PresetMultipleRegistersCommand.prototype, "registerStartAddress", {
get: function () {
return this._registerAddressGetter(this._rawPacket);
},
enumerable: true,
configurable: true
});
Object.defineProperty(PresetMultipleRegistersCommand.prototype, "registerLength", {
get: function () {
return this._registerLengthGetter(this._rawPacket);
},
enumerable: true,
configurable: true
});
Object.defineProperty(PresetMultipleRegistersCommand.prototype, "registerValues", {
get: function () {
return this._registerValuesGetter(this._rawPacket);
},
enumerable: true,
configurable: true
});
Object.defineProperty(PresetMultipleRegistersCommand.prototype, "registerValuesAsUint16Array", {
get: function () {
return new Uint16Array(this.registerValues);
},
enumerable: true,
configurable: true
});
/**
* Set success on this command to return a valid response to the emitting server.
*/
PresetMultipleRegistersCommand.prototype.success = function () {
this._responsePacket = this._successGetter(this._rawPacket);
this.onComplete.emit(this);
this.onSuccess.emit(this);
};
return PresetMultipleRegistersCommand;
}(ModbusCommand));
var ModbusCommandFactory = /** @class */ (function () {
function ModbusCommandFactory(options) {
this.simpleAddressing = true;
if (options !== undefined) {
if (options.simpleAddressing === false) {
this.simpleAddressing = false;
}
}
}
return ModbusCommandFactory;
}());
var ModbusTcpCommandFactory = /** @class */ (function (_super) {
__extends(ModbusTcpCommandFactory, _super);
function ModbusTcpCommandFactory(options) {
var _this = _super.call(this, options) || this;
_this._unitIdGetter = (function (requestPacket) {
return requestPacket.readUInt8(6);
});
_this._functionCodeGetter = (function (requestPacket) {
return requestPacket.readUInt8(7);
});
_this._packetCopySuccessGetter = (function (requestPacket) { return Buffer.from(requestPacket); });
_this._forceMultipleCoilsSuccessGetter = (function (requestPacket) {
var response = Array.from(requestPacket).slice(0, 12);
response[4] = 0x00;
response[5] = 0x06;
return Buffer.from(response);
});
_this._failureGetter = (function (requestPacket, exception) {
var response = [];
// First 2 bytes are the Transaction Identifier
response[0] = requestPacket.readUInt8(0);
response[1] = requestPacket.readUInt8(1);
// Next 2 bytes are protocol ID. These should always be 0x0000. But the protocol says to copy them from the request.
response[2] = requestPacket.readUInt8(2);
response[3] = requestPacket.readUInt8(3);
// Failure length is always constant
response[4] = 0x00;
response[5] = 0x03;
// Copy UnitId from request
response[6] = requestPacket.readUInt8(6);
// Function code is request function code with highest bit set
response[7] = requestPacket.readUInt8(7) | 128;
response[8] = exception;
return Buffer.from(new Uint8Array(response));
});
_this._presetMultipleRegistersSuccessGetter = (function (requestPacket) {
var response = Array.from(requestPacket).slice(0, 12);
response[4] = 0x00;
response[5] = 0x06;
return Buffer.from(response);
});
_this._readCoilSuccessGetter = function (requestPacket, data) {
var response = ModbusTcpCommandFactory._stubTcpHeader(requestPacket);
// Calculate number of bytes with coil data in response
var coilsRequested = _this._coilLengthGetter(requestPacket);
var byteLength = Math.ceil(coilsRequested / 8.0);
response[8] = byteLength;
// TCP byte length data
response[4] = (byteLength + 3) >> 8;
response[5] = (byteLength + 3) & 0xFF;
// Pad array with false at end to end on an 8 bit boundary
var paddedData = data.concat((new Array(8 - (coilsRequested % 8)).fill(false)));
for (var i = 0; i < byteLength; i++) {
// Take a slice of the array of length 8, reverse it, then fill the accumulator with it (starting from right)
response[9 + i] = paddedData.slice(i * 8, 8 + (i * 8)).reduce(function (accumulator, currentValue, currentIndex) { return accumulator | ((currentValue ? 1 : 0) << currentIndex); }, 0x00);
}
return Buffer.from(new Uint8Array(response));
};
_this._readInputStatusSuccessGetter = function (requestPacket, data) {
var response = ModbusTcpCommandFactory._stubTcpHeader(requestPacket);
// Calculate number of bytes with coil data in response
var inputsRequested = _this._inputLengthGetter(requestPacket);
var byteLength = Math.ceil(inputsRequested / 8.0);
response[8] = byteLength;
// TCP byte length data
response[4] = (byteLength + 3) >> 8;
response[5] = (byteLength + 3) & 0xFF;
// Pad array with false at end to end on an 8 bit boundary
var paddedData = data.concat((new Array(8 - (inputsRequested % 8)).fill(false)));
for (var i = 0; i < byteLength; i++) {
// Take a slice of the array of length 8, and fill the accumulator with it (starting from right)
response[9 + i] = paddedData.slice(i * 8, 8 + (i * 8)).reduce(function (accumulator, currentValue, currentIndex) { return accumulator | ((currentValue ? 1 : 0) << currentIndex); }, 0x00);
}
return Buffer.from(new Uint8Array(response));
};
_this._readRegistersSuccessGetter = function (requestPacket, data) {
var response = ModbusTcpCommandFactory._stubTcpHeader(requestPacket);
// Calculate number of bytes with coil data in response
var registersRequested = _this._registerLengthGetter(requestPacket);
var byteLength = registersRequested * 2;
response[8] = byteLength;
// TCP byte length data
response[4] = (byteLength + 3) >> 8;
response[5] = (byteLength + 3) & 0xFF;
for (var i = 0; i < registersRequested; i++) {
response[9 + (i * 2)] = data[i] >> 8;
response[10 + (i * 2)] = data[i] & 0xFF;
}
return Buffer.from(new Uint8Array(response));
};
_this._holdingRegisterAddressGetter = (function (requestPacket) {
return _this.simpleAddressing ? requestPacket.readUInt16BE(8) : requestPacket.readUInt16BE(8) + 40001;
});
_this._inputRegisterAddressGetter = (function (requestPacket) {
return _this.simpleAddressing ? requestPacket.readUInt16BE(8) : requestPacket.readUInt16BE(8) + 30001;
});
_this._registerValueGetter = (function (requestPacket) {
return requestPacket.readUInt16BE(10);
});
_this._registerLengthGetter = (function (requestPacket) {
return requestPacket.readUInt16BE(10);
});
_this._coilAddressGetter = (function (requestPacket) {
return _this.simpleAddressing ? requestPacket.readUInt16BE(8) : requestPacket.readUInt16BE(8) + 1;
});
_this._coilLengthGetter = (function (requestPacket) {
return requestPacket.readUInt16BE(10);
});
_this._coilStatusGetter = (function (requestPacket) {
var value = requestPacket.readUInt16BE(10);
if (value === 0xFF00) {
return true;
}
else if (value === 0x0000) {
return false;
}
throw new Error('_coilStatusGetter invalid value');
});
_this._coilStatusesGetter = function (requestPacket) {
var coilLength = _this._coilLengthGetter(requestPacket);
var byteLength = Math.ceil(coilLength / 8.0);
var packetByteLength = requestPacket.readUInt8(12);
if (byteLength !== packetByteLength || requestPacket.length !== byteLength + 13) {
// Malformed packet, check and throw exception
throw new Error('_coilStatusesGetter invalid length');
}
var coilArray = new Array(byteLength * 8);
for (var i = 0; i < byteLength; i++) {
var byteVal = requestPacket.readUInt8(13 + i);
for (var j = 0; j < 8; j++) {
coilArray[(i * 8) + j] = ((byteVal >> j) & 0x01) === 1;
}
}
return coilArray.slice(0, coilLength);
};
_this._registerValuesGetter = function (requestPacket) {
var registerLength = _this._registerLengthGetter(requestPacket);
var byteLength = registerLength * 2;
var packetByteLength = requestPacket.readUInt8(12);
if (byteLength !== packetByteLength || requestPacket.length !== byteLength + 13) {
// Malformed packet, check and throw exception
throw new Error('_registerValuesGetter invalidValue');
}
var registerArray = new Array(registerLength);
for (var i = 0; i < registerLength; i++) {
registerArray[i] = requestPacket.readUInt16BE(13 + (i * 2));
}
return registerArray;
};
_this._inputAddressGetter = (function (requestPacket) {
return _this.simpleAddressing ? requestPacket.readUInt16BE(8) : requestPacket.readUInt16BE(8) + 10001;
});
_this._inputLengthGetter = (function (requestPacket) {
return requestPacket.readUInt16BE(10);
});
_this._options = options;
return _this;
}
ModbusTcpCommandFactory._stubTcpHeader = function (requestPacket) {
var response = [];
// First 2 bytes are the Transaction Identifier
response[0] = requestPacket.readUInt8(0);
response[1] = requestPacket.readUInt8(1);
// Next 2 bytes are protocol ID. These should always be 0x0000. But the protocol says to copy them from the request.
response[2] = requestPacket.readUInt8(2);
response[3] = requestPacket.readUInt8(3);
// Copy UnitId from request
response[6] = requestPacket.readUInt8(6);
// Copy Function Code from request
response[7] = requestPacket.readUInt8(7);
return response;
};
ModbusTcpCommandFactory.prototype.fromPacket = function (packet) {
// Minimum Modbus TCP request packet size is 12
if (packet.length < 12) {
throw new ModbusCommandError('Packet length too short', packet);
}
var fc = this._functionCodeGetter(packet);
// Determine packet type, and call appropriate constructor
switch (fc) {
case ModbusFunctionCode.READ_COIL_STATUS:
return new ReadCoilStatusCommand(packet, this._unitIdGetter, this._functionCodeGetter, this._readCoilSuccessGetter, this._failureGetter, this._coilAddressGetter, this._coilLengthGetter);
case ModbusFunctionCode.READ_INPUT_STATUS:
return new ReadInputStatusCommand(packet, this._unitIdGetter, this._functionCodeGetter, this._readInputStatusSuccessGetter, this._failureGetter, this._inputAddressGetter, this._inputLengthGetter);
case ModbusFunctionCode.READ_HOLDING_REGISTERS:
return new ReadHoldingRegistersCommand(packet, this._unitIdGetter, this._functionCodeGetter, this._readRegistersSuccessGetter, this._failureGetter, this._holdingRegisterAddressGetter, this._registerLengthGetter);
case ModbusFunctionCode.READ_INPUT_REGISTERS:
return new ReadInputRegistersCommand(packet, this._unitIdGetter, this._functionCodeGetter, this._readRegistersSuccessGetter, this._failureGetter, this._inputRegisterAddressGetter, this._registerLengthGetter);
case ModbusFunctionCode.FORCE_SINGLE_COIL:
try {
this._coilStatusGetter(packet);
}
catch (_a) {
throw new ModbusCommandError('FORCE_SINGLE_COIL - Invalid coil status received', packet);
}
return new ForceSingleCoilCommand(packet, this._unitIdGetter, this._functionCodeGetter, this._packetCopySuccessGetter, this._failureGetter, this._coilAddressGetter, this._coilStatusGetter);
case ModbusFunctionCode.PRESET_SINGLE_REGISTER:
return new PresetSingleRegisterCommand(packet, this._unitIdGetter, this._functionCodeGetter, this._packetCopySuccessGetter, this._failureGetter, this._holdingRegisterAddressGetter, this._registerValueGetter);
case ModbusFunctionCode.FORCE_MULTIPLE_COILS:
try {
this._coilStatusesGetter(packet);
}
catch (_b) {
throw new ModbusCommandError('FORCE_MULTIPLE_COILS - Invalid coil status command received', packet);
}
return new ForceMultipleCoilsCommand(packet, this._unitIdGetter, this._functionCodeGetter, this._forceMultipleCoilsSuccessGetter, this._failureGetter, this._coilAddressGetter, this._coilLengthGetter, this._coilStatusesGetter);
case ModbusFunctionCode.PRESET_MULTIPLE_REGISTERS:
try {
this._registerValuesGetter(packet);
}
catch (_c) {
throw new ModbusCommandError('PRESET_MULTIPLE_REGISTERS - Invalid register command received', packet);
}
return new PresetMultipleRegistersCommand(packet, this._unitIdGetter, this._functionCodeGetter, this._presetMultipleRegistersSuccessGetter, this._failureGetter, this._holdingRegisterAddressGetter, this._registerLengthGetter, this._registerValuesGetter);
default:
throw new ModbusCommandError('Function code not implemented', packet);
}
};
return ModbusTcpCommandFactory;
}(ModbusCommandFactory));
var ModbusTcpServer = /** @class */ (function (_super) {
__extends(ModbusTcpServer, _super);
function ModbusTcpServer(options) {
var _this_1 = _super.call(this) || this;
_this_1._commandFactory = new ModbusTcpCommandFactory();
_this_1._options = options;
_this_1._commandFactory = new ModbusTcpCommandFactory(options);
_this_1._tcpServer = net.createServer(function (socket) {
var _this = _this_1;
socket.on('data', function (data) {
try {
// Build object from packet
var command = _this_1._commandFactory.fromPacket(data);
// Listen for success or failure events being emitted from command object
command.onComplete.once(function (command) {
socket.write(command.responsePacket);
});
// Determine packet type and emit corresponding event type
switch (command.functionCode) {
case ModbusFunctionCode.READ_COIL_STATUS:
_this.onReadCoilStatus.emit(command);
break;
case ModbusFunctionCode.READ_INPUT_STATUS:
_this.onReadInputStatus.emit(command);
break;
case ModbusFunctionCode.READ_HOLDING_REGISTERS:
_this.onReadHoldingRegisters.emit(command);
break;
case ModbusFunctionCode.READ_INPUT_REGISTERS:
_this.onReadInputRegisters.emit(command);
break;
case ModbusFunctionCode.FORCE_SINGLE_COIL:
_this.onForceSingleCoil.emit(command);
break;
case ModbusFunctionCode.PRESET_SINGLE_REGISTER:
_this.onPresetSingleRegister.emit(command);
break;
case ModbusFunctionCode.FORCE_MULTIPLE_COILS:
_this.onForceMultipleCoils.emit(command);
break;
case ModbusFunctionCode.PRESET_MULTIPLE_REGISTERS:
_this.onPresetMultipleRegisters.emit(command);
break;
}
}
catch (e) {
// TODO: Explicit typeguard here, look into changing from try/catch
_this.onCommandError.emit(e);
}
});
socket.on('error', function (e) {
_this.onServerError.emit(e);
});
});
return _this_1;
}
ModbusTcpServer.prototype.listen = function (port) {
this._tcpServer.listen(port);
return this;
};
ModbusTcpServer.prototype.close = function () {
this._tcpServer.close();
return this;
};
return ModbusTcpServer;
}(ModbusServer));
var index = /*#__PURE__*/Object.freeze({
Server: ModbusTcpServer,
CommandFactory: ModbusTcpCommandFactory
});
export { index as ModbusTcp, ModbusFunctionCode, ModbusCommandException };
//# sourceMappingURL=simple-modbus.esm.js.map