Cloud-Automation / node-modbus

Modbus TCP Client/Server implementation for Node.JS
471 stars 175 forks source link

How could I broadcast data of modbus in realtime? #118

Closed panyunyi closed 7 years ago

panyunyi commented 7 years ago

I used websockets(socket.io) to transfer the data.But the app crashed after a period of time because of memory leak caused the collapse.

var io=require('socket.io')(http);
var modbus = require('jsmodbus');
io.sockets.on( "connection", function( socket ){
    console.log('socket connect');
    let client = modbus.client.tcp.complete({ 
        'host'              : '192.168.1.8', 
        'port'              : 10000,
        'autoReconnect'     : false,
        'reconnectTimeout'  : 1000,
        'timeout'           : 5000,
        'unitId'            : 1
    });
    client.connect();
    client.on('connect', function () {
        console.log('modbus');
        setInterval(function(){
            client.readInputRegisters(1,24).then(function(resp){
                    let arr=resp.payload;
                    socket.emit('info',arr);
                },console.error);
        },1000);
        socket.on('switch',function(msg){
            client.writeSingleCoil(msg.flag, msg.on).then(function (resp) {
                console.log(resp);
            }, console.error);
            console.log(msg);
        });
        socket.on('disconnect',function(socket){
            console.log('socket disconnect');
            client.close();
            delete client;
        });
    });
    client.on('error', function (err) {
        console.log(err);
    });
    client.on('close', function () {
        console.log('modbus close');
    });
});

image

psorowka commented 7 years ago

Chances are good that the memory exceed comes from an outgrowing modbus command queue. This can happen in two ways:

  1. you are polling faster than your client responds
  2. you continue polling when the client is disconnected. jsmodbus will queue all requests you issue when the client is disconnected and send them out once it is connected again.

In your code, I see that you don't handle the case 2 correctly so I would search there first.

Still, from my experience it is not recommendable to poll modbus with a setInterval but rather start the next read in the callback of the previous.

stefanpoeter commented 7 years ago

@panyunyi See issue #19 and #29 for some inspiration.

panyunyi commented 7 years ago

I changed my code.Added a line.Look at this:

socket.on('disconnect',function(socket){
            console.log('socket disconnect');
            client.close();
            clearInterval(interval);
            delete client;
        });

The anonymous timer function registered by setInterval holds a reference to the client. In the case of an anonymous function can not be recovered (setInterval is a global function), client can not be recycled.And then memory leaks. So I added the clearInterval to clear the global function when the socket is disconnected. It's worked. There have been no memory leaks in the past. But there are still some print error messages:

Error:write after end
   at writeAfterEnd(_stream_writeable.js:193:12)
   at Socket.Writeable.write(_stream_writeable.js:244:5)
   at Socket.write(net.js:658:40)

Is this mean that polling faster than the client's response?

psorowka commented 7 years ago

no this looks like a race condition between client.close and clearInterval. I would first switch the two function calls and second I think this could be a good case for process.nextTick. The delete client should IMHO not be necessary.

socket.on('disconnect',function(socket){
            console.log('socket disconnect');
            clearInterval(interval);
                        process.nextTick(client.close);
        });
panyunyi commented 7 years ago

@psorowka I will try it.Thank you very much!

stefanpoeter commented 7 years ago

@panyunyi Any progress?

panyunyi commented 7 years ago

@BauchBeinePoe Yes.It's looks like ok.But the error is still here.

socket.on('disconnect',function(socket){
            console.log('socket disconnect');
            //client.close();
            clearInterval(interval);
            process.nextTick(client.close);
        });
Error:write after end
   at writeAfterEnd(_stream_writeable.js:193:12)
   at Socket.Writeable.write(_stream_writeable.js:244:5)
   at Socket.write(net.js:658:40)

The good news is the error has been appeared less.

stefanpoeter commented 7 years ago

Try this.

client.on('connect', function () {

                var disconnected = false;

        console.log('modbus');
        setInterval(function(){
            client.readInputRegisters(1,24).then(function(resp){
                    let arr=resp.payload;
                                        if (disconnected) {
                                                return;
                                        }
                    socket.emit('info',arr);
                },console.error);
        },1000);

...

socket.on('disconnect', function (socket) {
                              console.log('socket disconnect');
                              disconnected = true;
                              ... });
panyunyi commented 7 years ago

OK.I will try it.We will see the results the day after tomorrow. Thank you very much!

panyunyi commented 7 years ago

image There is something wrong.

client.on('connect', function () {
        var disconnected=false;
        console.log('modbus');
        let interval=setInterval(function(){
            client.readInputRegisters(1,24).then(function(resp){
                    let arr=resp.payload;
                    if(disconnected){
                        return;
                    }
                    socket.emit('info',arr);
                },console.error);
socket.on('disconnect',function(socket){
            console.log('socket disconnect');
            disconnected=true;
            clearInterval(interval);
            process.nextTick(client.close);
        });
stefanpoeter commented 7 years ago

Most of the time this is due to a missing execution context for the called method. Seems like you need to apply the client object to the process.nextTick(client.close). Maybe like this process.nextTick(client.close.bind(client)).

panyunyi commented 7 years ago

It's happened again.The error is still here. But it doesn't stop ever.

panyunyi commented 7 years ago

Thank you very much for your help!