simple-modbus
Version:
A simple library for working with Modbus with Typescript bindings.
240 lines • 14.2 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || (function () {
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);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var modbus_commands_1 = require("../modbus-commands");
var modbus_errors_1 = require("../error/modbus-errors");
var modbus_command_factory_1 = require("../modbus-command-factory");
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 modbus_errors_1.ModbusCommandError('Packet length too short', packet);
}
var fc = this._functionCodeGetter(packet);
// Determine packet type, and call appropriate constructor
switch (fc) {
case modbus_commands_1.ModbusFunctionCode.READ_COIL_STATUS:
return new modbus_commands_1.ReadCoilStatusCommand(packet, this._unitIdGetter, this._functionCodeGetter, this._readCoilSuccessGetter, this._failureGetter, this._coilAddressGetter, this._coilLengthGetter);
case modbus_commands_1.ModbusFunctionCode.READ_INPUT_STATUS:
return new modbus_commands_1.ReadInputStatusCommand(packet, this._unitIdGetter, this._functionCodeGetter, this._readInputStatusSuccessGetter, this._failureGetter, this._inputAddressGetter, this._inputLengthGetter);
case modbus_commands_1.ModbusFunctionCode.READ_HOLDING_REGISTERS:
return new modbus_commands_1.ReadHoldingRegistersCommand(packet, this._unitIdGetter, this._functionCodeGetter, this._readRegistersSuccessGetter, this._failureGetter, this._holdingRegisterAddressGetter, this._registerLengthGetter);
case modbus_commands_1.ModbusFunctionCode.READ_INPUT_REGISTERS:
return new modbus_commands_1.ReadInputRegistersCommand(packet, this._unitIdGetter, this._functionCodeGetter, this._readRegistersSuccessGetter, this._failureGetter, this._inputRegisterAddressGetter, this._registerLengthGetter);
case modbus_commands_1.ModbusFunctionCode.FORCE_SINGLE_COIL:
try {
this._coilStatusGetter(packet);
}
catch (_a) {
throw new modbus_errors_1.ModbusCommandError('FORCE_SINGLE_COIL - Invalid coil status received', packet);
}
return new modbus_commands_1.ForceSingleCoilCommand(packet, this._unitIdGetter, this._functionCodeGetter, this._packetCopySuccessGetter, this._failureGetter, this._coilAddressGetter, this._coilStatusGetter);
case modbus_commands_1.ModbusFunctionCode.PRESET_SINGLE_REGISTER:
return new modbus_commands_1.PresetSingleRegisterCommand(packet, this._unitIdGetter, this._functionCodeGetter, this._packetCopySuccessGetter, this._failureGetter, this._holdingRegisterAddressGetter, this._registerValueGetter);
case modbus_commands_1.ModbusFunctionCode.FORCE_MULTIPLE_COILS:
try {
this._coilStatusesGetter(packet);
}
catch (_b) {
throw new modbus_errors_1.ModbusCommandError('FORCE_MULTIPLE_COILS - Invalid coil status command received', packet);
}
return new modbus_commands_1.ForceMultipleCoilsCommand(packet, this._unitIdGetter, this._functionCodeGetter, this._forceMultipleCoilsSuccessGetter, this._failureGetter, this._coilAddressGetter, this._coilLengthGetter, this._coilStatusesGetter);
case modbus_commands_1.ModbusFunctionCode.PRESET_MULTIPLE_REGISTERS:
try {
this._registerValuesGetter(packet);
}
catch (_c) {
throw new modbus_errors_1.ModbusCommandError('PRESET_MULTIPLE_REGISTERS - Invalid register command received', packet);
}
return new modbus_commands_1.PresetMultipleRegistersCommand(packet, this._unitIdGetter, this._functionCodeGetter, this._presetMultipleRegistersSuccessGetter, this._failureGetter, this._holdingRegisterAddressGetter, this._registerLengthGetter, this._registerValuesGetter);
default:
throw new modbus_errors_1.ModbusCommandError('Function code not implemented', packet);
}
};
return ModbusTcpCommandFactory;
}(modbus_command_factory_1.ModbusCommandFactory));
exports.ModbusTcpCommandFactory = ModbusTcpCommandFactory;
//# sourceMappingURL=modbus-tcp-command-factory.js.map