├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── lib ├── Buffer.js ├── Exception.js ├── Helpers.js ├── Modbus.js └── protocol │ ├── GET_COMM_EVENT_COUNTER.js │ ├── GET_COMM_EVENT_LOG.js │ ├── MASK_WRITE_REGISTER.js │ ├── READ_COILS.js │ ├── READ_DEVICE_IDENTIFICATION.js │ ├── READ_DISCRETE_INPUTS.js │ ├── READ_EXCEPTION_STATUS.js │ ├── READ_FIFO_QUEUE.js │ ├── READ_FILE_RECORD.js │ ├── READ_HOLDING_REGISTERS.js │ ├── READ_INPUT_REGISTERS.js │ ├── READ_WRITE_MULTIPLE_REGISTERS.js │ ├── WRITE_FILE_RECORD.js │ ├── WRITE_MULTIPLE_COILS.js │ ├── WRITE_MULTIPLE_REGISTERS.js │ ├── WRITE_SINGLE_COIL.js │ └── WRITE_SINGLE_REGISTER.js ├── package.json └── test ├── help.js ├── integration ├── exceptions.js ├── get-comm-event-counter.js ├── get-comm-event-log.js ├── helpers.js ├── invalid.js ├── mask-write-register.js ├── parser.js ├── read-coils.js ├── read-device-identification.js ├── read-discrete-inputs.js ├── read-exception-status.js ├── read-fifo-queue.js ├── read-file-record.js ├── read-holding-registers.js ├── read-input-registers.js ├── read-write-multiple-registers.js ├── write-file-record.js ├── write-multiple-coils.js ├── write-multiple-registers.js ├── write-single-coil.js └── write-single-register.js └── run.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .DS_Store 3 | test.js 4 | ~* 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "8" 5 | - "10" 6 | - "12" 7 | 8 | cache: npm 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.14.0 - 5 Jun 2022 2 | 3 | - modbus: removes fs dependency to load protocol parts (@PBrunot) 4 | - deps: 5 | - mocha@10.0.0 6 | 7 | ## 1.13.1 - 29 Feb 2019 8 | 9 | - deps: 10 | - mocha@6.2.0 11 | 12 | ## 1.13.0 - 14 Feb 2019 13 | 14 | - format: 15 | - fixes #6 16 | - deps: 17 | - mocha@5.2.0 18 | 19 | ## 1.12.0 - 2 Nov 2018 20 | 21 | - minor fixes for NodeJS > 8 22 | 23 | ## 1.11.1 - 9 May 2018 24 | 25 | - helpers: 26 | - adds support to reply directly with a buffer instead of an Array of 2-byte buffers 27 | 28 | ## 1.11.0 - 27 Jun 2017 29 | 30 | - modbus: properly check for a valid object and not null 31 | 32 | ## 1.10.0 - 26 Jun 2017 33 | 34 | - read-coils: fixes not properly throwing when replying to coils with null/undef 35 | - readme: fixes link to stream module 36 | 37 | ## 1.9.0 - 23 Jun 2017 38 | 39 | - fixes function code for read-write-multiple-registers :( 40 | 41 | ## 1.8.4 - 21 Jun 2017 42 | 43 | - helpers: 44 | - exposes some helpers 45 | - fixes blocksToBuffer when a block has length < 2 46 | - test: 47 | - adds check for out of bounds response sizes 48 | - ensures read-file-record and write-file-record throw when reaching length limits 49 | 50 | ## 1.8.3 - 8 Jun 2017 51 | 52 | - exceptions: 53 | - adds a helper function .error() 54 | - test: 55 | - add node v8 56 | - deps: 57 | - mocha@3.4.2 58 | 59 | ## 1.8.2 - 19 May 2017 60 | 61 | - fixes buffer not being correctly converted to bits 62 | 63 | ## 1.8.1 - 27 Apr 2017 64 | 65 | - read-write-multiple-registers: 66 | - fixes minimum buffer length required for requests' 67 | 68 | ## 1.8.0 - 20 Apr 2017 69 | 70 | - modbus: 71 | - do not throw error on unknown function, return object with code and data 72 | 73 | ## 1.7.0 - 13 Apr 2017 74 | 75 | - READ_FIFO_QUEUE: 76 | - fixes not checking response size 77 | - GET_COMM_EVENT_LOG: 78 | - fixes minimum response size 79 | 80 | ## 1.6.0 - 13 Apr 2017 81 | 82 | - return null on invalid data instead of throwing 83 | 84 | ## 1.5.1 - 13 Apr 2017 85 | 86 | - buffer: 87 | - renames Utils to Buffer 88 | - ci: 89 | - fixes travis file 90 | 91 | ## 1.5.0 - 13 Apr 2017 92 | 93 | - adds support for at least node 0.10 94 | 95 | ## 1.4.0 - 12 Apr 2017 96 | 97 | - modbus: 98 | - adds a custom package builder 99 | - do not throw error on unknown function, return object with code and data 100 | - exception: 101 | - allow one to build an exception specifying fcode and error code 102 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Diogo Resende 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Modbus PDU 2 | 3 | [![Build Status](https://secure.travis-ci.org/node-modbus/pdu.png?branch=master)](http://travis-ci.org/node-modbus/pdu) 4 | [![](https://badge.fury.io/js/modbus-pdu.svg)](https://npmjs.org/package/modbus-pdu) 5 | 6 | This is a generic module to create all the modbus PDU message types. You should then use another abstraction to TCP, RTU and ASCII. 7 | 8 | You should not use this directly, use [stream](https://github.com/node-modbus/stream) instead. 9 | 10 | ### Install 11 | 12 | ```sh 13 | npm i modbus-pdu 14 | ``` 15 | 16 | ### Examples 17 | 18 | ```js 19 | var Modbus = require("modbus-pdu"); 20 | 21 | // 22 | console.log(Modbus.ReadCoils.Request.build(13, 20)); 23 | // { address: 13, quantity: 20 } 24 | console.log(Modbus.ReadCoils.Request.parse(new Buffer([ 0x01, 0x00, 0x0d, 0x00, 0x14 ]))); 25 | 26 | // 27 | console.log(Modbus.ReadFileRecord.Exception.build(Modbus.Exception.ServerDeviceBusy)); 28 | // { code: 'ReadFileRecord', exception: 'ServerDeviceBusy' } 29 | console.log(Modbus.Exception.parse(new Buffer([ 0x94, 0x06 ]))); 30 | 31 | // can also auto detect function code 32 | // { code: "ReadCoils", address: 13, quantity: 20 } 33 | console.log(Modbus.Request(new Buffer([ 0x01, 0x00, 0x0d, 0x00, 0x14 ]))); 34 | // { code: 'ReadCoils', data: [ 1, 0, 1, 1, 0, 1, 0, 1 ] } 35 | console.log(Modbus.Response(new Buffer([ 0x01, 0x01, 0xad ]))); 36 | ``` 37 | 38 | ### Function Codes 39 | 40 | - `01` ReadCoils(`address`, `quantity`) 41 | - `02` ReadDiscreteInputs(`address`, `quantity`) 42 | - `03` ReadHoldingRegisters(`address`, `quantity`) 43 | - `04` ReadInputRegisters(`address`, `quantity`) 44 | - `05` WriteSingleCoil(`address`, `value`) 45 | - `06` WriteSingleRegister(`address`, `value`) 46 | - `07` ReadExceptionStatus() 47 | - `0B` GetCommEventCounter() 48 | - `0C` GetCommEventLog() 49 | - `0F` WriteMultipleCoils(`address`, `values`) // `values` should be Array of `1`/`0` 50 | - `10` WriteMultipleRegisters(`address`, `values`) // `values` should be Array of 2-size Buffers 51 | - `14` ReadFileRecord(`requests`) // `requests` should be Array of objects with keys `file`, `address` and `length` 52 | - `15` WriteFileRecord(`requests`) // `requests` should be Array of objects with keys `file`, `address` and `values` (Array of 2-size Buffers) 53 | - `16` MaskWriteRegister(`address`, `andmask`, `ormask`) 54 | - `17` ReadWriteMultipleRegisters(`read_address`, `read_quantity`, `write_address`, `values`) // `values` should be Array of 2-size Buffers 55 | - `18` ReadFIFOQueue(`address`) 56 | - `2B/0E` ReadDeviceIdentification(`code`, `id`) 57 | 58 | ### Exceptions 59 | 60 | - `01` IllegalFunction 61 | - `02` IllegalDataAddress 62 | - `03` IllegalDataValue 63 | - `04` ServerDeviceFailure 64 | - `05` Aknowledge 65 | - `06` ServerDeviceBusy 66 | - `08` MemoryParityError 67 | - `0A` GatewayPathUnavailable 68 | - `0B` GatewayTargetDeviceFailedToRespond 69 | 70 | You can create an `Error` object with code and message using the following code: 71 | 72 | ```js 73 | var err = Modbus.Exceptions.error("GatewayPathUnavailable"); 74 | console.log(err.code); // 10 (0x0A) 75 | console.log(err.message); // "GatewayPathUnavailable" 76 | ``` 77 | 78 | You can use this error directly when replying to requests using the [stream](https://github.com/node-modbus/stream) module. 79 | -------------------------------------------------------------------------------- /lib/Buffer.js: -------------------------------------------------------------------------------- 1 | exports.alloc = function (size) { 2 | if (typeof Buffer.alloc == "function") { 3 | return Buffer.alloc(size); 4 | } 5 | 6 | return new Buffer(size); 7 | }; 8 | 9 | exports.from = function (data, encoding) { 10 | if (typeof Buffer.from == "function") { 11 | return Buffer.from(data, encoding); 12 | } 13 | 14 | return new Buffer(data, encoding); 15 | }; 16 | -------------------------------------------------------------------------------- /lib/Exception.js: -------------------------------------------------------------------------------- 1 | var Modbus = require("./Modbus"); 2 | var Buff = require("./Buffer"); 3 | 4 | exports.IllegalFunction = 0x01; 5 | exports.IllegalDataAddress = 0x02; 6 | exports.IllegalDataValue = 0x03; 7 | exports.ServerDeviceFailure = 0x04; 8 | exports.Aknowledge = 0x05; 9 | exports.ServerDeviceBusy = 0x06; 10 | exports.MemoryParityError = 0x08; 11 | exports.GatewayPathUnavailable = 0x0A; 12 | exports.GatewayTargetDeviceFailedToRespond = 0x0B; 13 | 14 | exports.load = function (functs) { 15 | for (var f in functs) { 16 | if (typeof functs[f] != "object" || !functs[f].hasOwnProperty("Code")) continue; 17 | 18 | functs[f].Exception = prepareException(f, functs[f]); 19 | } 20 | }; 21 | 22 | exports.error = function (reason) { 23 | if (typeof reason == "string") { 24 | var err = new Error(reason); 25 | err.code = exports[reason]; 26 | 27 | return err; 28 | } 29 | 30 | for (var k in exports) { 31 | if (typeof exports[k] != "number") continue; 32 | if (exports[k] != reason) continue 33 | 34 | var err = new Error(k); 35 | err.code = exports[k]; 36 | 37 | return err; 38 | } 39 | 40 | return new Error("" + reason); 41 | }; 42 | 43 | exports.build = function (fcode, code) { 44 | return Buff.from([ fcode | 0x80, (typeof code == "string" ? exports[code] : code) ]); 45 | }; 46 | 47 | exports.parse = function (buffer) { 48 | var data = { 49 | code : buffer[0] & 0x7F, 50 | exception : buffer[1] 51 | }; 52 | 53 | for (var k in Modbus) { 54 | if (typeof Modbus[k] == "object" && Modbus[k].Code == data.code) { 55 | data.code = k; 56 | break; 57 | } 58 | } 59 | 60 | for (var k in exports) { 61 | if (exports[k] == data.exception) { 62 | data.exception = k; 63 | break; 64 | } 65 | } 66 | 67 | return data; 68 | }; 69 | 70 | function prepareException(name, funct) { 71 | return { 72 | build: function (code) { 73 | return Buff.from([ funct.Code | 0x80, code ]); 74 | }, 75 | parse: function (buffer) { 76 | var exception = buffer[buffer.length - 1]; 77 | 78 | for (var k in exports) { 79 | if (exports[k] == exception) { 80 | exception = k; 81 | break; 82 | } 83 | } 84 | 85 | return exception; 86 | } 87 | }; 88 | } 89 | -------------------------------------------------------------------------------- /lib/Helpers.js: -------------------------------------------------------------------------------- 1 | var Buff = require("./Buffer"); 2 | 3 | exports.buildStartEndAddress = function buildStartEndAddress(start, end) { 4 | var buffer = Buff.alloc(4); 5 | 6 | buffer.writeUInt16BE(start, 0); 7 | buffer.writeUInt16BE(end - start + 1, 2); 8 | 9 | return buffer; 10 | }; 11 | 12 | exports.buildAddressQuantity = function buildAddressQuantity(address, quantity) { 13 | var buffer = Buff.alloc(4); 14 | 15 | buffer.writeUInt16BE(address, 0); 16 | buffer.writeUInt16BE(quantity, 2); 17 | 18 | return buffer; 19 | }; 20 | 21 | exports.buildEmpty = function buildEmpty() { 22 | return Buff.alloc(0); 23 | }; 24 | 25 | exports.parseStartEndAddress = function parseStartEndAddress(buffer) { 26 | if (buffer.length < 4) return null; 27 | 28 | return { 29 | start : buffer.readUInt16BE(0), 30 | end : buffer.readUInt16BE(0) + buffer.readUInt16BE(2) - 1 31 | }; 32 | }; 33 | 34 | exports.parseAddressQuantity = function parseAddressQuantity(buffer) { 35 | if (buffer.length < 4) return null; 36 | 37 | return { 38 | address : buffer.readUInt16BE(0), 39 | quantity : buffer.readUInt16BE(2) 40 | }; 41 | }; 42 | 43 | exports.parseAddressValue = function parseAddressValue(buffer) { 44 | if (buffer.length < 4) return null; 45 | 46 | return { 47 | address : buffer.readUInt16BE(0), 48 | value : buffer.slice(2, 4) 49 | }; 50 | }; 51 | 52 | exports.parseEmpty = function parseEmpty() { 53 | return {}; 54 | }; 55 | 56 | exports.numberToBuffer = function numberToBuffer(number) { 57 | if (number instanceof Buffer) { 58 | return number; 59 | } 60 | 61 | var buffer = Buff.alloc(2); 62 | 63 | buffer.writeUInt16BE(number, 0); 64 | 65 | return buffer; 66 | } 67 | 68 | exports.bitsToBuffer = function bitsToBuffer(bits) { 69 | if (bits == null || bits.length > 2040) { 70 | throw new Error("Buffer overflow, bit length is out of bounds"); 71 | } 72 | 73 | var buffer = Buff.alloc(Math.ceil(bits.length / 8) + 1); 74 | var i; 75 | 76 | buffer.fill(0x00); 77 | buffer[0] = buffer.length - 1; 78 | 79 | for (var index = 0; index < bits.length; index++) { 80 | i = Math.floor(index / 8) + 1; 81 | 82 | buffer[i] >>= 1; 83 | if (bits[index]) { 84 | buffer[i] |= 0x80; 85 | } 86 | } 87 | 88 | i = bits.length - (Math.floor(bits.length / 8) * 8); 89 | if (i > 0) { 90 | i = 8 - i; 91 | while (i > 0) { 92 | buffer[buffer.length - 1] >>= 1; 93 | i -= 1; 94 | } 95 | } 96 | 97 | return buffer; 98 | }; 99 | 100 | exports.blocksToBuffer = function blocksToBuffer(blocks) { 101 | if (Buffer.isBuffer(blocks)) { 102 | var buffer = Buff.alloc(blocks.length + 1); 103 | 104 | buffer[0] = blocks.length; 105 | 106 | blocks.copy(buffer, 1); 107 | 108 | return buffer; 109 | } 110 | 111 | var buffer = Buff.alloc((blocks.length * 2) + 1); 112 | 113 | buffer.writeUInt8(blocks.length * 2, 0); 114 | 115 | for (var i = 0; i < blocks.length; i++) { 116 | if (blocks[i].length < 2) { 117 | buffer[(i * 2) + 1] = 0; 118 | buffer[(i * 2) + 2] = 0; 119 | } 120 | 121 | blocks[i].copy(buffer, (i * 2) + 1, 0, 2); 122 | } 123 | 124 | return buffer; 125 | }; 126 | 127 | exports.bufferToBits = function bufferToBits(buffer) { 128 | var bits = []; 129 | 130 | for (var i = 1; i < Math.min(buffer.length, buffer[0] + 1); i++) { 131 | for (var j = 0; j < 8; j++) { 132 | bits.push((buffer[i] & (1 << j)) ? 1 : 0); 133 | } 134 | } 135 | 136 | return bits; 137 | }; 138 | 139 | exports.bufferToBlocks = function bufferToBlocks(buffer) { 140 | if (buffer.length === 0) return null; 141 | 142 | var total = buffer.readUInt8(0) / 2; 143 | var blocks = []; 144 | 145 | for (var i = 0; i < total; i++) { 146 | blocks.push(Buff.from([ buffer[(i * 2) + 1], buffer[(i * 2) + 2] ])); 147 | } 148 | 149 | return blocks; 150 | }; 151 | 152 | exports.copyBufferBlocks = function copyBufferBlocks(buffer, values, offset) { 153 | for (var i = 0; i < values.length; i++) { 154 | values[i].copy(buffer, offset + (i * 2), 0, 2); 155 | } 156 | }; 157 | -------------------------------------------------------------------------------- /lib/Modbus.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | var Exception = require("./Exception"); 3 | var Helpers = require("./Helpers"); 4 | var Buff = require("./Buffer"); 5 | 6 | load(); 7 | 8 | exports.Exception = Exception; 9 | exports.Package = function (fcode, data) { 10 | var buffer = Buff.alloc(data.length + 1); 11 | 12 | buffer.writeUInt8(fcode, 0); 13 | Buff.from(data).copy(buffer, 1); 14 | 15 | return buffer; 16 | }; 17 | 18 | exports.Helpers = { 19 | blocksToBuffer : Helpers.blocksToBuffer, 20 | bitsToBuffer : Helpers.bitsToBuffer, 21 | 22 | bufferToBlocks : Helpers.bufferToBlocks, 23 | bufferToBits : Helpers.bufferToBits, 24 | }; 25 | 26 | Exception.load(exports); 27 | 28 | function load() { 29 | // List of request files in protocol subdir. 30 | var files = [ 31 | "GET_COMM_EVENT_COUNTER", 32 | "GET_COMM_EVENT_LOG", 33 | "MASK_WRITE_REGISTER", 34 | "READ_COILS", 35 | "READ_DEVICE_IDENTIFICATION", 36 | "READ_DISCRETE_INPUTS", 37 | "READ_EXCEPTION_STATUS", 38 | "READ_FIFO_QUEUE", 39 | "READ_FILE_RECORD", 40 | "READ_HOLDING_REGISTERS", 41 | "READ_INPUT_REGISTERS", 42 | "READ_WRITE_MULTIPLE_REGISTERS", 43 | "WRITE_FILE_RECORD", 44 | "WRITE_MULTIPLE_COILS", 45 | "WRITE_MULTIPLE_REGISTERS", 46 | "WRITE_SINGLE_COIL", 47 | "WRITE_SINGLE_REGISTER", 48 | ]; 49 | 50 | files.map(function (file) { 51 | var funct = require("./protocol/" + file + ".js"); 52 | var camelName = file[0].toUpperCase() + file.substr(1).toLowerCase().replace(/_(\w)/g, function (m, c) { 53 | return c.toUpperCase(); 54 | }); 55 | 56 | exports[camelName] = { 57 | Code : funct.code, 58 | Request : { 59 | build : proxy(funct, "buildRequest"), 60 | parse : function (buffer) { 61 | // byte 1 is function code 62 | return funct.parseRequest(buffer.slice(1)); 63 | } 64 | }, 65 | Response : { 66 | build : proxy(funct, "buildResponse"), 67 | parse : function (buffer) { 68 | // byte 1 is function code 69 | return funct.parseResponse(buffer.slice(1)); 70 | } 71 | } 72 | }; 73 | }); 74 | 75 | exports.Request = function (buffer) { 76 | var code = buffer.readUInt8(0); 77 | 78 | for (var k in exports) { 79 | if (typeof exports[k] === "object" && exports[k].Code === code) { 80 | var data = exports[k].Request.parse(buffer); 81 | 82 | if (typeof data === "object" && !Array.isArray(data) && data !== null) { 83 | data.code = k; 84 | } else { 85 | data = { code: k, data: data }; 86 | } 87 | 88 | return data; 89 | } 90 | } 91 | 92 | return { 93 | code : buffer[0], 94 | data : buffer.slice(1) 95 | }; 96 | }; 97 | 98 | exports.Response = function (buffer) { 99 | var code = buffer.readUInt8(0); 100 | 101 | if (code & 0x80) { 102 | return Exception.parse(buffer); 103 | } 104 | 105 | for (var k in exports) { 106 | if (typeof exports[k] === "object" && exports[k].Code === code) { 107 | var data = exports[k].Response.parse(buffer); 108 | 109 | if (typeof data === "object" && !Array.isArray(data) && data !== null) { 110 | data.code = k; 111 | } else { 112 | data = { code: k, data: data }; 113 | } 114 | 115 | return data; 116 | } 117 | } 118 | 119 | return { 120 | code : buffer[0], 121 | data : buffer.slice(1) 122 | }; 123 | }; 124 | } 125 | 126 | function proxy(funct, method) { 127 | return function () { 128 | var stream = funct[method].apply(funct, arguments); 129 | var buffer = Buff.alloc(stream.length + 1); 130 | 131 | buffer[0] = funct.code; 132 | 133 | stream.copy(buffer, 1); 134 | 135 | return buffer; 136 | }; 137 | } 138 | -------------------------------------------------------------------------------- /lib/protocol/GET_COMM_EVENT_COUNTER.js: -------------------------------------------------------------------------------- 1 | var Helpers = require("../Helpers"); 2 | var Buff = require("../Buffer"); 3 | 4 | exports.code = 0x0B; 5 | 6 | exports.buildRequest = Helpers.buildEmpty; 7 | exports.parseRequest = Helpers.parseEmpty; 8 | 9 | exports.buildResponse = function (status, event_count) { 10 | return Buff.from([ status, event_count ]); 11 | }; 12 | exports.parseResponse = function (buffer) { 13 | if (buffer.length < 2) return null; 14 | 15 | return { 16 | status : buffer.readUInt8(0), 17 | event_count : buffer.readUInt8(1) 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /lib/protocol/GET_COMM_EVENT_LOG.js: -------------------------------------------------------------------------------- 1 | var Helpers = require("../Helpers"); 2 | var Buff = require("../Buffer"); 3 | 4 | exports.code = 0x0C; 5 | 6 | exports.buildRequest = Helpers.buildEmpty; 7 | exports.parseRequest = Helpers.parseEmpty; 8 | 9 | exports.buildResponse = function (status, event_count, message_count, events) { 10 | var buffer = Buff.alloc(events.length + 7); 11 | 12 | buffer.writeUInt8(buffer.length - 1, 0); 13 | buffer.writeUInt16BE(status, 1); 14 | buffer.writeUInt16BE(event_count, 3); 15 | buffer.writeUInt16BE(message_count, 5); 16 | 17 | events.copy(buffer, 7); 18 | 19 | return buffer; 20 | }; 21 | exports.parseResponse = function (buffer) { 22 | if (buffer.length < 7) return null; 23 | 24 | return { 25 | status : buffer.readUInt16BE(1), 26 | event_count : buffer.readUInt16BE(3), 27 | message_count : buffer.readUInt16BE(5), 28 | events : buffer.slice(7, buffer.readUInt8(0) + 1) 29 | }; 30 | }; 31 | -------------------------------------------------------------------------------- /lib/protocol/MASK_WRITE_REGISTER.js: -------------------------------------------------------------------------------- 1 | var Helpers = require("../Helpers"); 2 | var Buff = require("../Buffer"); 3 | 4 | exports.code = 0x16; 5 | 6 | exports.buildRequest = function (address, andmask, ormask) { 7 | var buffer = Buff.alloc(6); 8 | 9 | buffer.writeUInt16BE(address, 0); 10 | 11 | Helpers.numberToBuffer(andmask).copy(buffer, 2, 0, 2); 12 | Helpers.numberToBuffer(ormask).copy(buffer, 4, 0, 2); 13 | 14 | return buffer; 15 | }; 16 | exports.parseRequest = function (buffer) { 17 | if (buffer.length < 6) return null; 18 | 19 | return { 20 | address : buffer.readUInt16BE(0), 21 | andmask : buffer.slice(2, 4), 22 | ormask : buffer.slice(4, 6) 23 | }; 24 | }; 25 | 26 | exports.buildResponse = exports.buildRequest; 27 | exports.parseResponse = exports.parseRequest; 28 | -------------------------------------------------------------------------------- /lib/protocol/READ_COILS.js: -------------------------------------------------------------------------------- 1 | var Helpers = require("../Helpers"); 2 | 3 | exports.code = 0x01; 4 | 5 | exports.buildRequest = Helpers.buildAddressQuantity; 6 | exports.parseRequest = Helpers.parseAddressQuantity; 7 | 8 | exports.buildResponse = Helpers.bitsToBuffer; 9 | exports.parseResponse = Helpers.bufferToBits; 10 | -------------------------------------------------------------------------------- /lib/protocol/READ_DEVICE_IDENTIFICATION.js: -------------------------------------------------------------------------------- 1 | var Helpers = require("../Helpers"); 2 | var Buff = require("../Buffer"); 3 | 4 | const ObjectIds = { 5 | 0 : "VendorName", 6 | 1 : "ProductCode", 7 | 2 : "MajorMinorRevision", 8 | 3 : "VendorUrl", 9 | 4 : "ProductName", 10 | 5 : "ModelName", 11 | 6 : "UserApplicationName" 12 | }; 13 | 14 | const AccessTypes = { 15 | 1 : "BasicDeviceIdentification", 16 | 2 : "RegularDeviceIdentification", 17 | 3 : "ExtendedDeviceIdentification", 18 | 4 : "SpecificIdentificationObject" 19 | }; 20 | 21 | exports.code = 0x2B; 22 | 23 | exports.buildRequest = function (type, id) { 24 | var buffer = Buff.alloc(3); 25 | 26 | if (typeof type == "string") { 27 | for (var k in AccessTypes) { 28 | if (AccessTypes[k] == type) { 29 | type = k; 30 | break; 31 | } 32 | } 33 | } 34 | 35 | if (typeof id == "string") { 36 | for (var k in ObjectIds) { 37 | if (ObjectIds[k] == id) { 38 | id = k; 39 | break; 40 | } 41 | } 42 | } 43 | 44 | buffer.writeUInt8(0x0E, 0); 45 | buffer.writeUInt8(type, 1); 46 | buffer.writeUInt8(id, 2); 47 | 48 | return buffer; 49 | }; 50 | 51 | exports.parseRequest = function (buffer) { 52 | if (buffer.length < 3) return null; 53 | 54 | var req = { 55 | type : buffer.readUInt8(1), 56 | id : buffer.readUInt8(2) 57 | }; 58 | 59 | if (AccessTypes.hasOwnProperty(req.type)) { 60 | req.type = AccessTypes[req.type]; 61 | } 62 | 63 | if (ObjectIds.hasOwnProperty(req.id)) { 64 | req.id = ObjectIds[req.id]; 65 | } 66 | 67 | return req; 68 | }; 69 | 70 | exports.buildResponse = function (type, conformity, more, objects) { 71 | var object_len = objects.reduce(function (len, object) { 72 | return len + 2 + object.value.length; 73 | }, 0); 74 | var buffer = Buff.alloc(6 + object_len); 75 | var offset = 6; 76 | var next_id = 0; 77 | 78 | if (typeof type == "string") { 79 | for (var k in AccessTypes) { 80 | if (AccessTypes[k] == type) { 81 | type = k; 82 | break; 83 | } 84 | } 85 | } 86 | 87 | if (typeof conformity == "string") { 88 | for (var k in AccessTypes) { 89 | if (AccessTypes[k] == conformity) { 90 | conformity = k; 91 | break; 92 | } 93 | } 94 | } 95 | 96 | buffer.writeUInt8(0x0E, 0); 97 | buffer.writeUInt8(type, 1); 98 | buffer.writeUInt8(conformity, 2); 99 | buffer.writeUInt8(more ? 0xFF : 0x00, 3); 100 | // index 4 will be in the end 101 | buffer.writeUInt8(objects.length, 5); 102 | 103 | for (var i = 0; i < objects.length; i++) { 104 | // make a copy, don't change original 105 | var id = objects[i].id; 106 | 107 | if (typeof id == "string") { 108 | for (var k in ObjectIds) { 109 | if (ObjectIds[k] == id) { 110 | id = k; 111 | break; 112 | } 113 | } 114 | } 115 | buffer.writeUInt8(id, offset); 116 | buffer.writeUInt8(objects[i].value.length, offset + 1); 117 | 118 | objects[i].value.copy(buffer, offset + 2); 119 | 120 | offset += 2 + objects[i].value.length; 121 | next_id = id + 1; 122 | } 123 | 124 | buffer.writeUInt8(more ? next_id : 0x00, 4); 125 | 126 | return buffer; 127 | }; 128 | 129 | exports.parseResponse = function (buffer) { 130 | if (buffer.length < 6) return null; 131 | 132 | var data = { 133 | type : buffer.readUInt8(1), 134 | conformity : buffer.readUInt8(2), 135 | more : !!buffer.readUInt8(3), 136 | next : buffer.readUInt8(4), 137 | objects : [] 138 | }; 139 | var total = buffer.readUInt8(5); 140 | var offset = 6; 141 | 142 | if (AccessTypes.hasOwnProperty(data.type)) { 143 | data.type = AccessTypes[data.type]; 144 | } 145 | 146 | if (AccessTypes.hasOwnProperty(data.conformity)) { 147 | data.conformity = AccessTypes[data.conformity]; 148 | } 149 | 150 | for (var i = 0; i < total; i++) { 151 | var len = buffer.readUInt8(offset + 1); 152 | 153 | data.objects.push({ 154 | id : buffer.readUInt8(offset), 155 | value : buffer.slice(offset + 2, offset + 2 + len) 156 | }); 157 | 158 | offset += 2 + len; 159 | } 160 | 161 | data.objects.map(function (object) { 162 | if (ObjectIds.hasOwnProperty(object.id)) { 163 | object.id = ObjectIds[object.id]; 164 | } 165 | }); 166 | 167 | return data; 168 | }; 169 | -------------------------------------------------------------------------------- /lib/protocol/READ_DISCRETE_INPUTS.js: -------------------------------------------------------------------------------- 1 | var Helpers = require("../Helpers"); 2 | 3 | exports.code = 0x02; 4 | 5 | exports.buildRequest = Helpers.buildAddressQuantity; 6 | exports.parseRequest = Helpers.parseAddressQuantity; 7 | 8 | exports.buildResponse = Helpers.bitsToBuffer; 9 | exports.parseResponse = Helpers.bufferToBits; 10 | -------------------------------------------------------------------------------- /lib/protocol/READ_EXCEPTION_STATUS.js: -------------------------------------------------------------------------------- 1 | var Helpers = require("../Helpers"); 2 | var Buff = require("../Buffer"); 3 | 4 | exports.code = 0x07; 5 | 6 | exports.buildRequest = Helpers.buildEmpty; 7 | exports.parseRequest = Helpers.parseEmpty; 8 | 9 | exports.buildResponse = function (data) { 10 | return (Buffer.isBuffer(data) ? data.slice(0, 1) : Buff.from([ data ])); 11 | }; 12 | exports.parseResponse = function (buffer) { 13 | if (buffer.length === 0) return null; 14 | 15 | return { 16 | data : buffer.readUInt8(0) 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /lib/protocol/READ_FIFO_QUEUE.js: -------------------------------------------------------------------------------- 1 | var Helpers = require("../Helpers"); 2 | var Buff = require("../Buffer"); 3 | 4 | exports.code = 0x18; 5 | 6 | exports.buildRequest = function (address) { 7 | var buffer = Buff.alloc(2); 8 | 9 | buffer.writeUInt16BE(address, 0); 10 | 11 | return buffer; 12 | }; 13 | 14 | exports.parseRequest = function (buffer) { 15 | if (buffer.length < 2) return null; 16 | 17 | return { 18 | address : buffer.readUInt16BE(0) 19 | }; 20 | }; 21 | 22 | exports.buildResponse = function (registers) { 23 | var buffer = Buff.alloc(4 + (registers.length * 2)); 24 | 25 | buffer.writeUInt16BE(buffer.length - 2, 0); 26 | buffer.writeUInt16BE(registers.length, 2); 27 | 28 | Helpers.copyBufferBlocks(buffer, registers, 4); 29 | 30 | return buffer; 31 | }; 32 | 33 | exports.parseResponse = function (buffer) { 34 | if (buffer.length < 4) return null; 35 | 36 | var total = buffer.readUInt16BE(2); 37 | var registers = []; 38 | var offset = 4; 39 | 40 | for (var i = 0; i < total; i++) { 41 | registers.push(buffer.slice(offset, offset + 2)); 42 | 43 | offset += 2; 44 | } 45 | 46 | return registers; 47 | }; 48 | -------------------------------------------------------------------------------- /lib/protocol/READ_FILE_RECORD.js: -------------------------------------------------------------------------------- 1 | var Helpers = require("../Helpers"); 2 | var Buff = require("../Buffer"); 3 | 4 | exports.code = 0x14; 5 | 6 | exports.buildRequest = function (requests) { 7 | var buffer = Buff.alloc(1 + (7 * requests.length)); 8 | var offset = 1; 9 | 10 | buffer.writeUInt8(buffer.length - 1, 0); 11 | 12 | for (var i = 0; i < requests.length; i++) { 13 | buffer.writeUInt8(0x06, offset); 14 | buffer.writeUInt16BE(requests[i].file, offset + 1); 15 | buffer.writeUInt16BE(requests[i].address, offset + 3); 16 | buffer.writeUInt16BE(requests[i].length, offset + 5); 17 | 18 | offset += 7; 19 | } 20 | 21 | return buffer; 22 | }; 23 | exports.parseRequest = function (buffer) { 24 | if (buffer.length < 7) return null; 25 | 26 | var data = []; 27 | var offset = 2; 28 | var requests = (buffer.length - 1) / 7; 29 | 30 | for (var i = 0; i < requests; i++) { 31 | data.push({ 32 | file : buffer.readUInt16BE(offset), 33 | address : buffer.readUInt16BE(offset + 2), 34 | length : buffer.readUInt16BE(offset + 4) 35 | }); 36 | 37 | offset += 7; 38 | } 39 | 40 | return data; 41 | }; 42 | 43 | exports.buildResponse = function (responses) { 44 | var data_len = responses.reduce(function (len, response) { 45 | return len + (response.length * 2); 46 | }, 0); 47 | var buffer = Buff.alloc(1 + (responses.length * 2) + data_len); 48 | var offset = 1; 49 | 50 | buffer.writeUInt8(buffer.length - 1, 0); 51 | 52 | for (var i = 0; i < responses.length; i++) { 53 | buffer.writeUInt8(1 + (responses[i].length * 2), offset); 54 | buffer.writeUInt8(0x06, offset + 1); 55 | 56 | offset += 2; 57 | 58 | for (var j = 0; j < responses[i].length; j++) { 59 | responses[i][j].copy(buffer, offset, 0, 2); 60 | 61 | offset += 2; 62 | } 63 | } 64 | 65 | return buffer; 66 | }; 67 | 68 | exports.parseResponse = function (buffer) { 69 | if (buffer.length === 0) return null; 70 | 71 | var responses = []; 72 | var len = buffer.readUInt8(0); 73 | var offset = 1; 74 | 75 | while (offset < len) { 76 | var total = (buffer.readUInt8(offset) - 1) / 2; 77 | var registers = []; 78 | 79 | offset += 2; 80 | 81 | for (var i = 0; i < total; i++) { 82 | registers.push(buffer.slice(offset, offset + 2)); 83 | 84 | offset += 2; 85 | } 86 | 87 | responses.push(registers); 88 | } 89 | 90 | return responses; 91 | }; 92 | -------------------------------------------------------------------------------- /lib/protocol/READ_HOLDING_REGISTERS.js: -------------------------------------------------------------------------------- 1 | var Helpers = require("../Helpers"); 2 | 3 | exports.code = 0x03; 4 | 5 | exports.buildRequest = Helpers.buildAddressQuantity; 6 | exports.parseRequest = Helpers.parseAddressQuantity; 7 | 8 | exports.buildResponse = Helpers.blocksToBuffer; 9 | exports.parseResponse = Helpers.bufferToBlocks; 10 | -------------------------------------------------------------------------------- /lib/protocol/READ_INPUT_REGISTERS.js: -------------------------------------------------------------------------------- 1 | var Helpers = require("../Helpers"); 2 | 3 | exports.code = 0x04; 4 | 5 | exports.buildRequest = Helpers.buildAddressQuantity; 6 | exports.parseRequest = Helpers.parseAddressQuantity; 7 | 8 | exports.buildResponse = Helpers.blocksToBuffer; 9 | exports.parseResponse = Helpers.bufferToBlocks; 10 | -------------------------------------------------------------------------------- /lib/protocol/READ_WRITE_MULTIPLE_REGISTERS.js: -------------------------------------------------------------------------------- 1 | var Helpers = require("../Helpers"); 2 | var Buff = require("../Buffer"); 3 | 4 | exports.code = 0x17; 5 | 6 | exports.buildRequest = function (read_address, read_quantity, write_address, values) { 7 | var buffer = Buff.alloc(9 + (values.length * 2)); 8 | 9 | buffer.writeUInt16BE(read_address, 0); 10 | buffer.writeUInt16BE(read_quantity, 2); 11 | buffer.writeUInt16BE(write_address, 4); 12 | buffer.writeUInt16BE(values.length, 6); 13 | buffer.writeUInt8(values.length * 2, 8); 14 | 15 | Helpers.copyBufferBlocks(buffer, values, 9); 16 | 17 | return buffer; 18 | }; 19 | exports.parseRequest = function (buffer) { 20 | if (buffer.length < 8) return null; 21 | 22 | var blocks = buffer.readUInt8(8) / 2; 23 | var data = { 24 | read_address : buffer.readUInt16BE(0), 25 | read_quantity : buffer.readUInt16BE(2), 26 | write_address : buffer.readUInt16BE(4), 27 | values : [] 28 | }; 29 | 30 | for (var i = 0; i < blocks; i++) { 31 | data.values.push(buffer.slice(9 + (i * 2), 11 + (i * 2))); 32 | } 33 | 34 | return data; 35 | }; 36 | 37 | exports.buildResponse = function (values) { 38 | var buffer = Buff.alloc(values.length * 2 + 1); 39 | 40 | buffer.writeUInt8(values.length * 2, 0); 41 | 42 | Helpers.copyBufferBlocks(buffer, values, 1); 43 | 44 | return buffer; 45 | }; 46 | 47 | exports.parseResponse = function (buffer) { 48 | if (buffer.length === 0) return null; 49 | 50 | var blocks = buffer.readUInt8(0) / 2; 51 | var values = []; 52 | 53 | for (var i = 0; i < blocks; i++) { 54 | values.push(buffer.slice(1 + (i * 2), 3 + (i * 2))); 55 | } 56 | 57 | return { values: values }; 58 | }; 59 | -------------------------------------------------------------------------------- /lib/protocol/WRITE_FILE_RECORD.js: -------------------------------------------------------------------------------- 1 | var Helpers = require("../Helpers"); 2 | var Buff = require("../Buffer"); 3 | 4 | exports.code = 0x15; 5 | 6 | exports.buildRequest = function (requests) { 7 | var data_len = requests.reduce(function (len, request) { 8 | return len + (request.values.length * 2); 9 | }, 0); 10 | var buffer = Buff.alloc(1 + (requests.length * 7) + data_len); 11 | var offset = 1; 12 | 13 | buffer.writeUInt8(buffer.length - 1, 0); 14 | 15 | for (var i = 0; i < requests.length; i++) { 16 | buffer.writeUInt8(0x06, offset); 17 | buffer.writeUInt16BE(requests[i].file, offset + 1); 18 | buffer.writeUInt16BE(requests[i].address, offset + 3); 19 | buffer.writeUInt16BE(requests[i].values.length, offset + 5); 20 | 21 | offset += 7; 22 | 23 | for (var j = 0; j < requests[i].values.length; j++) { 24 | requests[i].values[j].copy(buffer, offset, 0, 2); 25 | 26 | offset += 2; 27 | } 28 | } 29 | 30 | return buffer; 31 | }; 32 | exports.parseRequest = function (buffer) { 33 | if (buffer.length === 0) return null; 34 | 35 | var len = buffer.readUInt8(0); 36 | var requests = []; 37 | var offset = 1; 38 | 39 | while (offset < len) { 40 | offset += 1; 41 | 42 | var request = { 43 | file : buffer.readUInt16BE(offset), 44 | address : buffer.readUInt16BE(offset + 2), 45 | values : [] 46 | }; 47 | var total = buffer.readUInt16BE(offset + 4); 48 | 49 | offset += 6; 50 | 51 | for (var i = 0; i < total; i++) { 52 | request.values.push(buffer.slice(offset, offset + 2)); 53 | 54 | offset += 2; 55 | } 56 | 57 | requests.push(request); 58 | } 59 | 60 | return requests; 61 | }; 62 | 63 | exports.buildResponse = exports.buildRequest; 64 | exports.parseResponse = exports.parseRequest; 65 | -------------------------------------------------------------------------------- /lib/protocol/WRITE_MULTIPLE_COILS.js: -------------------------------------------------------------------------------- 1 | var Helpers = require("../Helpers"); 2 | var Buff = require("../Buffer"); 3 | 4 | exports.code = 0x0F; 5 | 6 | exports.buildRequest = function (address, values) { 7 | var data = Helpers.bitsToBuffer(values); 8 | var buffer = Buff.alloc(data.length + 4); 9 | 10 | buffer.writeUInt16BE(address, 0); 11 | buffer.writeUInt16BE(values.length, 2); 12 | data.copy(buffer, 4); 13 | 14 | return buffer; 15 | }; 16 | exports.parseRequest = function (buffer) { 17 | if (buffer.length < 5) return null; 18 | 19 | return { 20 | address : buffer.readUInt16BE(0), 21 | quantity : buffer.readUInt16BE(2), 22 | values : Helpers.bufferToBits(buffer.slice(4, 5 + buffer.readUInt8(4))) 23 | }; 24 | }; 25 | 26 | exports.buildResponse = Helpers.buildAddressQuantity; 27 | exports.parseResponse = Helpers.parseAddressQuantity; 28 | -------------------------------------------------------------------------------- /lib/protocol/WRITE_MULTIPLE_REGISTERS.js: -------------------------------------------------------------------------------- 1 | var Helpers = require("../Helpers"); 2 | var Buff = require("../Buffer"); 3 | 4 | exports.code = 0x10; 5 | 6 | exports.buildRequest = function (address, values) { 7 | var buffer = Buff.alloc(5 + (values.length * 2)); 8 | 9 | buffer.writeUInt16BE(address, 0); 10 | buffer.writeUInt16BE(values.length, 2); 11 | buffer.writeUInt8(values.length * 2, 4); 12 | 13 | Helpers.copyBufferBlocks(buffer, values, 5); 14 | 15 | return buffer; 16 | }; 17 | exports.parseRequest = function (buffer) { 18 | if (buffer.length < 5) return null; 19 | 20 | var data = { 21 | address : buffer.readUInt16BE(0), 22 | quantity : buffer.readUInt16BE(2), 23 | values : [] 24 | }; 25 | 26 | var blocks = buffer.readUInt8(4) / 2; 27 | 28 | for (var i = 0; i < blocks; i++) { 29 | data.values.push(buffer.slice(5 + (i * 2), 7 + (i * 2))); 30 | } 31 | 32 | return data; 33 | }; 34 | 35 | exports.buildResponse = Helpers.buildAddressQuantity; 36 | exports.parseResponse = Helpers.parseAddressQuantity; 37 | -------------------------------------------------------------------------------- /lib/protocol/WRITE_SINGLE_COIL.js: -------------------------------------------------------------------------------- 1 | var Helpers = require("../Helpers"); 2 | var Buff = require("../Buffer"); 3 | 4 | exports.code = 0x05; 5 | 6 | exports.buildRequest = function (address, value) { 7 | var buffer = Buff.alloc(4); 8 | 9 | buffer.writeUInt16BE(address, 0); 10 | 11 | buffer[2] = (value ? 0xFF : 0x00); 12 | buffer[3] = 0x00; 13 | 14 | return buffer; 15 | }; 16 | 17 | exports.parseRequest = function (buffer) { 18 | var data = Helpers.parseAddressValue(buffer); 19 | 20 | if (data === null) return null; 21 | 22 | data.value = (data.value[0] == 0xFF && data.value[1] == 0x00 ? 1 : 0); 23 | 24 | return data; 25 | }; 26 | 27 | exports.buildResponse = exports.buildRequest; 28 | exports.parseResponse = exports.parseRequest; 29 | -------------------------------------------------------------------------------- /lib/protocol/WRITE_SINGLE_REGISTER.js: -------------------------------------------------------------------------------- 1 | var Helpers = require("../Helpers"); 2 | var Buff = require("../Buffer"); 3 | 4 | exports.code = 0x06; 5 | 6 | exports.buildRequest = function (address, value) { 7 | var buffer = Buff.alloc(4); 8 | 9 | buffer.writeUInt16BE(address, 0); 10 | value.copy(buffer, 2, 0, 2); 11 | 12 | return buffer; 13 | }; 14 | exports.parseRequest = Helpers.parseAddressValue; 15 | 16 | exports.buildResponse = exports.buildRequest; 17 | exports.parseResponse = Helpers.parseAddressValue; 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "modbus-pdu", 3 | "description" : "Modbus base PDU", 4 | "version" : "1.14.0", 5 | "author" : "Diogo Resende ", 6 | "license" : "MIT", 7 | "main" : "lib/Modbus.js", 8 | "repository" : "http://github.com/node-modbus/pdu.git", 9 | "keywords" : [ "modbus", "pdu", "exception" ], 10 | "scripts" : { "test": "node test/run" }, 11 | "devDependencies" : { 12 | "mocha" : "10.0.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/help.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var modbus = require(".."); 3 | 4 | exports.modbus = modbus; 5 | exports.trials = 100; 6 | 7 | exports.startEndAddress = function startEndAddress(fc, start, end) { 8 | var buffer = new Buffer(5); 9 | 10 | buffer.writeUInt8(fc, 0); 11 | buffer.writeUInt16BE(start, 1); 12 | buffer.writeUInt16BE(end - start + 1, 3); 13 | 14 | return buffer; 15 | }; 16 | 17 | exports.addressValue = function addressValue(fc, address, blocks) { 18 | var buffer = new Buffer(3 + (blocks.length * 2)); 19 | 20 | buffer.writeUInt8(fc, 0); 21 | buffer.writeUInt16BE(address, 1); 22 | 23 | for (var i = 0; i < blocks.length; i++) { 24 | blocks[i].copy(buffer, 3 + (i * 2), 0, 2); 25 | } 26 | 27 | return buffer; 28 | }; 29 | 30 | exports.startEndValues = function startEndValues(fc, start, end, values) { 31 | var buffer = new Buffer(6 + values.reduce(function (total, value) { return total + value.length }, 0)); 32 | 33 | buffer.writeUInt8(fc, 0); 34 | buffer.writeUInt16BE(start, 1); 35 | buffer.writeUInt16BE(end - start + 1, 3); 36 | buffer.writeUInt8(buffer.length - 6, 5); 37 | 38 | var offset = 6; 39 | 40 | values.map(function (value) { 41 | value.copy(buffer, offset); 42 | offset += value.length; 43 | }); 44 | 45 | return buffer; 46 | }; 47 | 48 | exports.randomBitList = function randomBitList(len) { 49 | var bits = []; 50 | 51 | for (var j = 0; j < len; j++) { 52 | bits.push(Math.random() > .5 ? 1 : 0); 53 | } 54 | 55 | while (bits.length % 8 > 0) { 56 | bits.push(0); 57 | } 58 | 59 | return bits; 60 | }; 61 | 62 | exports.randomBlockList = function randomBlockList(len, block_len) { 63 | var blocks = []; 64 | 65 | for (var j = 0; j < len; j++) { 66 | blocks.push(this.randomBlock(block_len)); 67 | } 68 | 69 | return blocks; 70 | }; 71 | 72 | exports.randomBlock = function randomBlock(len) { 73 | var buffer = new Buffer(len); 74 | 75 | for (var i = 0; i < buffer.length; i++) { 76 | buffer[i] = Math.round(Math.random() * 255); 77 | } 78 | 79 | return buffer; 80 | }; 81 | 82 | exports.bitsToBuffer = function bitsToBuffer(bits) { 83 | var buffer = new Buffer(Math.ceil(bits.length / 8) + 1); 84 | var i; 85 | 86 | buffer.fill(0x00); 87 | buffer[0] = buffer.length - 1; 88 | 89 | for (var index = 0; index < bits.length; index++) { 90 | i = Math.floor(index / 8) + 1; 91 | 92 | buffer[i] >>= 1; 93 | if (bits[index]) { 94 | buffer[i] |= 0x80; 95 | } 96 | } 97 | 98 | i = bits.length - (Math.floor(bits.length / 8) * 8); 99 | if (i > 0) { 100 | i = 8 - i; 101 | while (i > 0) { 102 | buffer[buffer.length - 1] >>= 1; 103 | i -= 1; 104 | } 105 | } 106 | 107 | return buffer; 108 | }; 109 | -------------------------------------------------------------------------------- /test/integration/exceptions.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var Help = require("../help"); 3 | 4 | describe("Exceptions", function () { 5 | it("should allow alls functions and codes", function () { 6 | assert.deepEqual( 7 | new Buffer([ 0x81, 0x01 ]), 8 | Help.modbus.ReadCoils.Exception.build(Help.modbus.Exception.IllegalFunction) 9 | ); 10 | assert.deepEqual( 11 | new Buffer([ 0x82, 0x02 ]), 12 | Help.modbus.ReadDiscreteInputs.Exception.build(Help.modbus.Exception.IllegalDataAddress) 13 | ); 14 | assert.deepEqual( 15 | new Buffer([ 0x94, 0x03 ]), 16 | Help.modbus.ReadFileRecord.Exception.build(Help.modbus.Exception.IllegalDataValue) 17 | ); 18 | assert.deepEqual( 19 | new Buffer([ 0x83, 0x04 ]), 20 | Help.modbus.ReadHoldingRegisters.Exception.build(Help.modbus.Exception.ServerDeviceFailure) 21 | ); 22 | assert.deepEqual( 23 | new Buffer([ 0x84, 0x05 ]), 24 | Help.modbus.ReadInputRegisters.Exception.build(Help.modbus.Exception.Aknowledge) 25 | ); 26 | assert.deepEqual( 27 | new Buffer([ 0x95, 0x06 ]), 28 | Help.modbus.WriteFileRecord.Exception.build(Help.modbus.Exception.ServerDeviceBusy) 29 | ); 30 | assert.deepEqual( 31 | new Buffer([ 0x90, 0x08 ]), 32 | Help.modbus.WriteMultipleRegisters.Exception.build(Help.modbus.Exception.MemoryParityError) 33 | ); 34 | assert.deepEqual( 35 | new Buffer([ 0x85, 0x0A ]), 36 | Help.modbus.WriteSingleCoil.Exception.build(Help.modbus.Exception.GatewayPathUnavailable) 37 | ); 38 | assert.deepEqual( 39 | new Buffer([ 0x86, 0x0B ]), 40 | Help.modbus.WriteSingleRegister.Exception.build(Help.modbus.Exception.GatewayTargetDeviceFailedToRespond) 41 | ); 42 | }); 43 | 44 | it("should parse buffers with function code and exception", function () { 45 | assert.deepEqual( 46 | { code: "ReadCoils", exception: "IllegalFunction" }, 47 | Help.modbus.Exception.parse(new Buffer([ 0x81, 0x01 ])) 48 | ); 49 | assert.deepEqual( 50 | { code: "WriteSingleCoil", exception: "ServerDeviceFailure" }, 51 | Help.modbus.Exception.parse(new Buffer([ 0x85, 0x04 ])) 52 | ); 53 | assert.deepEqual( 54 | { code: "WriteFileRecord", exception: "GatewayTargetDeviceFailedToRespond" }, 55 | Help.modbus.Exception.parse(new Buffer([ 0x95, 0x0B ])) 56 | ); 57 | }); 58 | 59 | it("should support creating errors using just the string code", function () { 60 | assert.equal( 61 | Help.modbus.Exception.error("MemoryParityError").code, 62 | 0x08 63 | ); 64 | assert.equal( 65 | Help.modbus.Exception.error("GatewayPathUnavailable").code, 66 | 0x0A 67 | ); 68 | }); 69 | 70 | it("should support creating errors using just the numeric code", function () { 71 | assert.equal( 72 | Help.modbus.Exception.error(0x08).message, 73 | "MemoryParityError" 74 | ); 75 | assert.equal( 76 | Help.modbus.Exception.error(0x0A).message, 77 | "GatewayPathUnavailable" 78 | ); 79 | }); 80 | 81 | it("should support creating errors using an unknown code", function () { 82 | var err1 = Help.modbus.Exception.error(0xFA); 83 | var err2 = Help.modbus.Exception.error("An Error"); 84 | 85 | assert.equal(err1.message, "250"); // 0xFA = 250 86 | assert.equal(err1.code, undefined); 87 | 88 | assert.equal(err2.message, "An Error"); 89 | assert.equal(err2.code, undefined); 90 | }); 91 | }); 92 | -------------------------------------------------------------------------------- /test/integration/get-comm-event-counter.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var Help = require("../help"); 3 | 4 | describe("Get Comm Event Counter", function () { 5 | it("should be [] => [ status, event_count ]", function () { 6 | for (var i = 0; i < Help.trials; i++) { 7 | var status = Math.round(Math.random() * 100); 8 | var event_count = Math.round(Math.random() * 100); 9 | 10 | assert.deepEqual( 11 | {}, 12 | Help.modbus.GetCommEventCounter.Request.parse( 13 | Help.modbus.GetCommEventCounter.Request.build() 14 | ) 15 | ); 16 | assert.deepEqual( 17 | { status: status, event_count: event_count }, 18 | Help.modbus.GetCommEventCounter.Response.parse( 19 | Help.modbus.GetCommEventCounter.Response.build(status, event_count) 20 | ) 21 | ); 22 | } 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/integration/get-comm-event-log.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var Help = require("../help"); 3 | 4 | describe("Get Comm Event Log", function () { 5 | it("should be [] => [ status, event_count, message_count, events ]", function () { 6 | for (var i = 0; i < Help.trials; i++) { 7 | var status = Math.round(Math.random() * 100); 8 | var event_count = Math.round(Math.random() * 100); 9 | var message_count = Math.round(Math.random() * 100); 10 | var events = Help.randomBlock(Math.random() * 4); 11 | 12 | assert.deepEqual( 13 | {}, 14 | Help.modbus.GetCommEventLog.Request.parse( 15 | Help.modbus.GetCommEventLog.Request.build() 16 | ) 17 | ); 18 | assert.deepEqual( 19 | { status: status, event_count: event_count, message_count: message_count, events: events }, 20 | Help.modbus.GetCommEventLog.Response.parse( 21 | Help.modbus.GetCommEventLog.Response.build(status, event_count, message_count, events) 22 | ) 23 | ); 24 | } 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/integration/helpers.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var Help = require("../help"); 3 | 4 | describe("Helpers", function () { 5 | it("blocksToBuffer", function () { 6 | assert.deepEqual( 7 | Help.modbus.Helpers.blocksToBuffer([ new Buffer([ 0x01, 0x02 ]), new Buffer([ 0x03, 0x04 ]) ]), 8 | new Buffer([ 0x04, 0x01, 0x02, 0x03, 0x04 ]) 9 | ); 10 | 11 | assert.deepEqual( 12 | Help.modbus.Helpers.blocksToBuffer([ new Buffer([ 0x01, 0x02 ]), new Buffer([ 0x03 ]) ]), 13 | new Buffer([ 0x04, 0x01, 0x02, 0x03, 0x00 ]) 14 | ); 15 | 16 | assert.deepEqual( 17 | Help.modbus.Helpers.blocksToBuffer([]), 18 | new Buffer([ 0x00 ]) 19 | ); 20 | 21 | assert.throws( 22 | function () { 23 | Help.modbus.Helpers.blocksToBuffer(); 24 | }, 25 | /undefined/ 26 | ); 27 | }); 28 | 29 | it("bitsToBuffer", function () { 30 | assert.deepEqual( 31 | Help.modbus.Helpers.bitsToBuffer([ 1, 1, 0, 1 ]), 32 | new Buffer([ 0x01, 0x0B ]) 33 | ); 34 | 35 | assert.deepEqual( 36 | Help.modbus.Helpers.bitsToBuffer([ 1, 1, 0, 1, 1, 0, 1, 0 ]), 37 | new Buffer([ 0x01, 0x5B ]) 38 | ); 39 | 40 | assert.deepEqual( 41 | Help.modbus.Helpers.bitsToBuffer([ 1, 1, 0, 1, 1, 0, 1, 0, 1 ]), 42 | new Buffer([ 0x02, 0x5B, 0x01 ]) 43 | ); 44 | }); 45 | 46 | it("bufferToBlocks", function () { 47 | assert.deepEqual( 48 | Help.modbus.Helpers.bufferToBlocks(new Buffer([ 0x04, 0x01, 0x02, 0x03, 0x04 ])), 49 | [ new Buffer([ 0x01, 0x02 ]), new Buffer([ 0x03, 0x04 ]) ] 50 | ); 51 | 52 | assert.deepEqual( 53 | Help.modbus.Helpers.bufferToBlocks(new Buffer([ 0x00 ])), 54 | [] 55 | ); 56 | 57 | assert.throws( 58 | function () { 59 | Help.modbus.Helpers.bufferToBlocks(); 60 | }, 61 | /undefined/ 62 | ); 63 | }); 64 | 65 | it("bufferToBits", function () { 66 | assert.deepEqual( 67 | Help.modbus.Helpers.bufferToBits(new Buffer([ 0x01, 0x0B ])), 68 | [ 1, 1, 0, 1, 0, 0, 0, 0 ] 69 | ); 70 | 71 | assert.deepEqual( 72 | Help.modbus.Helpers.bufferToBits(new Buffer([ 0x01, 0x5B ])), 73 | [ 1, 1, 0, 1, 1, 0, 1, 0 ] 74 | ); 75 | 76 | assert.deepEqual( 77 | Help.modbus.Helpers.bufferToBits(new Buffer([ 0x02, 0x5B, 0x01 ])), 78 | [ 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0 ] 79 | ); 80 | }); 81 | }); 82 | -------------------------------------------------------------------------------- /test/integration/invalid.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var Help = require("../help"); 3 | 4 | describe("Invalid requests", function () { 5 | it("should return null and not throw", function () { 6 | assert.equal( 7 | null, 8 | Help.modbus.ReadCoils.Request.parse(new Buffer([ 0x03, 0x04 ])) 9 | ); 10 | assert.equal( 11 | null, 12 | Help.modbus.ReadInputRegisters.Request.parse(new Buffer([ 0x07, 0x04, 0x84 ])) 13 | ); 14 | }); 15 | }); 16 | 17 | describe("Invalid responses", function () { 18 | it("should throw", function () { 19 | assert.throws( 20 | function () { 21 | Help.modbus.ReadCoils.Response.build(); 22 | }, 23 | /overflow/ 24 | ); 25 | assert.throws( 26 | function () { 27 | Help.modbus.ReadCoils.Response.build(null); 28 | }, 29 | /overflow/ 30 | ); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /test/integration/mask-write-register.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var Help = require("../help"); 3 | 4 | describe("Mask Write Register", function () { 5 | it("should be [ address, andmask, ormask ] => [ address, andmask, ormask ]", function () { 6 | for (var i = 0; i < Help.trials; i++) { 7 | var address = Math.round(Math.random() * 100); 8 | var andmask = Help.randomBlock(2); 9 | var ormask = Help.randomBlock(2); 10 | 11 | assert.deepEqual( 12 | { address : address, andmask : andmask, ormask : ormask }, 13 | Help.modbus.MaskWriteRegister.Request.parse( 14 | Help.modbus.MaskWriteRegister.Request.build(address, andmask, ormask) 15 | ) 16 | ); 17 | assert.deepEqual( 18 | { address : address, andmask : andmask, ormask : ormask }, 19 | Help.modbus.MaskWriteRegister.Response.parse( 20 | Help.modbus.MaskWriteRegister.Response.build(address, andmask, ormask) 21 | ) 22 | ); 23 | } 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/integration/parser.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var Help = require("../help"); 3 | 4 | describe("Generic parser", function () { 5 | it("should support requests", function () { 6 | assert.deepEqual( 7 | Help.modbus.Request(new Buffer([ 0x01, 0x00, 0x0a, 0x00, 0x40 ])), 8 | { code: "ReadCoils", address: 10, quantity: 64 } 9 | ); 10 | 11 | assert.deepEqual( 12 | Help.modbus.Request(new Buffer([ 0x0F, 0x00, 0x13, 0x00, 0x0A, 0x02, 0xCD, 0x01 ])), 13 | { code: "WriteMultipleCoils", address: 19, quantity: 10, values: [ 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 ] } 14 | ); 15 | }); 16 | 17 | it("should support responses", function () { 18 | assert.deepEqual( 19 | Help.modbus.Response(new Buffer([ 0x81, 0x01 ])), 20 | { code: "ReadCoils", exception: "IllegalFunction" } 21 | ); 22 | 23 | assert.deepEqual( 24 | Help.modbus.Response(new Buffer([ 0x01, 0x03, 0x04, 0x02, 0xCD ])), 25 | { code: "ReadCoils", data: [ 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1 ] } 26 | ); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/integration/read-coils.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var Help = require("../help"); 3 | 4 | describe("Read Coils", function () { 5 | it("should be [ address, quantity ] => [ bit, bit, .. ]", function () { 6 | for (var i = 0; i < Help.trials; i++) { 7 | var address = Math.round(Math.random() * 100); 8 | var bits = Help.randomBitList(10 + Math.round(Math.random() * 4000)); 9 | 10 | assert.deepEqual( 11 | { address : address, quantity : bits.length }, 12 | Help.modbus.ReadCoils.Request.parse( 13 | Help.modbus.ReadCoils.Request.build(address, bits.length) 14 | ) 15 | ); 16 | 17 | if (bits.length > 2040) { 18 | assert.throws( 19 | function () { 20 | Help.modbus.ReadCoils.Response.build(bits) 21 | }, 22 | /(out of bounds|RangeError)/ 23 | ); 24 | } else { 25 | assert.deepEqual( 26 | bits, 27 | Help.modbus.ReadCoils.Response.parse( 28 | Help.modbus.ReadCoils.Response.build(bits) 29 | ) 30 | ); 31 | } 32 | } 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /test/integration/read-device-identification.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var Help = require("../help"); 3 | 4 | describe("Read Device Identification", function () { 5 | it("should be [ type, id ] => [ type, conformity, more, next, object, object, .. ]", function () { 6 | for (var i = 0; i < Help.trials; i++) { 7 | var type = "BasicDeviceIdentification"; 8 | var id = "MajorMinorRevision"; 9 | var objects = [{ 10 | id: "VendorName", value: new Buffer("ThinkDigital") // company 11 | }, { 12 | id: "ProductCode", value: new Buffer("modbus-pdu") // product 13 | }]; 14 | 15 | assert.deepEqual( 16 | { type : type, id : id }, 17 | Help.modbus.ReadDeviceIdentification.Request.parse( 18 | Help.modbus.ReadDeviceIdentification.Request.build(type, id) 19 | ) 20 | ); 21 | assert.deepEqual( 22 | { type: type, conformity: type, more: true, next: 11, objects: objects }, 23 | Help.modbus.ReadDeviceIdentification.Response.parse( 24 | Help.modbus.ReadDeviceIdentification.Response.build(type, type, true, objects) 25 | ) 26 | ); 27 | } 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/integration/read-discrete-inputs.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var Help = require("../help"); 3 | 4 | describe("Read Discrete Inputs", function () { 5 | it("should be [ address, quantity ] => [ bit, bit, .. ]", function () { 6 | for (var i = 0; i < Help.trials; i++) { 7 | var address = Math.round(Math.random() * 100); 8 | var bits = Help.randomBitList(10 + Math.round(Math.random() * 4000)); 9 | 10 | assert.deepEqual( 11 | { address : address, quantity : bits.length }, 12 | Help.modbus.ReadDiscreteInputs.Request.parse( 13 | Help.modbus.ReadDiscreteInputs.Request.build(address, bits.length) 14 | ) 15 | ); 16 | 17 | if (bits.length > 2040) { 18 | assert.throws( 19 | function () { 20 | Help.modbus.ReadDiscreteInputs.Response.build(bits) 21 | }, 22 | /(out of bounds|RangeError)/ 23 | ); 24 | } else { 25 | assert.deepEqual( 26 | bits, 27 | Help.modbus.ReadDiscreteInputs.Response.parse( 28 | Help.modbus.ReadDiscreteInputs.Response.build(bits) 29 | ) 30 | ); 31 | } 32 | } 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /test/integration/read-exception-status.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var Help = require("../help"); 3 | 4 | describe("Read Exception Status", function () { 5 | it("should be [] => [ data ]", function () { 6 | for (var i = 0; i < Help.trials; i++) { 7 | var data = Math.round(Math.random() * 100); 8 | 9 | assert.deepEqual( 10 | {}, 11 | Help.modbus.ReadExceptionStatus.Request.parse( 12 | Help.modbus.ReadExceptionStatus.Request.build() 13 | ) 14 | ); 15 | assert.deepEqual( 16 | { data: data }, 17 | Help.modbus.ReadExceptionStatus.Response.parse( 18 | Help.modbus.ReadExceptionStatus.Response.build(data) 19 | ) 20 | ); 21 | } 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/integration/read-fifo-queue.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var Help = require("../help"); 3 | 4 | describe("Read FIFO Queue", function () { 5 | it("should be [ address ] => [ register, .. ]", function () { 6 | for (var i = 0; i < Help.trials; i++) { 7 | var address = Math.round(Math.random() * 100); 8 | var blocks = Help.randomBlockList(5, 2); 9 | 10 | assert.deepEqual( 11 | { address: address }, 12 | Help.modbus.ReadFifoQueue.Request.parse( 13 | Help.modbus.ReadFifoQueue.Request.build(address) 14 | ) 15 | ); 16 | assert.deepEqual( 17 | blocks, 18 | Help.modbus.ReadFifoQueue.Response.parse( 19 | Help.modbus.ReadFifoQueue.Response.build(blocks) 20 | ) 21 | ); 22 | } 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/integration/read-file-record.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var Help = require("../help"); 3 | 4 | describe("Read File Record", function () { 5 | it("should be [{ file, address, length }, ..] => [ data, .. ]", function () { 6 | for (var i = 0; i < Help.trials; i++) { 7 | var req = []; 8 | var res = []; 9 | var total = (10 + Math.round(Math.random() * 40)); 10 | 11 | for (var j = 0; j < total; j++) { 12 | var address = Math.round(Math.random() * 10); 13 | 14 | req.push({ 15 | file : Math.round(Math.random() * 10), 16 | address : address, 17 | length : Math.round(Math.random() * 10) 18 | }); 19 | 20 | res.push(Help.randomBlockList(5, 2)); 21 | } 22 | 23 | if (total > 36) { 24 | assert.throws( 25 | function () { 26 | Help.modbus.ReadFileRecord.Request.build(req); 27 | }, 28 | /(out of bounds|RangeError)/ 29 | ); 30 | } else { 31 | assert.deepEqual( 32 | req, 33 | Help.modbus.ReadFileRecord.Request.parse( 34 | Help.modbus.ReadFileRecord.Request.build(req) 35 | ) 36 | ); 37 | } 38 | 39 | if (total > 21) { 40 | assert.throws( 41 | function () { 42 | Help.modbus.ReadFileRecord.Response.build(res); 43 | }, 44 | /(out of bounds|RangeError)/ 45 | ); 46 | } else { 47 | assert.deepEqual( 48 | res, 49 | Help.modbus.ReadFileRecord.Response.parse( 50 | Help.modbus.ReadFileRecord.Response.build(res) 51 | ) 52 | ); 53 | } 54 | } 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /test/integration/read-holding-registers.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var Help = require("../help"); 3 | 4 | describe("Read Holding Registers", function () { 5 | it("should be [ address, quantity ] => [ buffer block, buffer block, .. ]", function () { 6 | for (var i = 0; i < Help.trials; i++) { 7 | var address = Math.round(Math.random() * 100); 8 | var blocks = Help.randomBlockList(10 + Math.round(Math.random() * 500), 2); 9 | 10 | assert.deepEqual( 11 | { address : address, quantity : blocks.length }, 12 | Help.modbus.ReadHoldingRegisters.Request.parse( 13 | Help.modbus.ReadHoldingRegisters.Request.build(address, blocks.length) 14 | ) 15 | ); 16 | 17 | if (blocks.length > 127) { 18 | assert.throws( 19 | function () { 20 | Help.modbus.ReadHoldingRegisters.Response.build(blocks) 21 | }, 22 | /(out of bounds|RangeError)/ 23 | ); 24 | } else { 25 | assert.deepEqual( 26 | blocks, 27 | Help.modbus.ReadHoldingRegisters.Response.parse( 28 | Help.modbus.ReadHoldingRegisters.Response.build(blocks) 29 | ) 30 | ); 31 | } 32 | } 33 | }); 34 | 35 | it("should be [ address, quantity ] => buffer", function () { 36 | for (var i = 0; i < Help.trials; i++) { 37 | var address = Math.round(Math.random() * 100); 38 | var blocks = Help.randomBlock(7); 39 | 40 | assert.deepEqual( 41 | { address : address, quantity : blocks.length }, 42 | Help.modbus.ReadHoldingRegisters.Request.parse( 43 | Help.modbus.ReadHoldingRegisters.Request.build(address, blocks.length) 44 | ) 45 | ); 46 | } 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /test/integration/read-input-registers.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var Help = require("../help"); 3 | 4 | describe("Read Input Registers", function () { 5 | it("should be [ address, quantity ] => [ buffer block, buffer block, .. ]", function () { 6 | for (var i = 0; i < Help.trials; i++) { 7 | var address = Math.round(Math.random() * 100); 8 | var blocks = Help.randomBlockList(10 + Math.round(Math.random() * 500), 2); 9 | 10 | assert.deepEqual( 11 | { address : address, quantity : blocks.length }, 12 | Help.modbus.ReadInputRegisters.Request.parse( 13 | Help.modbus.ReadInputRegisters.Request.build(address, blocks.length) 14 | ) 15 | ); 16 | 17 | if (blocks.length > 127) { 18 | assert.throws( 19 | function () { 20 | Help.modbus.ReadInputRegisters.Response.build(blocks) 21 | }, 22 | /(out of bounds|RangeError)/ 23 | ); 24 | } else { 25 | assert.deepEqual( 26 | blocks, 27 | Help.modbus.ReadInputRegisters.Response.parse( 28 | Help.modbus.ReadInputRegisters.Response.build(blocks) 29 | ) 30 | ); 31 | } 32 | } 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /test/integration/read-write-multiple-registers.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var Help = require("../help"); 3 | 4 | describe("Read Write Multiple Registers", function () { 5 | it("should be [ read_address, read_quantity, write_address, values ] => [ values ]", function () { 6 | for (var i = 0; i < Help.trials; i++) { 7 | var read_address = Math.round(Math.random() * 100); 8 | var read_quantity = 5; 9 | var write_address = Math.round(Math.random() * 100); 10 | var values = Help.randomBlockList(read_quantity, 2); 11 | 12 | assert.deepEqual( 13 | { read_address: read_address, read_quantity: read_quantity, write_address: write_address, values: values }, 14 | Help.modbus.ReadWriteMultipleRegisters.Request.parse( 15 | Help.modbus.ReadWriteMultipleRegisters.Request.build(read_address, read_quantity, write_address, values) 16 | ) 17 | ); 18 | assert.deepEqual( 19 | { values: values }, 20 | Help.modbus.ReadWriteMultipleRegisters.Response.parse( 21 | Help.modbus.ReadWriteMultipleRegisters.Response.build(values) 22 | ) 23 | ); 24 | } 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/integration/write-file-record.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var Help = require("../help"); 3 | 4 | describe("Write File Record", function () { 5 | it("should be [{ file, address }, ..] => [ data, .. ]", function () { 6 | for (var i = 0; i < Help.trials; i++) { 7 | var req = []; 8 | var res = []; 9 | var total = (10 + Math.round(Math.random() * 40)); 10 | 11 | for (var j = 0; j < total; j++) { 12 | var address = Math.round(Math.random() * 10); 13 | 14 | req.push({ 15 | file : Math.round(Math.random() * 10), 16 | address : address, 17 | values : Help.randomBlockList(5, 2) 18 | }); 19 | 20 | res.push({ 21 | file : Math.round(Math.random() * 10), 22 | address : address, 23 | values : Help.randomBlockList(5, 2) 24 | }); 25 | } 26 | 27 | if (total > 15) { 28 | assert.throws( 29 | function () { 30 | Help.modbus.WriteFileRecord.Request.build(req); 31 | }, 32 | /(out of bounds|RangeError)/ 33 | ); 34 | } else { 35 | assert.deepEqual( 36 | req, 37 | Help.modbus.WriteFileRecord.Request.parse( 38 | Help.modbus.WriteFileRecord.Request.build(req) 39 | ) 40 | ); 41 | } 42 | 43 | if (total > 15) { 44 | assert.throws( 45 | function () { 46 | Help.modbus.WriteFileRecord.Request.build(req); 47 | }, 48 | /(out of bounds|RangeError)/ 49 | ); 50 | } else { 51 | assert.deepEqual( 52 | res, 53 | Help.modbus.WriteFileRecord.Response.parse( 54 | Help.modbus.WriteFileRecord.Response.build(res) 55 | ) 56 | ); 57 | } 58 | } 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /test/integration/write-multiple-coils.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var Help = require("../help"); 3 | 4 | describe("Write Multiple Coils", function () { 5 | it("should be [ start, end, bits ] => [ start, end ]", function () { 6 | for (var i = 0; i < Help.trials; i++) { 7 | var address = Math.round(Math.random() * 100); 8 | var bits = Help.randomBitList(13); 9 | 10 | assert.deepEqual( 11 | { address : address, quantity: bits.length, values : bits }, 12 | Help.modbus.WriteMultipleCoils.Request.parse( 13 | Help.modbus.WriteMultipleCoils.Request.build(address, bits) 14 | ) 15 | ); 16 | assert.deepEqual( 17 | { address : address, quantity : bits.length }, 18 | Help.modbus.WriteMultipleCoils.Response.parse( 19 | Help.modbus.WriteMultipleCoils.Response.build(address, bits.length) 20 | ) 21 | ); 22 | } 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/integration/write-multiple-registers.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var Help = require("../help"); 3 | 4 | describe("Write Multiple Registers", function () { 5 | it("should be [ start, end, blocks ] => [ start, end ]", function () { 6 | for (var i = 0; i < Help.trials; i++) { 7 | var address = Math.round(Math.random() * 100); 8 | var blocks = Help.randomBlockList(5, 2); 9 | 10 | assert.deepEqual( 11 | { address : address, quantity : blocks.length, values : blocks }, 12 | Help.modbus.WriteMultipleRegisters.Request.parse( 13 | Help.modbus.WriteMultipleRegisters.Request.build(address, blocks) 14 | ) 15 | ); 16 | assert.deepEqual( 17 | { address : address, quantity : blocks.length }, 18 | Help.modbus.WriteMultipleRegisters.Response.parse( 19 | Help.modbus.WriteMultipleRegisters.Response.build(address, blocks.length) 20 | ) 21 | ); 22 | } 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/integration/write-single-coil.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var Help = require("../help"); 3 | 4 | describe("Write Single Coil", function () { 5 | it("should be [ address, value ] => [ address, value ]", function () { 6 | for (var i = 0; i < Help.trials; i++) { 7 | var address = Math.round(Math.random() * 100); 8 | var value = (Math.random() > .5 ? 1 : 0); 9 | 10 | assert.deepEqual( 11 | { address : address, value : value }, 12 | Help.modbus.WriteSingleCoil.Request.parse( 13 | Help.modbus.WriteSingleCoil.Request.build(address, value) 14 | ) 15 | ); 16 | assert.deepEqual( 17 | { address : address, value : value }, 18 | Help.modbus.WriteSingleCoil.Response.parse( 19 | Help.modbus.WriteSingleCoil.Response.build(address, value) 20 | ) 21 | ); 22 | } 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/integration/write-single-register.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var Help = require("../help"); 3 | 4 | describe("Write Single Register", function () { 5 | it("should be [ address, block ] => [ address, block ]", function () { 6 | for (var i = 0; i < Help.trials; i++) { 7 | var address = Math.round(Math.random() * 100); 8 | var value = Help.randomBlock(2); 9 | 10 | assert.deepEqual( 11 | { address : address, value : value }, 12 | Help.modbus.WriteSingleRegister.Request.parse( 13 | Help.modbus.WriteSingleRegister.Request.build(address, value) 14 | ) 15 | ); 16 | assert.deepEqual( 17 | { address : address, value : value }, 18 | Help.modbus.WriteSingleRegister.Response.parse( 19 | Help.modbus.WriteSingleRegister.Response.build(address, value) 20 | ) 21 | ); 22 | } 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/run.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"); 2 | var path = require("path"); 3 | var Mocha = require("mocha"); 4 | var mocha = new Mocha(); 5 | var location = path.normalize(path.join(__dirname, "integration")); 6 | 7 | fs.readdirSync(location).filter(function (file) { 8 | return (file.substr(-3) == '.js'); 9 | }).forEach(function (file) { 10 | mocha.addFile(path.join(location, file)); 11 | }); 12 | 13 | mocha.run(function (failures) { 14 | process.exit(failures); 15 | }); 16 | --------------------------------------------------------------------------------