Giter Site home page Giter Site logo

stream's Introduction

Modbus Stream

Build Status Package Version

This is a NodeJS module to help you process modbus data. It uses pdu to build the core PDU and then uses transports to extend the rest.

Features

  • Support almost every standard function code
  • Support standard exceptions
  • Support transports
    • TCP
    • RTU
    • ASCII
  • Support drivers
    • TCP
    • UDP
    • Serial (RS232, RS485)

Example

This is my current test.js file. It creates a client and a server network socket and the server requests coils as soon as the client connects.

var modbus = require("modbus-stream");

modbus.tcp.server({ debug: "server" }, (connection) => {
    connection.readCoils({ address: 5, quantity: 8 }, (err, info) => {
        console.log("response", info.response.data);
    });
}).listen(12345, () => {
    modbus.tcp.connect(12345, { debug: "client" }, (err, connection) => {
        connection.on("read-coils", (request, reply) => {
            reply(null, [ 1, 0, 1, 0, 1, 1, 0, 1 ]);
        });
    });
});

Usage

Connection

To connect to a modbus device over TCP, use:

var modbus = require("modbus-stream");

modbus.tcp.connect(502, "134.2.56.231", { debug: "automaton-2454" }, (err, connection) => {
    // do something with connection
});

To listen for connections over TCP, use:

var modbus = require("modbus-stream");

modbus.tcp.server({ debug: "server" }, (connection) => {
    // do something with connection
}).listen(502, () => {
    // ready
});

To connecto to a modbus device over a serial port, use:

var modbus = require("modbus-stream");

modbus.serial.connect("/dev/ttyS123", { debug: "automaton-123" }, (err, connection) => {
    // do something with connection
});

Requests

After having a connection, you can send requests and listen for responses.

modbus.serial.connect("/dev/ttyS123", { debug: "automaton-123" }, (err, connection) => {
    if (err) throw err;

    connection.readCoils({ address: 52, quantity: 8, extra: { unitId: 25 } }, (err, res) => {
        if (err) throw err;

        console.log(res); // response
    })
});

Every method accepts an object options which have defaults parameters (like address = 0) and a callback, in case you want to see the response from the remote device. Here is a list of supported function codes and the corresponding methods:

Base Reads

  • readCoils (address = 0, quantity = 1)
  • readDiscreteInputs (address = 0, quantity = 1)
  • readHoldingRegisters (address = 0, quantity = 1)
  • readInputRegisters (address = 0, quantity = 1)

Base Writes

  • writeSingleCoil (address = 0, value = 0)
  • writeSingleRegister (address = 0, value = <Buffer 0x00 0x00>)
  • writeMultipleCoils (address = 0, values = [])
  • writeMultipleRegisters (address = 0, values = [ <Buffer 0x00 0x00> ])

File Records

  • readFileRecord (requests = [])
  • writeFileRecord (requests = [])

FIFO

  • readFifoQueue (address = 0)

Advanced

  • maskWriteRegister (address = 0, andmask = 0xFFFF, ormask = 0x0000)
  • readWriteMultipleRegisters (read_address = 0, read_quantity = 1, write_address = 0, values = [ <Buffer 0x00 0x00> ])
  • readDeviceIdentification (type = "BasicDeviceIdentification", id = "ProductName")
  • readExceptionStatus ()
  • getCommEventCounter ()
  • getCommEventLog ()

For more information on these methods, look at the pdu repository which is used to build the packets.

Responses

To respond to remote requests, listen for events.

modbus.serial.connect("/dev/ttyS123", {
    // except "debug", everything else is the default for serial
    baudRate : 9600,
    dataBits : 8,
    stopBits : 1,
    parity   : "none",
    debug    : "automaton-123"
}, (err, connection) => {
    if (err) throw err;

    connection.events.on("read-coils", (req, reply) => {
        console.log(req); // request

        // ...
        return reply(null, [ data ]);
    })
});

Events

There are events propagated from the transports up to the stream. You should bind some event listener just in case the connection or serial device errors or just closes. Remember that in NodeJS, an emitted error event without a listener will cause the process to throw an uncaughtException.

Transport Closed (close)

This event is emitted when the serialport module emits a close event or when a socket emits an end event.

Transport Error (error)

This event if something happens to the underlying stream, like a ECONNRESET or something similar.

stream's People

Contributors

buffcode avatar dresende avatar insidegen avatar jacobq avatar jhillacre avatar pillerflorianfischer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stream's Issues

[question] How to get remote address on the server side?

For example, I've got this code:

let modbus = require('modbus-stream')

modbus.tcp.server({ debug: "server" }, (connection) => {
    connection.on("write-single-register", (request, reply) => {
        console.log('Remote address') // somehow print remote address here
    });

}).listen(12345, 'localhost', () => {
    modbus.tcp.connect(12345, 'localhost', { debug: "client" }, (err, connection) => {
        let msg = Buffer.alloc(2);
        msg.writeUInt16BE(0x01, 0);
        connection.writeSingleRegister({address: 05, value: msg, extra: {unitId: 12}}, (err, info) => {
            console.log(info)
        });
        });
    });

I'm looking for something like socket.remoteAddress

I know that there is something like that _connectionKey: '4:127.0.0.1:12345' in request object.
But I'd like to find "easy and correct" approach. If it is exists, of course.

How to remove debugging?

I have tried setting the debug field to null and false in the extra option when reading from holding registers with no luck.

connection.readHoldingRegisters ({address : address, quantity : 1, extra: { unitId: slave, debug: false} }, (err, res) => {
     console.log(res);
})

However, I keep getting the following:

2019-09-06T16:13:38.543Z << undefined 0x[ 00, 1B, 00, 00, 00, 06, 02, 03, 00, 5E, 00, 01 ]

Is it expected behaviour?

Serialport dependency causing failure when installing on Windows

Hi,
as the title says, trying to install modbus-stream on Windows causes this:

prebuild-install WARN install No prebuilt binaries found (target=12.14.1 runtime=node arch=x64 platform=win32)

