jsmodbus
Version:
Implementation for the Serial/TCP Modbus protocol.
880 lines (758 loc) • 25.2 kB
JavaScript
/* global describe, it, beforeEach */
const assert = require('assert')
const Modbus = require('../')
const sinon = require('sinon')
const EventEmitter = require('events')
describe('TCP Client Tests.', function () {
let socket
let socketMock
beforeEach(function () {
socket = new EventEmitter()
socket.write = function () {}
socketMock = sinon.mock(socket)
})
/* with the read coils tests we test most of the common errors
* like modbus exceptions, outOfSync errors, timeouts and so on */
describe('Read Coils Tests.', function () {
const ReadCoilsResponseBody = require('../src/response/read-coils.js')
it('should create request from buffer', function () {
const buffer = Buffer.from([
0x01, // fc
0x02, // byte count
0xdd, // coils
0x00
])
const response = ReadCoilsResponseBody.fromBuffer(buffer)
assert.ok(response !== null)
assert.equal(0x01, response.fc)
assert.equal(0x02, response.numberOfBytes)
assert.equal(0x04, response.byteCount)
assert.deepEqual(
[true,
false,
true,
true,
true,
false,
true,
true,
false,
false,
false,
false,
false,
false,
false,
false
], response.valuesAsArray)
})
it('should handle invalid buffer content', function () {
const buffer = Buffer.from([
0x01, // fc
0x02, // byte count
0xdd // coils
])
const response = ReadCoilsResponseBody.fromBuffer(buffer)
assert.ok(response === null)
})
it('should handle a invalid request (invalid quantity)', function (done) {
const client = new Modbus.client.TCP(socket)
socket.emit('connect')
socketMock.expects('write').never()
client.readCoils(10, 0x7D01)
.then(function (resp) {
assert.ok(false)
}).catch(function (e) {
assert.equal('InvalidQuantity', e.message)
socketMock.verify()
done()
})
})
it('should handle a valid request with a exception response', function (done) {
const client = new Modbus.client.TCP(socket)
const response = Buffer.from([
0x00, 0x01, // transaction id
0x00, 0x00, // protocol
0x00, 0x03, // byte count
0x01, // unit id
0x81, // function code
0x01 // exception code
])
socket.emit('connect')
socketMock.expects('write').once()
client.readCoils(10, 11)
.then(function (resp) {
assert.ok(false)
}).catch(function (e) {
assert.equal('ModbusException', e.err)
assert.equal(0x01, e.response.body.fc)
assert.equal(0x01, e.response.body.code)
socketMock.verify()
done()
})
socket.emit('data', response)
})
it('should handle a valid request with timeout', function (done) {
const client = new Modbus.client.TCP(socket, 2, 100) // unit id = 2, timeout = 100ms
socket.emit('connect')
socketMock.expects('write').once()
client.readCoils(10, 11)
.then(function (resp) {
assert.ok(false)
}).catch(function (e) {
assert.equal('Timeout', e.err)
socketMock.verify()
done()
})
})
it('should handle a valid request while offline', function (done) {
const client = new Modbus.client.TCP(socket)
client.readCoils(10, 11)
.then(function (resp) {
assert.ok(false)
}).catch(function (e) {
assert.equal('Offline', e.err)
done()
})
})
it('should handle two valid request while offline', function (done) {
const client = new Modbus.client.TCP(socket)
client.readCoils(10, 11)
.then(function (resp) {
assert.ok(false)
}).catch(function (e) {
assert.equal('Offline', e.err)
})
client.readCoils(11, 12)
.then(function (resp) {
assert.ok(false)
}).catch(function (e) {
assert.equal('Offline', e.err)
done()
})
})
it('should handle two valid requests', function (done) {
const client = new Modbus.client.TCP(socket)
const responseA = Buffer.from([
0x00, 0x01, // transaction id
0x00, 0x00, // protocol
0x00, 0x05, // byte count
0x01, // unit id
0x01, // function code
0x02, // byte count
0xdd, // coils
0x00
])
const responseB = Buffer.from([
0x00, 0x02, // transaction id
0x00, 0x00, // protocol
0x00, 0x05, // byte count
0x01, // unit id
0x01, // function code
0x02, // byte count
0xdd, // coils
0x00
])
socket.emit('connect')
socketMock.expects('write').twice()
client.readCoils(10, 11)
.then(function (resp) {
assert.ok(true)
socket.emit('data', responseB)
}).catch(function (e) {
assert.ok(false)
})
client.readCoils(11, 12)
.then(function (resp) {
assert.ok(true)
done()
}).catch(function (e) {
assert.ok(false)
})
socket.emit('data', responseA)
})
it('should handle a valid request with an out of sync response', function (done) {
const client = new Modbus.client.TCP(socket)
const response = Buffer.from([
0x00, 0x02, // transaction id is WRONG!!!!
0x00, 0x00, // protocol
0x00, 0x05, // byte count
0x01, // unit id
0x01, // function code
0x02, // byte count
0xdd, // coils
0x00
])
socket.emit('connect')
socketMock.expects('write').once()
client.readCoils(10, 11)
.then(function (resp) {
assert.ok(false)
}).catch(function (e) {
assert.equal('OutOfSync', e.err)
socketMock.verify()
done()
})
socket.emit('data', response)
})
it('should handle two valid request with an out of sync response', function (done) {
const client = new Modbus.client.TCP(socket)
const response = Buffer.from([
0x00, 0x02, // transaction id is WRONG!!!!
0x00, 0x00, // protocol
0x00, 0x05, // byte count
0x01, // unit id
0x01, // function code
0x02, // byte count
0xdd, // coils
0x00
])
socket.emit('connect')
socketMock.expects('write').once()
client.readCoils(10, 11)
.then(function (resp) {
assert.ok(false)
}).catch(function (e) {
assert.equal('OutOfSync', e.err)
})
client.readCoils(12, 13)
.then(function () {
assert.ok(false)
}).catch(function (e) {
assert.equal('OutOfSync', e.err)
socketMock.verify()
done()
})
socket.emit('data', response)
})
it('should handle a valid request with an out of sync response (wrong fc)', function (done) {
const client = new Modbus.client.TCP(socket)
const response = Buffer.from([
0x00, 0x01, // transaction id
0x00, 0x00, // protocol
0x00, 0x05, // byte count
0x01, // unit id
0x02, // function code WRONG!!!
0x02, // byte count
0xdd, // coils
0x00
])
socket.emit('connect')
socketMock.expects('write').once()
client.readCoils(10, 11)
.then(function (resp) {
assert.ok(false)
}).catch(function (e) {
assert.equal('OutOfSync', e.err)
socketMock.verify()
done()
})
socket.emit('data', response)
})
it('should handle a valid request with a wrong protocol response', function (done) {
const client = new Modbus.client.TCP(socket)
const response = Buffer.from([
0x00, 0x01, // transaction id
0x00, 0x01, // protocol WRONG !!!!
0x00, 0x05, // byte count
0x01, // unit id
0x01, // function code
0x02, // byte count
0xdd, // coils
0x00
])
socket.emit('connect')
socketMock.expects('write').once()
client.readCoils(10, 11)
.then(function (resp) {
assert.ok(false)
done()
}).catch(function (e) {
assert.equal('Protocol', e.err)
socketMock.verify()
done()
})
socket.emit('data', response)
})
})
describe('Read Discrete Inputs Tests.', function () {
it('should handle a valid request', function (done) {
const client = new Modbus.client.TCP(socket, 2)
const response = Buffer.from([
0x00, 0x01, // transaction id
0x00, 0x00, // protocol
0x00, 0x05, // byte count
0x02, // unit id
0x02, // function code
0x02, // byte count
0xdd, // coils
0x00
])
socket.emit('connect')
socketMock.expects('write').once()
client.readDiscreteInputs(10, 11)
.then(function (resp) {
assert.ok(resp.response)
assert.ok(resp.request)
socketMock.verify()
done()
}).catch(function (error) {
console.error(error)
})
socket.emit('data', response)
})
it('should handle a invalid request (invalid start address)', function (done) {
const client = new Modbus.client.TCP(socket)
socket.emit('connect')
socketMock.expects('write').never()
client.readDiscreteInputs(0xFFFF01, 11)
.then(function (resp) {
assert.ok(false)
}).catch(function (error) {
assert.equal('InvalidStartAddress', error.message)
socketMock.verify()
done()
})
})
it('should handle a invalid request (invalid quantity)', function (done) {
const client = new Modbus.client.TCP(socket)
socket.emit('connect')
socketMock.expects('write').never()
client.readDiscreteInputs(10, 0x7D01)
.then(function (resp) {
assert.ok(false)
}).catch(function (e) {
assert.equal('InvalidQuantity', e.message)
socketMock.verify()
done()
})
})
})
describe('Read Holding Registers Tests.', function () {
it('should handle a valid request', function (done) {
const client = new Modbus.client.TCP(socket, 2)
const response = Buffer.from([
0x00, 0x01, // transaction id
0x00, 0x00, // protocol
0x00, 0x07, // byte count
0x02, // unit id
0x03, // function code
0x04, // byte count
0x43, 0x21, // registers
0x12, 0x34
])
socket.emit('connect')
socketMock.expects('write').once()
client.readHoldingRegisters(2, 2)
.then(function (resp) {
assert.ok(resp.response)
assert.ok(resp.request)
socketMock.verify()
done()
}).catch(function (error) {
console.error(error)
})
socket.emit('data', response)
})
it('should handle a invalid request (invalid start address)', function (done) {
const client = new Modbus.client.TCP(socket)
socket.emit('connect')
socketMock.expects('write').never()
client.readHoldingRegisters(0xFFFF01, 11)
.then(function (resp) {
assert.ok(false)
}).catch(function (error) {
assert.equal('InvalidStartAddress', error.message)
socketMock.verify()
done()
})
})
it('should handle a invalid request (invalid quantity)', function (done) {
const client = new Modbus.client.TCP(socket)
socket.emit('connect')
socketMock.expects('write').never()
client.readHoldingRegisters(10, 0x7D01)
.then(function (resp) {
assert.ok(false)
}).catch(function (e) {
assert.equal('InvalidQuantity', e.message)
socketMock.verify()
done()
})
})
})
describe('Read Input Registers Tests.', function () {
it('should handle a valid request', function (done) {
const client = new Modbus.client.TCP(socket, 2)
const response = Buffer.from([
0x00, 0x01, // transaction id
0x00, 0x00, // protocol
0x00, 0x07, // byte count
0x02, // unit id
0x04, // function code
0x04, // byte count
0x43, 0x21, // registers
0x12, 0x34
])
socket.emit('connect')
socketMock.expects('write').once()
client.readInputRegisters(2, 2)
.then(function (resp) {
assert.ok(resp.response)
assert.ok(resp.request)
socketMock.verify()
done()
}).catch(function (error) {
console.error(error)
})
socket.emit('data', response)
})
it('should handle a invalid request (invalid start address)', function (done) {
const client = new Modbus.client.TCP(socket)
socket.emit('connect')
socketMock.expects('write').never()
client.readInputRegisters(0xFFFF01, 11)
.then(function (resp) {
assert.ok(false)
}).catch(function (error) {
assert.equal('InvalidStartAddress', error.message)
socketMock.verify()
done()
})
})
it('should handle a invalid request (invalid quantity)', function (done) {
const client = new Modbus.client.TCP(socket)
socket.emit('connect')
socketMock.expects('write').never()
client.readInputRegisters(10, 0x7D01)
.then(function (resp) {
assert.ok(false)
}).catch(function (e) {
assert.equal('InvalidQuantity', e.message)
socketMock.verify()
done()
})
})
})
describe('Write Single Coil Tests.', function () {
it('should handle a valid request', function (done) {
const client = new Modbus.client.TCP(socket, 2)
const response = Buffer.from([
0x00, 0x01, // transaction id
0x00, 0x00, // protocol
0x00, 0x06, // byte count
0x02, // unit id
0x05, // function code
0x12, 0x34, // output address
0xFF, 0x00 // output value
])
socket.emit('connect')
socketMock.expects('write').once()
client.writeSingleCoil(2, true)
.then(function (resp) {
assert.ok(resp.response)
assert.ok(resp.request)
socketMock.verify()
done()
}).catch(function (error) {
console.error(error)
})
socket.emit('data', response)
})
it('should handle a invalid request (invalid start address)', function (done) {
const client = new Modbus.client.TCP(socket)
socket.emit('connect')
socketMock.expects('write').never()
client.writeSingleCoil(0xFFFF01, false)
.then(function (resp) {
assert.ok(false)
}).catch(function (error) {
assert.equal('InvalidStartAddress', error.message)
socketMock.verify()
done()
})
})
})
describe('Write Single Register Tests.', function () {
it('should handle a valid request', function (done) {
const client = new Modbus.client.TCP(socket, 2)
const response = Buffer.from([
0x00, 0x01, // transaction id
0x00, 0x00, // protocol
0x00, 0x06, // byte count
0x02, // unit id
0x06, // function code
0x12, 0x34, // output address
0x43, 0x21 // output value
])
socket.emit('connect')
socketMock.expects('write').once()
client.writeSingleRegister(0x1234, 0x4321)
.then(function (resp) {
assert.ok(resp.response)
assert.ok(resp.request)
socketMock.verify()
done()
}).catch(function (error) {
console.error(error)
})
socket.emit('data', response)
})
it('should handle a invalid request (invalid start address)', function (done) {
const client = new Modbus.client.TCP(socket)
socket.emit('connect')
socketMock.expects('write').never()
client.writeSingleRegister(0xFFFF01, 0x4321)
.then(function (resp) {
assert.ok(false)
}).catch(function (error) {
assert.equal('InvalidStartAddress', error.message)
socketMock.verify()
done()
})
})
it('should handle a invalid request (invalid value, to big)', function (done) {
const client = new Modbus.client.TCP(socket)
socket.emit('connect')
socketMock.expects('write').never()
client.writeSingleRegister(0x1234, 0x12345)
.then(function (resp) {
assert.ok(false)
}).catch(function (error) {
assert.equal('InvalidValue', error.message)
socketMock.verify()
done()
})
})
it('should handle a invalid request (invalid value, float)', function (done) {
const client = new Modbus.client.TCP(socket)
socket.emit('connect')
socketMock.expects('write').never()
client.writeSingleRegister(0x1234, Math.PI)
.then(function (resp) {
assert.ok(false)
}).catch(function (error) {
assert.equal('InvalidValue', error.message)
socketMock.verify()
done()
})
})
it('should handle a invalid request (invalid value, negative)', function (done) {
const client = new Modbus.client.TCP(socket)
socket.emit('connect')
socketMock.expects('write').never()
client.writeSingleRegister(0x1234, -123)
.then(function (resp) {
assert.ok(false)
}).catch(function (error) {
assert.equal('InvalidValue', error.message)
socketMock.verify()
done()
})
})
})
describe('Write Multiple Coils Tests.', function () {
it('should handle a valid request (with array)', function (done) {
const client = new Modbus.client.TCP(socket, 2)
const response = Buffer.from([
0x00, 0x01, // transaction id
0x00, 0x00, // protocol
0x00, 0x06, // byte count
0x02, // unit id
0x0F, // function code
0x12, 0x34, // starting address
0x00, 0x08 // quantity of outputs
])
socket.emit('connect')
socketMock.expects('write').once()
client.writeMultipleCoils(0x1234, [1, 0, 1, 1, 0, 1, 1, 1])
.then(function (resp) {
assert.ok(resp.response)
assert.ok(resp.request)
socketMock.verify()
done()
}).catch(function (error) {
console.error(error)
})
socket.emit('data', response)
})
it('should handle a valid request (with buffer)', function (done) {
const client = new Modbus.client.TCP(socket, 2)
const response = Buffer.from([
0x00, 0x01, // transaction id
0x00, 0x00, // protocol
0x00, 0x06, // byte count
0x02, // unit id
0x0F, // function code
0x12, 0x34, // starting address
0x00, 0x08 // quantity of outputs
])
socket.emit('connect')
socketMock.expects('write').once()
client.writeMultipleCoils(0x1234, Buffer.from([0x7b]))
.then(function (resp) {
assert.ok(resp.response)
assert.ok(resp.request)
socketMock.verify()
done()
}).catch(function (error) {
console.error(error)
})
socket.emit('data', response)
})
it('should handle a invalid request (invalid start address)', function (done) {
const client = new Modbus.client.TCP(socket)
socket.emit('connect')
socketMock.expects('write').never()
client.writeMultipleCoils(0xFFFF01, [0, 1, 0, 1, 0, 1, 0, 1, 0, 1])
.then(function (resp) {
assert.ok(false)
}).catch(function (error) {
assert.equal('InvalidStartAddress', error.message)
socketMock.verify()
done()
})
})
it('should handle a invalid request (invalid array size)', function (done) {
const client = new Modbus.client.TCP(socket)
const arr = []
for (let i = 0; i < (0x07b0 * 8) + 1; i += 1) {
arr.push(1)
}
socket.emit('connect')
socketMock.expects('write').never()
client.writeMultipleCoils(0x0, arr)
.then(function (resp) {
assert.ok(false)
}).catch(function (error) {
assert.equal('InvalidArraySize', error.message)
socketMock.verify()
done()
})
})
it('should handle a invalid request (invalid buffer size)', function (done) {
const client = new Modbus.client.TCP(socket)
const buf = Buffer.alloc(0x07b1)
socket.emit('connect')
socketMock.expects('write').never()
client.writeMultipleCoils(0x0, buf)
.then(function (resp) {
assert.ok(false)
}).catch(function (error) {
assert.equal('InvalidBufferSize', error.message)
socketMock.verify()
done()
})
})
it('should handle a invalid request (inconsistent buffer size)', function (done) {
const client = new Modbus.client.TCP(socket)
const buf = Buffer.alloc(0x07a0 / 8)
socket.emit('connect')
socketMock.expects('write').never()
client.writeMultipleCoils(0x0, buf, 0x7b0)
.then(function (resp) {
assert.ok(false)
}).catch(function (error) {
assert.equal('InvalidBufferSize', error.message)
socketMock.verify()
done()
})
})
})
describe('Write Multiple Registers Tests.', function () {
it('should handle a valid request (with array)', function (done) {
const client = new Modbus.client.TCP(socket, 2)
const response = Buffer.from([
0x00, 0x01, // transaction id
0x00, 0x00, // protocol
0x00, 0x06, // byte count
0x02, // unit id
0x10, // function code
0x12, 0x34, // starting address
0x00, 0x10 // quantity of outputs
])
socket.emit('connect')
socketMock.expects('write').once()
client.writeMultipleRegisters(0x1234, [0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008])
.then(function (resp) {
assert.ok(resp.response)
assert.ok(resp.request)
socketMock.verify()
done()
}).catch(function (error) {
console.error(error)
})
socket.emit('data', response)
})
it('should handle a valid request (with buffer)', function (done) {
const client = new Modbus.client.TCP(socket, 2)
const response = Buffer.from([
0x00, 0x01, // transaction id
0x00, 0x00, // protocol
0x00, 0x06, // byte count
0x02, // unit id
0x10, // function code
0x12, 0x34, // starting address
0x00, 0x10 // quantity of outputs
])
socket.emit('connect')
socketMock.expects('write').once()
client.writeMultipleRegisters(0x1234, Buffer.from([0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08]))
.then(function (resp) {
assert.ok(resp.response)
assert.ok(resp.request)
socketMock.verify()
done()
}).catch(function (error) {
console.error(error)
})
socket.emit('data', response)
})
it('should handle a invalid request (invalid start address)', function (done) {
const client = new Modbus.client.TCP(socket)
socket.emit('connect')
socketMock.expects('write').never()
client.writeMultipleRegisters(0xFFFF01, [])
.then(function (resp) {
assert.ok(false)
}).catch(function (error) {
assert.equal('InvalidStartAddress', error.message)
socketMock.verify()
done()
})
})
it('should handle a invalid request (invalid array size)', function (done) {
const client = new Modbus.client.TCP(socket)
const arr = []
for (let i = 0; i < (0x007b + 1); i += 1) {
arr.push(i)
}
socket.emit('connect')
socketMock.expects('write').never()
client.writeMultipleRegisters(0x0, arr)
.then(function (resp) {
assert.ok(false)
}).catch(function (error) {
assert.equal('InvalidArraySize', error.message)
socketMock.verify()
done()
})
})
it('should handle a invalid request (invalid buffer size)', function (done) {
const client = new Modbus.client.TCP(socket)
const buf = Buffer.alloc((0x007b * 2) + 1)
socket.emit('connect')
socketMock.expects('write').never()
client.writeMultipleRegisters(0x0, buf)
.then(function (resp) {
assert.ok(false)
done()
}).catch(function (error) {
assert.equal('InvalidBufferSize', error.message)
socketMock.verify()
done()
})
})
})
})