Cloud-Automation / node-modbus

Modbus TCP Client/Server implementation for Node.JS
465 stars 174 forks source link

Modbus Simulator #198

Closed wongjasont closed 6 years ago

wongjasont commented 6 years ago

Hi,

I'm writing my own Modbus Simulator using node-modbus (My goal is something similar to modbus_sim; https://github.com/dhoomakethu/modbus_sim_cli/tree/master/modbus_sim) I noticed that the example SimpleServer.js shows that it is possible.

How do I start and which files should I take a close look at?

Many thanks!

wongjasont commented 6 years ago

I'm writing an implementation of a Modbus Simulator over TCP. I would assume that a "SERVER" would be exactly what I needed for the simulator; so that Modbus Clients would be able to connect to it. The source below creates a Modbus TCP Server. Then creates a client that interacts with it:

'use strict'

let net = require('net');
let modbus = require('jsmodbus');
let netServer = new net.Server()
let server = new modbus.server.TCP(netServer);
let socket = new net.Socket();
let opts = {
    host: '127.0.0.1',
    port : '5440'
};
let client = new modbus.client.TCP(socket, 1, 30000);

server.on('connection', function (client) {
    console.log('connection');
});

server.on('readCoils', function (request, response, send) {
    console.log('>>> readCoils', request, response, send);
});

server.on('readInputRegisters', function (request, response, send) {
    console.log('>>> readInputRegisters', request, response, send);
});

server.on('readDiscreteInputs', function (request, response, send) {
    console.log('>>> readDiscreteInputs', request, response, send);
});

server.on('readHoldingRegisters', function (request, response, send) {
    console.log('>>> readHoldingRegisters', request, response, send);
});

server.on('writeSingleRegister', function (value, address) {
    console.log('>>> writeSingleRegister', value, address)
});

server.on('writeSingleCoil', function (value) {
    console.log('>>> writeMultipleCoils', value)
});

server.on('writeMultipleCoils', function (value) {
    console.log('>>> writeMultipleCoils', value)
});

server.on('writeMultipleRegisters', function (value) {
    console.log('>>> writeMultipleRegisters')
});

netServer.listen(5440)

function fulfilled(result) {
    console.log('RESULT', result.response._body)
}

function rejected(err) {
    console.log('ERROR', err)
}

socket.connect(opts);
socket.on('connect', () => {
    client.readCoils(1, 1).then(fulfilled, rejected);
    client.readInputRegisters(1, 1).then(fulfilled, rejected);
    client.readDiscreteInputs(1, 1).then(fulfilled, rejected);
    client.readHoldingRegisters(1, 1).then(fulfilled, rejected);

    client.writeSingleCoil(1, 1).then(fulfilled, rejected);
    client.writeSingleRegister(1, 1).then(fulfilled, rejected);
    client.writeMultipleCoils(1, [1], 1).then(fulfilled, rejected);
    client.writeMultipleRegisters(1, [1]).then(fulfilled, rejected);
});

I assume that the event listeners listed on the server will all trigger because all methods were called after connect but unfortunately only 2 of them are triggered (writeMultipleCoils, writeMultipleRegisters):

RESULT ReadCoilsResponseBody {...}
RESULT ReadInputRegistersResponseBody {...}
RESULT ReadDiscreteInputsResponseBody {...}
RESULT ReadHoldingRegistersResponseBody {...}
RESULT WriteSingleCoilResponseBody {...}
RESULT WriteSingleRegisterResponseBody {...}
>>> writeMultipleCoils
RESULT WriteMultipleCoilsResponseBody {...}
>>> writeMultipleRegisters
RESULT WriteMultipleRegistersResponseBody {...}

So my question is, how do I get them to trigger those events? I've looked around node-modbus source myself and have only found the emit calls in tcp-server-response-handler; is that the only place where these events are emitted? I assume that I will need to implement my own calls like:

server.coils.writeUInt16BE(0x0000, 0)
server.coils.writeUInt16BE(0x0000, 2)
server.coils.writeUInt16BE(0x0000, 4)
server.coils.writeUInt16BE(0x0000, 6)

server.discrete.writeUInt16BE(0x5678, 0)

server.holding.writeUInt16BE(0x0000, 0)
server.holding.writeUInt16BE(0x0000, 2)

server.input.writeUInt16BE(0xff00, 0)
server.input.writeUInt16BE(0xff00, 2)

On every trigger of those events; As seen in /examples/SimpleServer.js

Thank you in advance!

stefanpoeter commented 6 years ago

The event,for example writeMultipleCoils, are fired when there is no coils buffer in the server so that you can handle those request with your own buffer or implementation. It is planed that there are pre and post events as you can see here:

      this._server.emit('writeMultipleCoils', this._server.coils, oldStatus)
      this._server.coils.fill(arrayStatusToBuffer(newStatus))
      this._server.emit('postWriteMultipleCoils', this._server.coils, newStatus)

