Skip to content

Commit 0fc091b

Browse files
authored
Merge pull request #922 from EmergingTechnologyAdvisors/parser-streams
Transform parsers into streams
2 parents 394dae8 + d2bb973 commit 0fc091b

File tree

11 files changed

+498
-249
lines changed

11 files changed

+498
-249
lines changed

README.md

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -507,57 +507,58 @@ The `close` event's callback is called with no arguments when the port is closed
507507
<a name="module_serialport--SerialPort.parsers"></a>
508508

509509
#### `SerialPort.parsers` : <code>object</code>
510-
Parsers will process incoming data in a variety of ways and are meant to be passed to a port during construction.
510+
The default Parsers are [Transform streams](https://nodejs.org/api/stream.html#stream_class_stream_transform) that will parse data in a variety of ways and can be used to process incoming data.
511+
512+
To use any of the parsers you need to create them and then pipe the serialport to the parser. Be sure not to write to the parser but to the SerialPort object.
511513

512514
**Kind**: static property of <code>[SerialPort](#exp_module_serialport--SerialPort)</code>
513515
**Properties**
514516

515517
| Name | Type | Description |
516518
| --- | --- | --- |
517-
| raw | <code>function</code> | emits a raw buffer as a data event as it's received. This is the default parser. |
518-
| readline | <code>function</code> | returns a function that emits a string as a data event after a newline delimiter is received. |
519-
| byteLength | <code>function</code> | returns a function that emits a data event as a buffer after a specific number of bytes are received. |
520-
| byteDelimiter | <code>function</code> | returns a function that emits a data event each time a byte sequence (an array of bytes) is received. |
519+
| ByteLength | <code>Class</code> | is a transform stream that emits data each time a byte sequence is received. |
520+
| Delimiter | <code>Class</code> | is a transform stream that emits data as a buffer after a specific number of bytes are received. |
521+
| ReadLine | <code>Class</code> | is a transform stream that emits data after a newline delimiter is received. |
521522

522523
**Example**
523-
To use the readline parser, you must provide a delimiter as such:
524-
525524
```js
526525
var SerialPort = require('serialport');
526+
var ReadLine = SerialPort.parsers.ReadLine;
527+
var port = new SerialPort('/dev/tty-usbserial1');
528+
var parser = new ReadLine();
529+
port.pipe(parser);
530+
parser.on('data', console.log);
531+
port.write('ROBOT PLEASE RESPOND\n');
527532

528-
var port = new SerialPort('/dev/tty-usbserial1', {
529-
parser: SerialPort.parsers.readline('\n')
530-
});
533+
// creating the parser and piping can be shortened to
534+
var parser = port.pipe(new ReadLine());
531535
```
532536

533-
To use the raw parser don't specify any parser, however if you really want to you can:
534-
537+
To use the byte length parser, you must provide the length of the number of bytes:
535538
```js
536539
var SerialPort = require('serialport');
537-
538-
var port = new SerialPort('/dev/tty-usbserial1', {
539-
parser: SerialPort.parsers.raw
540-
});
540+
var ByteLength = SerialPort.parsers.ByteLength
541+
var port = new SerialPort('/dev/tty-usbserial1');
542+
var parser = port.pipe(new ByteLength({length: 8}));
543+
parser.on('data', console.log);
541544
```
542545

543-
Note that the raw parser does not guarantee that all data it receives will come in a single event.
544-
545-
To use the byte sequence parser, you must provide a delimiter as an array of bytes:
546+
To use the Delimiter parser you must specify, you must provide a delimiter as a string, buffer, or an array of bytes:
546547
```js
547548
var SerialPort = require('serialport');
548-
549-
var port = new SerialPort('/dev/tty-usbserial1', {
550-
parser: SerialPort.parsers.byteDelimiter([10,13])
551-
});
549+
var Delimiter = SerialPort.parsers.Delimiter;
550+
var port = new SerialPort('/dev/tty-usbserial1');
551+
var parser = port.pipe(new Delimiter({delimiter: new Buffer('EOL')}));
552+
parser.on('data', console.log);
552553
```
553554

554-
To use the byte length parser, you must provide a delimiter as a length in bytes:
555+
To use the ReadLine parser, you may provide a delimiter (defaults to '\n')
555556
```js
556557
var SerialPort = require('serialport');
557-
558-
var port = new SerialPort('/dev/tty-usbserial1', {
559-
parser: SerialPort.parsers.byteLength(5)
560-
});
558+
var ReadLine = SerialPort.parsers.ReadLine;
559+
var port = new SerialPort('/dev/tty-usbserial1');
560+
var parser = port.pipe(ReadLine({delimiter: '\r\n'}));
561+
parser.on('data', console.log);
561562
```
562563

563564
-
@@ -634,7 +635,6 @@ A callback called with an error or null.
634635
| xoff | <code>boolean</code> | <code>false</code> | flow control setting |
635636
| xany | <code>boolean</code> | <code>false</code> | flow control setting |
636637
| bufferSize | <code>number</code> | <code>65536</code> | Size of read buffer |
637-
| parser | <code>function</code> | <code>Parsers.raw</code> | The parser to transform read data, defaults to the `raw` parser that emits data as it's received. |
638638
| platformOptions | <code>object</code> | | sets platform specific options |
639639
| platformOptions.vmin | <code>number</code> | <code>1</code> | see [`man termios`](http://linux.die.net/man/3/termios) |
640640
| platformOptions.vtime | <code>number</code> | <code>0</code> | see [`man termios`](http://linux.die.net/man/3/termios) |

lib/parser-byte-length.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
'use strict';
2+
var Transform = require('readable-stream').Transform;
3+
var inherits = require('inherits');
4+
5+
function ByteLengthParser(options) {
6+
if (!(this instanceof ByteLengthParser)) {
7+
return new ByteLengthParser(options);
8+
}
9+
Transform.call(this, options);
10+
11+
options = options || {};
12+
13+
if (typeof options.length !== 'number') {
14+
throw new TypeError('length is not a number');
15+
}
16+
17+
if (options.length < 1) {
18+
throw new TypeError('length is not greater than 0');
19+
}
20+
21+
this.length = options.length;
22+
this.buffer = new Buffer(0);
23+
}
24+
25+
inherits(ByteLengthParser, Transform);
26+
27+
ByteLengthParser.prototype._transform = function(chunk, encoding, cb) {
28+
var data = Buffer.concat([this.buffer, chunk]);
29+
while (data.length >= this.length) {
30+
var out = data.slice(0, this.length);
31+
this.push(out);
32+
data = data.slice(this.length);
33+
}
34+
this.buffer = data;
35+
cb();
36+
};
37+
38+
ByteLengthParser.prototype._flush = function(cb) {
39+
this.push(this.buffer);
40+
this.buffer = new Buffer(0);
41+
cb();
42+
};
43+
44+
45+
module.exports = ByteLengthParser;

lib/parser-delimiter.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use strict';
2+
var Transform = require('readable-stream').Transform;
3+
var inherits = require('inherits');
4+
var bindexOf;
5+
6+
if (typeof Buffer.prototype.indexOf === 'function') {
7+
bindexOf = function(buff, search, offset, encoding) {
8+
return buff.indexOf(search, offset, encoding);
9+
};
10+
} else {
11+
bindexOf = require('buffer-indexof');
12+
}
13+
14+
function DelimiterParser(options) {
15+
if (!(this instanceof DelimiterParser)) {
16+
return new DelimiterParser(options);
17+
}
18+
Transform.call(this, options);
19+
20+
options = options || {};
21+
22+
if (options.delimiter === undefined) {
23+
throw new TypeError('delimiter is not a bufferable object');
24+
}
25+
26+
if (options.delimiter.length === 0) {
27+
throw new TypeError('delimiter has a 0 or undefined length');
28+
}
29+
30+
this.delimiter = new Buffer(options.delimiter);
31+
this.buffer = new Buffer(0);
32+
}
33+
34+
inherits(DelimiterParser, Transform);
35+
36+
DelimiterParser.prototype._transform = function(chunk, encoding, cb) {
37+
var data = Buffer.concat([this.buffer, chunk]);
38+
var position;
39+
while ((position = bindexOf(data, this.delimiter)) !== -1) {
40+
this.push(data.slice(0, position));
41+
data = data.slice(position + this.delimiter.length);
42+
}
43+
this.buffer = data;
44+
cb();
45+
};
46+
47+
DelimiterParser.prototype._flush = function(cb) {
48+
this.push(this.buffer);
49+
this.buffer = new Buffer(0);
50+
cb();
51+
};
52+
53+
module.exports = DelimiterParser;

lib/parser-read-line.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
'use strict';
2+
var DelimiterParser = require('./parser-delimiter');
3+
var inherits = require('inherits');
4+
5+
function ReadLineParser(options) {
6+
if (!(this instanceof ReadLineParser)) {
7+
return new ReadLineParser(options);
8+
}
9+
10+
options = options || {};
11+
12+
if (options.delimiter === undefined) {
13+
options.delimiter = new Buffer('\n', 'utf8');
14+
}
15+
16+
DelimiterParser.call(this, options);
17+
18+
var encoding = options.encoding || 'utf8';
19+
this.delimiter = new Buffer(options.delimiter, encoding);
20+
this.setEncoding(encoding);
21+
}
22+
23+
inherits(ReadLineParser, DelimiterParser);
24+
module.exports = ReadLineParser;

lib/parsers.js

Lines changed: 50 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,58 @@
11
'use strict';
22

3-
// Copyright 2011 Chris Williams <chris@iterativedesigns.com>
3+
/**
4+
* The default Parsers are [Transform streams](https://nodejs.org/api/stream.html#stream_class_stream_transform) that will parse data in a variety of ways and can be used to process incoming data.
45
5-
function isEqual(arr1, arr2) {
6-
var isArray = Array.isArray;
7-
if (!isArray(arr1) || !isArray(arr2) || arr1.length !== arr2.length) {
8-
return false;
9-
}
10-
var l = arr1.length;
11-
for (var i = 0; i < l; i += 1) {
12-
if (arr1[i] !== arr2[i]) {
13-
return false;
14-
}
15-
}
16-
return true;
17-
}
6+
To use any of the parsers you need to create them and then pipe the serialport to the parser. Be sure not to write to the parser but to the SerialPort object.
7+
* @name module:serialport.parsers
8+
* @type {object}
9+
* @property {Class} [ByteLength] is a transform stream that emits data each time a byte sequence is received.
10+
* @property {Class} [Delimiter] is a transform stream that emits data as a buffer after a specific number of bytes are received.
11+
* @property {Class} [ReadLine] is a transform stream that emits data after a newline delimiter is received.
12+
* @example
13+
```js
14+
var SerialPort = require('serialport');
15+
var ReadLine = SerialPort.parsers.ReadLine;
16+
var port = new SerialPort('/dev/tty-usbserial1');
17+
var parser = new ReadLine();
18+
port.pipe(parser);
19+
parser.on('data', console.log);
20+
port.write('ROBOT PLEASE RESPOND\n');
1821
19-
module.exports = {
20-
raw: function(emitter, buffer) {
21-
emitter.emit('data', buffer);
22-
},
22+
// creating the parser and piping can be shortened to
23+
var parser = port.pipe(new ReadLine());
24+
```
25+
26+
To use the byte length parser, you must provide the length of the number of bytes:
27+
```js
28+
var SerialPort = require('serialport');
29+
var ByteLength = SerialPort.parsers.ByteLength
30+
var port = new SerialPort('/dev/tty-usbserial1');
31+
var parser = port.pipe(new ByteLength({length: 8}));
32+
parser.on('data', console.log);
33+
```
2334
24-
// encoding: ascii utf8 utf16le ucs2 base64 binary hex
25-
// More: http://nodejs.org/api/buffer.html#buffer_buffer
26-
readline: function(delimiter, encoding) {
27-
if (typeof delimiter === 'undefined' || delimiter === null) { delimiter = '\r' }
28-
if (typeof encoding === 'undefined' || encoding === null) { encoding = 'utf8' }
29-
// Delimiter buffer saved in closure
30-
var data = '';
31-
return function(emitter, buffer) {
32-
// Collect data
33-
data += buffer.toString(encoding);
34-
// Split collected data by delimiter
35-
var parts = data.split(delimiter);
36-
data = parts.pop();
37-
parts.forEach(function(part) {
38-
emitter.emit('data', part);
39-
});
40-
};
41-
},
35+
To use the Delimiter parser you must specify, you must provide a delimiter as a string, buffer, or an array of bytes:
36+
```js
37+
var SerialPort = require('serialport');
38+
var Delimiter = SerialPort.parsers.Delimiter;
39+
var port = new SerialPort('/dev/tty-usbserial1');
40+
var parser = port.pipe(new Delimiter({delimiter: new Buffer('EOL')}));
41+
parser.on('data', console.log);
42+
```
4243
43-
// Emit a data event every `length` bytes
44-
byteLength: function(length) {
45-
var data = new Buffer(0);
46-
return function(emitter, buffer) {
47-
data = Buffer.concat([data, buffer]);
48-
while (data.length >= length) {
49-
var out = data.slice(0, length);
50-
data = data.slice(length);
51-
emitter.emit('data', out);
52-
}
53-
};
54-
},
44+
To use the ReadLine parser, you may provide a delimiter (defaults to '\n')
45+
```js
46+
var SerialPort = require('serialport');
47+
var ReadLine = SerialPort.parsers.ReadLine;
48+
var port = new SerialPort('/dev/tty-usbserial1');
49+
var parser = port.pipe(ReadLine({delimiter: '\r\n'}));
50+
parser.on('data', console.log);
51+
```
52+
*/
5553

56-
// Emit a data event each time a byte sequence (delimiter is an array of byte) is found
57-
// Sample usage : byteDelimiter([10, 13])
58-
byteDelimiter: function(delimiter) {
59-
if (!Array.isArray(delimiter)) {
60-
delimiter = [ delimiter ];
61-
}
62-
var buf = [];
63-
return function(emitter, buffer) {
64-
for (var i = 0; i < buffer.length; i++) {
65-
buf[buf.length] = buffer[i];
66-
if (isEqual(delimiter, buf.slice(-delimiter.length))) {
67-
emitter.emit('data', buf);
68-
buf = [];
69-
}
70-
}
71-
};
72-
}
54+
module.exports = {
55+
ReadLine: require('./parser-read-line'),
56+
Delimiter: require('./parser-delimiter'),
57+
ByteLength: require('./parser-byte-length')
7358
};

0 commit comments

Comments
 (0)