问题
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