but they are not all implemented. This way you can handle before and after the request execution. Pre and Post handler are implemented for writeMultipleRegisters and writeMultipleCoils.

wongjasont commented 6 years ago

@stefanpoeter Do you mean that even if the events are not triggered; I can assume that the correct values are returned in response? I have a set of test cases that runs a client against it, writing and reading values but a lot of the test cases are failing:

Test TCP Modbus Mapper
    TCP Read and Write boolean
      ✓ should write true to address 00001
      ✓ should read true from address 00001
      ✓ should write false to address 00001
      ✓ should read false from address 00001
    TCP Read and Write string
      ✓ should write XX to address 40001
      ✓ should read XX from address 40001
      ✓ should write XXABCDEF to address 40001
      ✓ should read XXAB from address 40001
      ✓ should write XXXX to address 40001
      ✓ should read XXXX from address 40001
    TCP Read and Write uint16
      ✓ should write 123 to address 40003
      1) should read 123 from address 40003
      ✓ should write 65535 to address 40003
      2) should read 65535 from address 40003
      ✓ should throw error when writing 65536 to address 40003
      ✓ should throw error when writing -1 to address 40003
    TCP Read and Write uint32
      ✓ should write 123 to address 40004
      3) should read 123 from address 40004
      ✓ should write 4294967295 to address 40004
      4) should read 4294967295 from address 40004
      ✓ should throw error when writing 4294967296 to address 40004
      ✓ should throw error when writing -1 to address 40004
    TCP Read and Write sint16
      ✓ should write 123 to address 40006
      5) should read 123 from address 40006
      ✓ should write -123 to address 40006
      6) should read -123 from address 40006
      ✓ should write 32767 to address 40006
      7) should read 32767 from address 40006
      ✓ should write -32768 to address 40006
      8) should read -32768 from address 40006
      ✓ should throw error when writing 32768 to address 40006
      ✓ should throw error when writing -32769 to address 40006
    TCP Read and Write sint32
      ✓ should write 123 to address 40007
      9) should read 123 from address 40007
      ✓ should write -123 to address 40007
      10) should read -123 from address 40007
      ✓ should write 2147483647 to address 40007
      11) should read 2147483647 from address 40007
      ✓ should write -2147483648 to address 40007
      12) should read -2147483648 from address 40007
      ✓ should throw error when writing 2147483648 to address 40007
      ✓ should throw error when writing -2147483649 to address 40007
    TCP Read and Write float32
      ✓ should write 1.2 to address 40009
      13) should read 1.2000000476837158 from address 40009
      ✓ should write -222.111 to address 40009
      14) should read -222.11099243164062 from address 40009
      ✓ should write 0.0000000000000002 to address 40009
      15) should read 2.000000033724767e-16 from address 40009
      ✓ should write -1.00000000000000001 to address 40009
      16) should read -1.00000000000000001 from address 40009
      ✓ should write 1234567890.123456789 to address 40009
      17) should read 1234567936 from address 40009
    TCP Read and Write float64
      ✓ should write 111.222 to address 40011
      18) should read 111.222 from address 40011
      ✓ should write -222.111 to address 40011
      19) should read -222.111 from address 40011
      ✓ should write 0.0000000000000002 to address 40011
      20) should read 0.0000000000000002 from address 40011
      ✓ should write -1.00000000000000001 to address 40011
      21) should read -1.00000000000000001 from address 40011
      ✓ should write 1234567890.123456789 to address 40011
      22) should read 1234567890.123456789 from address 40011
    TCP Read and Write Whole Mappings
      ✓ should write all mappings
      23) should read all mappings
    TCP Test Write Permissions
      ✓ should throw error when writing true to address 10001
      ✓ should throw error when writing true to address 19999
      ✓ should throw error when writing 123 to address 30001
      ✓ should throw error when writing 123 to address 39999
      ✓ should throw error when writing 123 to unwritable mapping on address 40001
  46 passing (183ms)
  23 failing

As you can see, most of the failing tests are either on reading or writing - will still have to investigate. Shockingly, the tests of the high-level type string are all passing. Initially I used the simulator from https://github.com/dhoomakethu/modbus_sim_cli/tree/master/modbus_sim and all these cases passed, and now I could not point my finger at to where it went wrong.

So does it also mean that the SimpleServer example is already a master/slave simulator by itself?

stefanpoeter commented 6 years ago

So does it also mean that the SimpleServer example is already a master/slave simulator by itself?

Don't know what you mean by simulator but the simple server handles most common modbus functions calls.

stefanpoeter commented 6 years ago

Can you redo the test with branch v3.1.0-dev?

wongjasont commented 6 years ago

@stefanpoeter Test done. The issues are resolved in branch v3.1.0 Thank you for resolving this, I hope the next release will be up soon. Closing this issue now.