Then follow a number of errors as the automatic compilation fails. As a result I cannot install modbus-stream on Windows.
Package [email protected] can be installed on Windows. Can you up the dependency on your project or otherwise fix installation on Windows?

Thanks,
Regards

Alessandro

Error: This socket has been ended by the other party

I am trying to run a tcp master app in a docker container. First time I run "docker-compose up" everything went well but after that all I am getting is this:

Can you please help dresende?

 Error: This socket has been ended by the other party
     at Socket.writeAfterFIN [as write] (net.js:290:12)
     at Transport.BaseTransport.write (/C:\Users\Ilhan\Desktop\IcaxMaster/node_modules/modbus-stream/lib/transport/transport.js:46:15)
     at /C:\Users\Ilhan\Desktop\IcaxMaster/node_modules/modbus-stream/lib/transport/transport.js:80:13
     at call_queue (/C:\Users\Ilhan\Desktop\IcaxMaster/node_modules/modbus-stream/lib/transport/transport.js:190:38)
     at Transport.BaseTransport.retrySend (/C:\Users\Ilhan\Desktop\IcaxMaster/node_modules/modbus-stream/lib/transport/transport.js:79:2)
     at Transport.BaseTransport.send (/C:\Users\Ilhan\Desktop\IcaxMaster/node_modules/modbus-stream/lib/transport/transport.js:72:8)
     at Stream.writeMultipleRegisters (/C:\Users\Ilhan\Desktop\IcaxMaster/node_modules/modbus-stream/lib/stream.js:174:17)
     at Timeout._onTimeout (/C:\Users\Ilhan\Desktop\IcaxMaster/app.js:38:20)
     at ontimeout (timers.js:386:11)
     at tryOnTimeout (timers.js:250:5)

I don't get a response, but debug information

Hi!

I'm able to establish a connection to a Siemens S7 via Modbus TCP.

I'm trying to read the holding registers. If I run the application I get the "Connected." info and the debug information that shows the data sent to the device. But I don't get the res object of the function call. I get a timeout instead:

Error while reading holding registers:  { Error: GatewayTargetDeviceFailedToRespond
    at Object.exports.error (/Users/uNki/Documents/Development/modbus_test/node_modules/modbus-pdu/lib/Exception.js:24:13)
    at Timeout._onTimeout (/Users/uNki/Documents/Development/modbus_test/node_modules/modbus-stream/lib/transport/transport.js:93:33)
    at ontimeout (timers.js:475:11)
    at tryOnTimeout (timers.js:310:5)
    at Timer.listOnTimeout (timers.js:270:5) code: 11 }

here is my code:

const modbus = require("modbus-stream");

modbus.tcp.connect(502, "192.168.0.1", { debug: "server" }, (err, connection) => {
    if (err)
        console.error('Connection error: ', err)

    console.log('Connected.\n')

    connection.readHoldingRegisters({ address: 0, quantity: 20, extra: { unitId: 1 } }, (err, res) => {
        if (err)
            return console.error('Error while reading holding registers: ', err)
        console.log('Response: ', res); // 
    })

    connection.on('error', err => {
        if (err.message != 'write EPIPE')
            console.log('Unknown error:', err.message)
    })
});

Event is received and responded even when slaveId doesn't match

For example,

modbus.serial.connect('/dev/ttyS1', {slaveId: 10}, (err, conn){
conn.on('read-coils', (req, reply) => {
console.log(req)
})
})

