节点串行端口堆叠侦听器并且没有使用承诺获得完整响应

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

我遇到了一个问题,即我尝试使用的基于承诺的代码并不是每次都能得到完整的响应。我将数据侦听器与 .on 一起使用,但将其更改为 .once,因为测试显示我在每次调用时都在堆叠数据侦听器。但无论哪种方式,我偶尔都会得到部分回应。那么我该如何解决这个问题。不是堆叠侦听器,而是每次都获得完整的响应......并使用承诺来完成。

    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);
      });

这里是调用代码

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)
                        });

这里是使用 .on 调用 4 次的输出,完整的响应应该是 [ 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

另一次响应是 [170, 1, 0],大多数时候我都能得到完整的响应。

.once 的结果类似,但侦听器未堆叠。

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

想法?想法?关于修复但使用承诺。

我的代码来自我在这里找到的想法。 Nodejs map serial port write to receive data

serialport gitter 的人那里得到了一些帮助,所以发布了我想出的一个完整的工作解决方案。底线是你必须使用 .on 然后你必须以某种方式知道你将要返回多少字节。对于我的设备,我返回一个前导字节,然后是第二个字节,告诉后面有多少字节,最后一个字节是校验和。我写这个是为了 "plug in" 他们自己的缓冲区解析器可以一直连接块,直到你告诉它不要通过将完成标志更改为 true 来连接。

所以 sendPort 上面的结果是这样的,其中 port 已经创建并打开 serialPortcmd 是作为缓冲区发送到设备的命令,parser 是特定于您的设备的函数,它将以块的形式解析返回的缓冲区,直到您说完成为止。

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);
    });
},

Parse 对象看起来像这样

// 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

}

然后你需要编写你自己的解析器函数,你传递到这里 let parse = _.Parse(parser); 特定于您的设备。这是用于 NCD ProXR 中继板。

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
        }
    }
},

为了让它不堆叠 "data" 个侦听器,我在创建端口时添加了一个重置​​方法,当我知道已检索到完整的响应时可以调用该方法。我使用了 removeALLlisteners,因为我无法让单曲工作,没关系,我知道代码中的任何地方都没有其他 "data" 侦听器。

 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;

最后要注意的是 Parse 对象包含一个 .reset 方法,您会看到它和 port.reset 在完成完整响应后被调用。您需要这样做,否则 done 标志不会变为 false,并且 .response 仍将包含前一个并且 "data" 听众将堆叠。