node serialport stacking listeners and not getting complete response using a promise

不打扰是莪最后的温柔 提交于 2019-12-11 08:42:43

问题


I'm having an issue which is the promise based code I am trying to use is not every time getting back the complete response. I was using the data listener with .on, but changed it to .once because testing revealed I was stacking data listeners with each call. But either way I occasionally get partial responses. So how can I fix this. Not stack listeners, but get the complete response every time...and do it using a promise.

    sendPort: function(port, src) {
        return new Promise((resolve, reject) => {
        // .once, not stacking but sometimes incomplete responses, .on stacking listener
            port.once('data', (data) => {
                resolve(data); // TODO parse data here or maybe after return

            });
            port.once('error', (err) => {
                reject(err);
            });
            // have same debug in .then after call showing listerner not removed with .on
            Debug.L1('sendport num data listeners: ', port.listenerCount("data"));  

            port.write(src);
      });

Here is calling code

com.openPort(port).then(port => {
                            _.pTimeout(3000, com.sendPort(port, NCD.gen(args.cmd)))
                                .then(received => {
                                    console.log('complete response: ', NCD.parse(received));
                                    Debug.L1('resolved num data listeners: ', port.listenerCount("data"));
                                })
                        })
                        .catch(function(e) {
                            console.log('error: ', e)
                        });

here is output using .on called 4 times the complete response should have been [ 170, 1, 0, 171 ]

  debug:1 api command array:  +0ms [ 170, 3, 254, 175, 0, 90 ]
  debug:1 sendport num data listeners:  +1ms 4
   complete response:  [ 170 ]
  debug:1 resolved num data listeners:  +2ms 4

another time the response was [ 170, 1, 0 ], most times I get the complete response back.

results are similar for .once but listener is not stacked.

   debug:1 sendport num data listeners:  +0ms 1
complete response:  [ 170, 1, 0 ]
  debug:1 resolved num data listeners:  +1ms 0

Thoughts? Ideas? about a fix but using promises.

My code comes from ideas I found here. Nodejs map serial port write to receive data


回答1:


Got some help from the folks at the serialport gitter so posting a working complete solution I came up with. The bottom line is you have to use .on and then you have to somehow know how many bytes you are going to get back. In my case for my device I get back a leading byte then the second byte telling how many bytes to follow then the last byte is a checksum. I wrote this so one could "plug in" their own buffer parser that keeps concatenating chunks until you tell it not to by changing a done flag to true.

So sendPort above ended up like this where port is already created and opened serialPort, cmd is your outgoing command to the device as a buffer and parser is the function specific to your device that will parse the returning buffer in chunks until you say it's done.

sendPort: function(port, cmd, parser) {
    return new Promise((resolve, reject) => {
        Debug.L2('port and buffer for write', port, cmd)

        let parse = _.Parse(parser); //create object with response and done fields and reference to attached parser

        Debug.L1('parse response and done initally,  ', parse.response, parse.done);

        port.on('data', (chunk) => {

            parse.parser(chunk)
                // Debug.L1('parsed: ', parse.response)
            Debug.L1('parse done after parser call:', parse.done);

            if (parse.done) {
                resolve(parse.response);
                parse.reset() // sets .done to false and clears out .response
                Debug.L1('response and done after resolve/complete,  ', parse.response, parse.done);
                port.reset(); //removes all listners to avoid stacking on next call to sendPort
            }

        });
        port.on('error', (err) => {
            reject(err);
        });

        port.write(cmd);
    });
},

where the Parse object looks like this

// See sendPort - used with custom parser function to return response and completion flag
Parse: function(parser) {
let parse = function() {}

parse.parser = parser;

parse.reset = function reset() {
    this.response = [];
    this.done = '';
}

parse.reset(); // used here to intialize response and

return parse

}

then you need to write your own parser function that you pass into here let parse = _.Parse(parser); which is specific to your device. This is for a NCD ProXR relay board.

parse: function(chunk) {

    for (var byte of chunk) {
        this.response.push(byte);
        Debug.L1('response being built ', this.response)
    }
    Debug.L1('current chunck response ', this.response)
        // api version where first byte is 170,
    if (this.response[1]) {  // second slot is number of bits to follow exlcuding checksum
        if (this.response.length >= 3 + this.response[1]) {   // 3 = 170 + number of bits bit + checksum
            this.done = true
        }
    }
},

To get this not to stack "data" listeners I added a reset method when I create the port that I can call when I know the complete response has been retrieved. I used removeALLlisteners cause I could not get the single to work and it's ok I know there are no other "data" listeners in the code anywhere.

 let serialport = require('serialport'),

 createPort: function(sysDevName, opts) {
    opts = opts || {};
    opts.autoOpen = false; // actually open device later
    Debug.L1(sysDevName, opts);
    let port = new serialport(sysDevName, opts, (err) => {
        if (err) {
            Debug.L1("create error" + err.message);
            return err;
        }

    })
    port.reset = function() {
        this.removeAllListeners("data");
        this.removeAllListeners("error");
    }
    return port;

final note is that the Parse object contains a .reset method and you see it and the port.reset being called after the complete response has been complied. You need to do this or else the done flag won't get to false and the .response will still contain the previous one and "data" listeners will stack.



来源:https://stackoverflow.com/questions/40075466/node-serialport-stacking-listeners-and-not-getting-complete-response-using-a-pro

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!