The events is always received for event if the slaveId is not 10, and it responds with exception when reply is not called (we should ignore when slaveId doesn't match).

Node can receive requests which is not intended for it in RS-485 network.

TypeScript declarations

Hi,

first of all thanks to you for your solid work!

We are planning to use your library within one of our TypeScript-based projects and noticed that there aren't any type declarations yet.
I'm willing to provide those declarations and it would be nice when some one could test / extend the types to include serial and UDP, as we probably only use either TCP or UDP in our project.

I will publish the types in my own repository first of all and if everything looks okay a PR to the official @types namespace managed by https://github.com/DefinitelyTyped/DefinitelyTyped will be created.

There are no changes to the actual codebase necessary, though this project would really benefit from a migration to typescript (codecompletion for driver / stream options etc.)

Recovering from a lost connection

Hi,

I'm probably missing something simple, but how would one detect/cleanup after a connection loss and then reconnect when connectivity is restored?

Currently I connect and save the connection in a variable. I use that connection periodically. If it gets an error (cable unplugged in this case), I call close on the connection. When the link is restored, I make a new connection and things continue. However, at some point soon after the reconnect a ECONNRESET is thrown.

What is the proper way to handle connection loss?

Thanks,
Kevin

Unit ID modbus stream

i have a modbus client with ( modbus unit id = 245 for example) how can i add this in node-modbus stream methods to read values ( coils and inputs )

turning off debugging in console?

Hi
I'm getting an array of buffers of registers with every connection.readHoldingRegisters call.
It looks like:
2019-11-04T22:11:33.896Z >> server-node 0x[ 00, 01, 00, 00, 00, DD, 01, 03, DA........
How can I turn off please? Thank you

QUESTION How to get device data once a second?

The code I can understand it is simple. But my problem is the connection. Fo instance I have something like

setInterval(() => {
    // get device info here
}, 2000);

1, In the section where I collect data from the device, do I call connect every time or I should connect before the interval and then only read registers? Something like this

let run = async () => {
    let conn = await modbus.tcp.connect(502, "134.2.56.231", { debug: "automaton-2454" });
    setInterval(() => {
        conn.readCoil();
    }, 2000);
}
run();

Or like this

setInterval(() => {
    modbus.tcp.connect(502, "134.2.56.231", { debug: "automaton-2454" }, (err, connection) => {
        connection.readCoil();
    });
}, 2000);
  1. If I can do the first method then how do I catch en error if the connection gets lost?

"GatewayTargetDeviceFailedToRespond" error with multiple "readHoldingRegisters"

Hi!
I get this error GatewayTargetDeviceFailedToRespond when I execute several times the readHoldingRegisters function. I use the readHoldingRegisters function inside a loop.
Any idea?
Thanks.

Simple test
var modbus = require("modbus-stream"); // v0.38.0

var startDate = Date.now();

modbus.tcp.connect(502, "localhost", { "debug": null }, function (err, connection) {
  if (err == null && connection != null) {
    for (var i = 0 ; i < 10 ; i++) {
      (function (address) {
        connection.readHoldingRegisters({ "address": address, "quantity": 1 }, function (err, res) {
          if (err != null) {
            console.log((Date.now() - startDate) + "ms - Register[" + address + "]: " + err.message);
          }
          else if (res != null) {
            console.log((Date.now() - startDate) + "ms - Register[" + address + "]: " + res.response.data[0].readInt16BE(0));
          }
        });
      })(i);
    }
  }
});

3 executions with this test in the same context:

Console output # 1
13ms - Register[0]: 10
14ms - Register[1]: 11
15ms - Register[2]: 12
15ms - Register[3]: 13
15ms - Register[4]: 14
15ms - Register[5]: 15
15ms - Register[6]: 16
15ms - Register[7]: 17
15ms - Register[9]: 19
30009ms - Register[8]: GatewayTargetDeviceFailedToRespond
Console output # 2
13ms - Register[0]: 10
14ms - Register[1]: 11
14ms - Register[2]: 12
15ms - Register[5]: 15
15ms - Register[6]: 16
15ms - Register[7]: 17
15ms - Register[9]: 19
30009ms - Register[3]: GatewayTargetDeviceFailedToRespond
30009ms - Register[4]: GatewayTargetDeviceFailedToRespond
30009ms - Register[8]: GatewayTargetDeviceFailedToRespond
Console output # 3
13ms - Register[0]: 10
14ms - Register[1]: 11
15ms - Register[2]: 12
15ms - Register[3]: 13
15ms - Register[4]: 14
15ms - Register[5]: 15
15ms - Register[6]: 16
15ms - Register[7]: 17
30010ms - Register[8]: GatewayTargetDeviceFailedToRespond
30010ms - Register[9]: GatewayTargetDeviceFailedToRespond

IllegalDataValue while reading holding registers on loop

Hello, I've succesfully connected a to a real device and I'm able to read a value as soon as the device connects, the problem is that if a surround the readHoldingRegister in a loop, it only reads the value the first time and catches an exception of IllegalDataValue the next one.

Here' the code of my client
`modbus.tcp.connect(port, address, { debug: 'device-1' }, (err, connection) => {
if (err){
console.log(err);
}
else{
console.log('Device connected');
var buffer;
var hex = ''

for (let i=0; i<5; i++){
  connection.readHoldingRegisters({ address: 50526, quantity: 2, extra: { unitId: 1, retry:   1000, retries: 3 }}, (err, res) =>{
    if (err != null){
      throw err;
    }
    else{
      buffer = res.response.data;
      hex = buffer[1].toString('hex');
      console.log(hex);
    }
  });
}`

I don't know if it's because my response is wrong, here's the code for the server:
`modbus.tcp.server({ debug: 'server' }, (connection) => {
connection.on("read-holding-registers", function (req, reply) {
console.log(req);
console.log('OK');
return reply(null, [ Buffer.from([ 0x00, req.request.address ]) ]);
});

}).listen(502, () => {
console.log('Listening');
}); `

[Help] How to get incoming-data ?

var modbus = require("modbus-stream");

modbus.tcp.server({ debug: "server" }, (connection) => {
connection.readCoils({from: 1, to: 40}, (err, info) => {
   console.log(info.response.data);
    });
}).listen(502, () => {
    // ready
});

info.response.data is incoming-data ?

Correct Parameters to reply to writeMultipleRegisters

I am trying to create a server and I need to send the reply after the client calls writeMultipleRegisters.
What are the correct parameters to send back and is there a helper function that will create the response?

How can I disconnect from modus.tcp.connect

My PLC has 4 threads that I can connect to. Once I connect the 4 times, it will crash on the 5th attempt. Is there any documentation on disconnecting from an existing connection?

I've tried connection.close(), connection.off() without any luck.

Syntax for generic `reply` function

Similar to #34 I'm confused about the different signatures that need to be used for reply. It seems to depend on the function code, that is, reply seems to assume the data it receives is byte-for-byte identical to the data section of the response frame, even if that includes additional fields like start address, number of elements to read/write, etc. Is that right?

For example, in my application I define a "modbus map" to associate handler functions with ROB & ROR read operations (function code 2 & 4) as well as RWB & RWR read/write operations (codes 1, 3, 5, 6, 15, and 16). However, I am not certain how I should call reply with the resulting data.

modbus.tcp.server(serverOptions, (connection: any) => {
    // ...
    connection.transport.on('request', (eventName, transaction, reply) => {
        const request = transaction.request;
        const size = request.quantity;
        const firstAddr = request.address;
        const lastAddr = firstAddr + size - 1;
        // (call appropriate handlers)
        // ...
        // This is the part that seems weird / opaque to me.
        // Why isn't there a function that will just accept `result` and format it appropriately
        // (since the transaction contains the information needed)?
        switch(eventName) {
            case 'read-coils':               // fcode 1: response format = [ID][FC][BC][DATA, 1 byte per 8 coils]
            case 'read-discrete-inputs':     // fcode 2: response format = [ID][FC][BC][DATA, 1 byte per 8 coils]
                reply(null, result);
                break;
            case 'read-holding-registers':   // fcode 3: response format = [ID][FC][BC][DATA, 2 bytes per register]
            case 'read-input-registers':     // fcode 4: response format = [ID][FC][BC][DATA, 2 bytes per register]
                result = result.map(x => (x instanceof Buffer) ? x : createUInt16BufferFromNumber(x));
                reply(null, result);
                break;
            case 'write-single-coil':        // fcode 5: response format = [ID][FC][ADDR][DATA] (mirrors request exactly)
            case 'write-single-register':    // fcode 6: response format = [ID][FC][ADDR][DATA] (mirrors request exactly)
                reply(null, firstAddr, request.value);
                break;
            case 'write-multiple-coils':     // fcode 15: response format = [ID][FC][ADDR][NUM]
            case 'write-multiple-registers': // fcode 16: response format = [ID][FC][ADDR][NUM]
                reply(null, firstAddr, size);
                break;
            default:
                warn(`Replying with default format -- probably wrong!`, result);
                reply(null, result);
        }
    })
    // ...
}

Question about getting response from device

I have a lock system hooked up to the tcp controller i'm using with node-modbus. I can successfully send the unlock to modbus but I'm trying to figure out a way to listen to the device. After unlocking, when the device LOCKS how do I listen on port 502 so that I can trigger an event?

My current code is:

modbus.tcp.connect(502, "10.100.55.17", { debug: "automaton-2454" }, (err, connection) => {
    //do something with connection
		connection.writeSingleCoil({ address: 0, value: 0 }, (err, info) => {
        console.log("response", info.response);
    });

		connection.writeSingleCoil({ address: 0, value: 1 }, (err, info) => {
        console.log("response", info.response);
    });

		connection.on("read-coils", (request, reply) => {
				console.log("**READ-COILS**")
				reply(null, [ 0, 0, 0, 0, 0, 0, 0, 0 ]);
		});

		connection.on("write-single-coil", (request, reply) => {
				console.log("**WRITE-SINGLE-COIL**")
				reply(null, 0, 1);
		});
});


modbus.tcp.server({ debug: "server" }, (connection) => {
    // do something with connection

		connection.on("read-coils", (request, reply) => {
				console.log("**READ-COILS**")
				//reply(null, [ 0, 0, 0, 0, 0, 0, 0, 0 ]);
		});

		connection.on("write-single-coil", (request, reply) => {
				console.log("**WRITE-SINGLE-COIL**")
				//reply(null, 0, 1);
		});

		connection.on("read-single-coil", (request, reply) => {
				console.log("**READ-SINGLE-COIL**")
				reply(null, 0, 1);
		});

		connection.readCoils({ address: 0, quantity: 16 }, (err, info) => {
        console.log("response", info.response);
    });

		connection.readCoils({ from: 0, to: 7 }, (err, info) => {
        console.log("PDU", info.pdu);
        console.log("response", info.response.data);
    });

}).listen(502, () => {
    // ready
		console.log("** GOT RESPONSE FROM DEVICE **")

});

options not getting passed to tcp server or client

In the modbus.js file the {retry:options.retry} seems to be clearing out all the options.
Changing it to {options} seems to resolve.:

if (typeof options.retry == "undefined") {
	options.retry = 30000;
}
var socket = exports.drivers.tcp.connect(port, host, options);
socket.attach(exports.transports.tcp.prepare({ retry: options.retry }), next);

It is present twice in the file (line 45 and 66).

Thanks!

TCP Transport: Transaction ID overflow

Managed to overflow the transaction id counter in lib/transport/tcp.js. I'm not 100% familiar with the raw protocol, but would it be safe for node-modbus-stream to be resetting the transaction ID instead of overflowing? If not, I guess I could catch this and rebuild the connection.

My exception:

TypeError: "value" argument is out of bounds
    at checkInt (buffer.js:1041:11)
    at Buffer.writeUInt16BE (buffer.js:1110:5)
    at Transport.wrap (/home/logger/server/node_modules/modbus-stream/lib/transport/tcp.js:38:8)
    at Transport.BaseTransport.send (/home/logger/server/node_modules/modbus-stream/lib/transport/transport.js:72:23)
    at Stream.readInputRegisters (/home/logger/server/node_modules/modbus-stream/lib/stream.js:103:17)
    at voltageAndRanges (/home/logger/server/lib/plc.js:104:49)
    at /home/logger/server/node_modules/async/dist/async.js:3853:24
    at replenish (/home/logger/server/node_modules/async/dist/async.js:946:17)
    at /home/logger/server/node_modules/async/dist/async.js:950:9
    at eachOfLimit (/home/logger/server/node_modules/async/dist/async.js:975:24)
    at /home/logger/server/node_modules/async/dist/async.js:980:16
    at eachOf (/home/logger/server/node_modules/async/dist/async.js:1051:5)

please add a socket timeout option

I would like a way to have the socket close automatically.
socket provides two methods already; we just need to pass them into your tcp.js driver.

../drivers/tcp.js file modifications here:

// if the timeout option is present; make sure it is an integer
// and limit it between 1 and 60 seconds

if (options.timeout) {
	let timeOut = Math.min(60000, Math.max(1000, parseInt(options.timeout)));
	socket.setTimeout(timeOut);
	socket.on('timeout', () => {
		socket.end();
	});
}

// --- End of timeout option

// if the closeOnData is set to 'true' then
// end() the socket connection when data is returned from
// the tcp connection.

	if (options.closeOnData === true) {
		socket.on('data', () => {
		       socket.end();
	       })
        }
// --- End of closeOnData option
return socket;

use like this:

modbus.tcp.connect(12345, { timeout: 3000, closeOnData: true, debug: "client" }, (err, connection) => {
    connection.readCoils({ address: 1, quantity: 1 }, (err, res) => {
        if (err) throw err;
        console.log(res.response); // response here
    })
});

Thanks!

How to read the value of the coil after writing on it

Hello,

I am using the function 'writeSingleCoil' to write value 1 on address 5.
The example is given below:
var modbus = require("modbus-stream");
modbus.tcp.server({ debug: "server" }, (connection) => {
connection.writeSingleCoil({ address: 5, value: 1 }, (err, info) => {
console.log("Write Request: Server");
console.log("response", info.response);
});
}).listen(12345, () => {
modbus.tcp.connect(12345, { debug: "client" }, (err, connection) => {
connection.on("write-single-coil", (request, reply) => {
console.log("Write Request: Client");
console.log(request.request.value);
reply(null, 2,1);
});
});
});
My Questions are:

  1. Is this value getting stored somewhere in the memory?
  2. Can I retrieve the the saved value?

Erreur after some times of execcuting sequential reading of coils

hello my project of modbus-steam it's reading some registers of my modbus tcp devices .

i use a Setinterval JS function to read every minute my devices , but after some times ( may be 1 or 2 hours or less ) my script stops with ECONNRESET ERROR

Question : Ho can i handlle this error to disable script stopp and continue reading or waiting until connection is OK

error is

Error: read ECONNRESET at TCP.onStreamRead (internal/stream_base_commons.js:111:27) Emitted 'error' event at: at Transport.<anonymous> (D:\serveur\readmodbus\node_modules\modbus-stream\l ib\stream.js:36:10) at Transport.emit (events.js:182:13) at Transport.<anonymous> (D:\serveur\readmodbus\node_modules\modbus-stream\l ib\transport\transport.js:21:8) at Socket.emit (events.js:182:13) at emitErrorNT (internal/streams/destroy.js:82:8) at emitErrorAndCloseNT (internal/streams/destroy.js:50:3) at process._tickCallback (internal/process/next_tick.js:63:19)

blocks[i].copy is not a function

var modbus = require("modbus-stream");

module.exports = {
    initModbus: function() {
         modbus.tcp.server({ debug: "server" }, (connection) => {
              connection.readHoldingRegisters( {address: 0, quantity: 1}, (err, info) => {
                   console.log("response", info.response.data);
              });
         }).listen(12345, () => {
              modbus.tcp.connect(12345, { debug: "client" }, (err, connection) => {
                   connection.on("read-holding-registers", (request, reply) => {
                        reply(null, [234]);
                   });
              });
         });
    }
}

Hi, thanks for your effort. Could not achieve just a simple thing. What can be wrong here?

Error output:

events.js:141
      throw er; // Unhandled 'error' event
      ^

TypeError: blocks[i].copy is not a function
    at Object.blocksToBuffer [as buildResponse] (/home/ubuntu/workspace/EasygenSlave/node_modules/modbus-stream/node_modules/modbus-pdu/lib/Helpers.js:111:13)
    at Object.build (/home/ubuntu/workspace/EasygenSlave/node_modules/modbus-stream/node_modules/modbus-pdu/lib/Modbus.js:113:30)
    at /home/ubuntu/workspace/EasygenSlave/node_modules/modbus-stream/lib/transport/transport.js:165:75
    at Stream.<anonymous> (/home/ubuntu/workspace/EasygenSlave/modbus-tcp-server.js:12:13)
    at emitTwo (events.js:87:13)
    at Stream.emit (events.js:172:7)
    at Transport.<anonymous> (/home/ubuntu/workspace/EasygenSlave/node_modules/modbus-stream/lib/stream.js:44:10)
    at emitThree (events.js:97:13)
    at Transport.emit (events.js:175:7)
    at handle (/home/ubuntu/workspace/EasygenSlave/node_modules/modbus-stream/lib/transport/transport.js:156:16)

Can you give me guidance when you are available? Cheers.

Can I Increase the delay before "GatewayTargetDeviceFailedToRespond" error?

I have a serial connection to a Modbus Server and everything seems to work okay, but I seem to get a "GatewayTargetDeviceFailedToRespond". If I retry the operation, it seems to continue, but this causes the overall communication rate to be much slower than desired.
I tried using retries, retry, but the results seems to be the same.
I also tried modifying maxDataInterval, but again the results seemed to be similar.
What variable is used to decide how long to wait, before deciding the device failed to respond?
Is it one of the variables mentioned above?
Eric

Modbus discover all the module and generate a json file with the data model of each device Master/slave connected to the network

Dear @jhillacre , @dresende , @jacobq , @buffcode , @insideGen

I am working on a proprietary protocol translator from /to modbus.

I need to discover all the modbus device master/slave and get the list of parameters they are able to support (finally the data model) for each devic discovered.

It's our data model for one parameter it could be string, boolean, real, integer, ....

{

                    "number": 0,
                    "identifier": "ParameTypeInteger",
                    "description": "",
                    "value": 0,
                    "minimum": -4096,
                    "maximum": 0,
                    "access": "readWrite",
                    "format": "%8.2f°",
                    "enumeration": "",
                    "factor": 32,
                    "isOnline": true,
                    "formula": "1*\n1/",
                    "default": -1024,
                    "type": "integer"
                }               {

                    "number": 1,
                    "identifier": "ParameTypeReal",
                    "description": "",
                    "value": 0,
                    "minimum": 0,
                    "maximum": 4096,
                    "access": "readWrite",
                    "format": "%8.2f°",
                    "enumeration": "",
                    "factor": 1,
                    "isOnline": true,
                    "formula": "10*\n10/",
                    "default": 100,
                    "type": "real"
                },
                {
                    "number": 2,
                    "identifier": "ParameTypeString",
                    "description": "",
                    "value": "text",
                    "access": "readWrite",
                    "enumeration": "Yes\nNo\nOk",
                    "isOnline": true,
                    "default": "default",
                    "type": "string"
                },
                {
                    "number": 3,
                    "identifier": "ParameTypeEnumeration",
                    "description": "",
                    "value": 0,
                    "access": "readWrite",
                    "enumeration": "Line\nMic+48V\nMic",
                    "isOnline": true,
                    "default": 0,
                    "type": "enum"
                },
                {
                    "number": 4,
                    "identifier": "ParameTypeBoolean",
                    "description": "",
                    "value": true,
                    "access": "readWrite",
                    "isOnline": true,
                    "default": false,
                    "type": "boolean"
                }

How we could do with you code to generate a json file with all the devices ?

Best regards
Youssef

IllegalData while trying to write multiple coils

I'm trying to write multiple coils at once but I'm getting IllegalData error. I can write single coils without any problem, maybe that's not the format that I should use? If not, what should I use instead? Also, how should I format the buffers for writing registers?

modbus.tcp.connect(device.port, device.ip, { debug: null }, (err, connection) => {
                connection.writeMultipleCoils({address: address, value: [1,0,0,0,1,1,1,0], extra: {unitId: device.id_slave}}, (err, data) => {
                });
          });

Callback called a second time from "readInputRegisters"

Running into an issue migrating over from node-modbus-tcp. I seem to have a readInputRegisters callback being called a second time. The first time has my results, the second time has a GatewayTargetDeviceFailedToRespond error object.

I'm using the async library to handle callbacks to run multiple readInputRegisters in parallel. Async throws an exception when its callback is called a second time. I added a call counter to my function and it seems like the first readInputRegisters that returned successfully was then called again.

Is this caused by transport.retryTimer being overwritten in lib/transport/transport.js by the second call to readInputRegisters?

Here is my code:

setup
const modbus = require('modbus-stream');
let modbusConn;
...
modbus.tcp.connect(
  sql.currentSettings.modbusPort,
  sql.currentSettings.ipAddress,
  (err, connection) => {
    if (err) {
      return die(err, 'modbus');
    }
    modbusConn = connection;
  }
);
polled function (called every ~50 ms)
let callCount = 0;
...
function getPlcDMAndHSCPV(cb) {
  let myCallCount = callCount++;
  console.log('myCallCount', myCallCount);
  async.parallel(
    {
      counter: innercb => modbusConn.readInputRegisters(
        {address: 520, quantity: 2},
        (err, result) => {
          console.log('counterCb', {myCallCount: myCallCount, err: err, result: result}); innercb(err, result);
        }
      ),
      voltageAndRanges: innercb => modbusConn.readInputRegisters(
        {address: 1000, quantity: 6},
        (err, result) => {
          console.log('voltageAndRangesCb', {myCallCount: myCallCount, err: err, result: result}); innercb(err, result);
        }
      ),
    },
    (err, results) => {
      if (err) {
        return cb(err);
      }
      let mapped = [];
      mapped[0] = results.voltageAndRanges.response.data[0].readInt16BE(0);
      mapped[1] = results.voltageAndRanges.response.data[1].readInt16BE(0);
      mapped[3] = Buffer.concat(results.counter.response.data).readInt32BE(0);
      mapped[2] = (mapped[3] / pulseToDistanceRatio).toFixed(2);
      mapped[4] = results.voltageAndRanges.response.data[4].readInt16BE(0);
      mapped[5] = results.voltageAndRanges.response.data[5].readInt16BE(0);
      cb(null, mapped);
    }
  );
}
output (counterCb with myCallCount of 0 is called first and last.)
myCallCount 0
counterCb { myCallCount: 0,
  err: null,
  result: 
   { transactionId: 1,
     protocol: 0,
     length: 7,
     unitId: 1,
     pdu: <Buffer 04 04 02 7d d0 27>,
     callback: [Function],
     response: { code: 'ReadInputRegisters', data: [Object] } } }
voltageAndRangesCb { myCallCount: 0,
  err: null,
  result: 
   { transactionId: 2,
     protocol: 0,
     length: 15,
     unitId: 1,
     pdu: <Buffer 04 0c 00 34 00 37 ff ff ff ff 00 01 00 01>,
     callback: [Function],
     response: { code: 'ReadInputRegisters', data: [Object] } } }

... 30 seconds of similar calls ...

myCallCount 599
counterCb { myCallCount: 599,
  err: null,
  result: 
   { transactionId: 1199,
     protocol: 0,
     length: 7,
     unitId: 1,
     pdu: <Buffer 04 04 02 7d b7 aa>,
     callback: [Function],
     response: { code: 'ReadInputRegisters', data: [Object] } } }
voltageAndRangesCb { myCallCount: 599,
  err: null,
  result: 
   { transactionId: 1200,
     protocol: 0,
     length: 15,
     unitId: 1,
     pdu: <Buffer 04 0c 00 3a 00 33 ff ff ff ff 00 01 00 01>,
     callback: [Function],
     response: { code: 'ReadInputRegisters', data: [Object] } } }
counterCb { myCallCount: 0,
  err: 
   { Error: GatewayTargetDeviceFailedToRespond
       at Object.exports.error (/home/joel/WebstormProjects/well-logger/server/node_modules/modbus-pdu/lib/Exception.js:24:13)
       at Timeout._onTimeout (/home/joel/WebstormProjects/well-logger/server/node_modules/modbus-stream/lib/transport/transport.js:89:33)
       at ontimeout (timers.js:386:14)
       at tryOnTimeout (timers.js:250:5)
       at Timer.listOnTimeout (timers.js:214:5) code: 11 },
  result: undefined }
exception (after output)
error: uncaughtException: Callback was already called. 
{ date: 'Wed Jul 19 2017 16:29:20 GMT-0600 (MDT)',
  process: 
   { pid: 15041,
     uid: 1002,
     gid: 1002,
     cwd: '/home/joel/WebstormProjects/well-logger/server',
     execPath: '/home/joel/.nvm/versions/node/v6.11.1/bin/node',
     version: 'v6.11.1',
     argv: 
      [ '/home/joel/.nvm/versions/node/v6.11.1/bin/node',
        '/home/joel/WebstormProjects/well-logger/server/app.js' ],
     memoryUsage: 
      { rss: 57839616,
        heapTotal: 36737024,
        heapUsed: 32882016,
        external: 396488 } },
  os: 
   { loadavg: [ 0.8125, 0.5537109375, 0.38232421875 ],
     uptime: 194414 },
  trace: 
   [ { column: 32,
       file: '/home/joel/WebstormProjects/well-logger/server/node_modules/async/dist/async.js',
       function: null,
       line: 903,
       method: null,
       native: false },
     { column: 13,
       file: '/home/joel/WebstormProjects/well-logger/server/node_modules/async/dist/async.js',
       function: null,
       line: 3835,
       method: null,
       native: false },
     { column: 93,
       file: '/home/joel/WebstormProjects/well-logger/server/lib/plc.js',
       function: 'modbusConn.readInputRegisters',
       line: 108,
       method: 'readInputRegisters',
       native: false },
     { column: 14,
       file: '/home/joel/WebstormProjects/well-logger/server/node_modules/modbus-stream/lib/transport/transport.js',
       function: 'Timeout._onTimeout',
       line: 89,
       method: '_onTimeout',
       native: false },
     { column: 14,
       file: 'timers.js',
       function: 'ontimeout',
       line: 386,
       method: null,
       native: false },
     { column: 5,
       file: 'timers.js',
       function: 'tryOnTimeout',
       line: 250,
       method: null,
       native: false },
     { column: 5,
       file: 'timers.js',
       function: 'Timer.listOnTimeout',
       line: 214,
       method: 'listOnTimeout',
       native: false } ],
  stack: 
   [ 'Error: Callback was already called.',
     '    at /home/joel/WebstormProjects/well-logger/server/node_modules/async/dist/async.js:903:32',
     '    at /home/joel/WebstormProjects/well-logger/server/node_modules/async/dist/async.js:3835:13',
     '    at modbusConn.readInputRegisters (/home/joel/WebstormProjects/well-logger/server/lib/plc.js:108:93)',
     '    at Timeout._onTimeout (/home/joel/WebstormProjects/well-logger/server/node_modules/modbus-stream/lib/transport/transport.js:89:14)',
     '    at ontimeout (timers.js:386:14)',
     '    at tryOnTimeout (timers.js:250:5)',
     '    at Timer.listOnTimeout (timers.js:214:5)' ] }

ascii slave

How can i setup a modbus ascii slave?
I found no option to use the ascii transport.

ECONNRESET

Hi Diego,

Thanks again for your work. Is below a drop of established connection? Is there something that can be enabled to automatically establish the connection again? If not, what is the best way to reestablish the connection.

Thank you for your help.

Ilhan

events.js:167
      throw er; // Unhandled 'error' event
      ^

Error: read ECONNRESET
    at TCP.onread (net.js:659:25)
Emitted 'error' event at:
    at Transport.<anonymous> (/home/ilhans/tools/grid-tester/node_modules/modbus-stream/lib/stream.js:36:10)
    at Transport.emit (events.js:182:13)
    at Transport.<anonymous> (/home/ilhans/tools/grid-tester/node_modules/modbus-stream/lib/transport/transport.js:21:8)
    at Socket.emit (events.js:182:13)
    at emitErrorNT (internal/streams/destroy.js:82:8)
    at emitErrorAndCloseNT (internal/streams/destroy.js:50:3)
    at process._tickCallback (internal/process/next_tick.js:63:19)

Node 12 compatibility

The version of serialport included in package.json (<7) is not compatible with node 12 and generates a lot of errors during installation. Have you already planned the update of that package?

Error: GatewayTargetDeviceFailedToRespond

receive data from TCP client :

{ Error: GatewayTargetDeviceFailedToRespond
at Object.exports.error (/Users/mac/Desktop/RLLP-PCL5new/node_modules/modbus-pdu/lib/Exception.js:24:13)
at Timeout._onTimeout (/Users/mac/Desktop/RLLP-PCL5new/node_modules/modbus-stream/lib/transport/transport.js:92:33)
at ontimeout (timers.js:498:11)
at tryOnTimeout (timers.js:323:5)
at Timer.listOnTimeout (timers.js:290:5) code: 11 }

I cant find cause?

Why does the example show the server sending a request to the client?

In Modbus(TCP), the terms "server" & "slave" refer to devices that do not initiate contact with other devices but rather only respond to request they receive. The terms "client" & "master" refer to devices that initiate requests. So why does the first example have the server (slave) send a request (read-coils address 3-7) to a client (master)? This seems backwards.

This is my current test.js file. It creates a client and a server network socket and the server requests coils as soon as the client connects.

Modbus : get Discover all the devices and generate the data model

Dear @jhillacre , @dresende , @jacobq , @buffcode , @insideGen

I am working on a proprietary protocol translator from /to modbus.

I need to discover all the modbus device master/slave and get the list of parameters they are able to support (finally the data model) for each devic discovered.

It's our data model for one parameter it could be string, boolean, real, integer, ....

{

                    "number": 0,
                    "identifier": "ParameTypeInteger",
                    "description": "",
                    "value": 0,
                    "minimum": -4096,
                    "maximum": 0,
                    "access": "readWrite",
                    "format": "%8.2f°",
                    "enumeration": "",
                    "factor": 32,
                    "isOnline": true,
                    "formula": "1*\n1/",
                    "default": -1024,
                    "type": "integer"
                }               {

                    "number": 1,
                    "identifier": "ParameTypeReal",
                    "description": "",
                    "value": 0,
                    "minimum": 0,
                    "maximum": 4096,
                    "access": "readWrite",
                    "format": "%8.2f°",
                    "enumeration": "",
                    "factor": 1,
                    "isOnline": true,
                    "formula": "10*\n10/",
                    "default": 100,
                    "type": "real"
                },
                {
                    "number": 2,
                    "identifier": "ParameTypeString",
                    "description": "",
                    "value": "text",
                    "access": "readWrite",
                    "enumeration": "Yes\nNo\nOk",
                    "isOnline": true,
                    "default": "default",
                    "type": "string"
                },
                {
                    "number": 3,
                    "identifier": "ParameTypeEnumeration",
                    "description": "",
                    "value": 0,
                    "access": "readWrite",
                    "enumeration": "Line\nMic+48V\nMic",
                    "isOnline": true,
                    "default": 0,
                    "type": "enum"
                },
                {
                    "number": 4,
                    "identifier": "ParameTypeBoolean",
                    "description": "",
                    "value": true,
                    "access": "readWrite",
                    "isOnline": true,
                    "default": false,
                    "type": "boolean"
                }

How we could do with you code to generate a json file with all the devices ?

Best regards
Youssef

Cannot set property 'code' of null

I have a USB RS485 adapter connected to a MODBUS temperature sensor. I have a C program using libmodbus successfully reading the device. I want to use Node.js, of course.

I'm getting this error:

$ sudo node mstemp.js 
<< automaton-123 0x[ 01, 04, 00, 01, 00, 01, 60, 0A ]
>> automaton-123 0x[ 01, 04, 02, 0C ]
events.js:160
      throw er; // Unhandled 'error' event
      ^

TypeError: Cannot set property 'code' of null
    at Object.exports.Response (/home/david/modbus-server/node_modules/modbus-pdu/lib/Modbus.js:84:16)
    at SerialPort.<anonymous> (/home/david/modbus-server/node_modules/modbus-stream/lib/transport/transport.js:82:24)
    at emitOne (events.js:96:13)
    at SerialPort.emit (events.js:188:7)
    at SerialPort.raw (/home/david/modbus-server/node_modules/serialport/lib/parsers.js:7:13)
    at SerialPort._emitData (/home/david/modbus-server/node_modules/serialport/lib/serialport.js:313:18)
    at SerialPort.<anonymous> (/home/david/modbus-server/node_modules/serialport/lib/serialport.js:293:14)
    at SerialPort.<anonymous> (/home/david/modbus-server/node_modules/serialport/lib/serialport.js:306:7)
    at FSReqWrap.wrapper [as oncomplete] (fs.js:681:17)

The line of code is here in modbus-pdu:

 if (typeof exports[k] == "object" && exports[k].Code == code) {
                                var data = exports[k].Response.parse(buffer);

                                if (typeof data == "object" && !util.isArray(data)) {
                                        data.code = k;  // CRASH HERE
                                } else {
                                        data = { code: k, data: data };
                                }

                                return data;
                        }

So.. going by the crash, somehow data is null but by the test it has typeof == object and it is not an array.

My code is adapted from your examples:

const modbus = require('modbus-stream');
const util   = require('util');

modbus.serial.connect("/dev/ttyUSB0", {
    // except "debug", everything else is the default for serial
    baudRate : 9600,
    dataBits : 8,
    stopBits : 1,
    parity   : "none",
    debug    : "automaton-123"
}, (err, connection) => {
    if (err) throw err;

    connection.readInputRegisters({ address: 1, quantity: 1 }, (err, res) => {
        if (err) throw err;

        console.log(res); // response
    })
});

Something I see missing -- not setting the device ID to connect to. The device is at ID=1 and I see in the protocol dump that is the address which was requested.

unitId option do not work as expect

const modbus = require(".")
modbus.tcp.connect(10000, "127.0.0.1", { unitId: 3, debug: "automaton-2454" }, (err, connection) => {
    // do something with connection
    connection.readCoils( {address: 0, quantity:4}, (err, res) => {
        console.log(err)
        console.log(res)
    })
});

debug log:

2017-09-12T10:23:56.833Z << automaton-2454 0x[ 00, 01, 00, 00, 00, 06, 01, 01, 00, 00, 00, 04 ]

unitId still 01, ([ 00, 01, 00, 00, 00, 06, 01(should be 03), 01, 00, 00, 00, 04 ])

Supporting SSL / TLS

I am not able to find any information regarding the support of SSL / TLS. I am not able to see any specific security options on the tcp transport / connect options

Is it as simple as specifying the SSL port number instead?

Ending a Connection, Multiple Reading of Registers

Hi ,
I am new to modbus & node.js . Nevertheless I was able to read and write holding registers of a my-PV Boiler Heater ( Model: ELWA-E ) . This heater screw in heater has a LAN and modbus-tcp connection.

I am not sure why the node execution continues (does not end) , and why a third communication fails.

The code below opens a connection, then reads all the holding registers, sets the target boiler temperature , and finally re-reads the set register to validate the change.

I need to somehow close the connection ( timeout , timer ? .... ) , and be able to do the last "readHoldingRegisters". Any help would be appreciated.

Here is the code -

Detect wrong modbus responses and discard noise bytes

Hi dresende

Thank you for your great library.

This is my scenario:

  • A device slave (programmable automaton) behind NAT communicating through GPRS. Start the communication with the backend and, to keep the connection alive, sends heartbeat periodically to backend (heartbeat is a 0xFF byte).
  • A nodejs-backend using modbus-stream in TCP server mode. Once the device-slave establish the TCP connection, the backend send modbus messages (ReadHoldingRegisters, WriteCoil,...) to device-slave.

The issue is: the modbus-stream library doesn't discard the heartbeat from the internal buffer and through exception because of wrong modbus responses.

... <modbus-response><noise-byte><noise-byte><noise-byte><modbus-response><modbus-response><noise-byte><modbus-response> ...

Can you add some check code to discard those "heartbeat/noise bytes"?

I've been looking at your code and i think the best place to add this check code is:

lib/transport/tcp.js (line 73) (inside Transport.prototype.unwrap function):

and a simple check code, based on "Protocol Identifier" is always 0x0000, min length and Unit-ID matching, would be:

for(; (this.buffer.length >= 7);) {
	if ((this.buffer.readUInt16BE(2) !== 0) || (this.buffer.readUInt16BE(4) < 3) || (this.buffer.readUInt8(6) !== this.unitId)) {
		this.buffer = this.buffer.slice(1, this.buffer.length);
	} else {
		break;
	}
}

What do you think?

Thank you

Can someone provide a sample of writeSingleCoil?

I'm able to read all my coils fine, preform hex conversions, etc just fine. I'm having issues setting some binary coils (or any coils) and can narrow it down to my weak understanding of JS in this instance. Could someone point me in the write direction of a writeSingleCoil use example? Sorry to be a bother.

Disabling logs

Hi,

Is there a way to disable logging. Log files go crazy.

Thanks.

slaveId working?

Just trying to get a serial connection on a Raspberry Pi going to a modbus slave with address 10. I know the address is correct and 9600 baud etc because I can connect using another program on the Pi to the device.
I have the following code for modbus-stream
modbus.serial.connect("/dev/ttyUSB0",{slaveId:10},(err,connection)=> etc
The connection appears to be set up as I can look at the connection parameter and it looks OK, the transport variable always contains slaveId:1 regardless of what I put in the setup object. Am I doing something wrong here or does the slaveId setting not work?
Just a PS here. I managed to find out how to change the slave device address to 1 and the code works, however I do need to address other devices besides address 1. So the question still stands
Thanks
Robyn

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.