Skip to content

Commit

Permalink
Transform parsers into streams
Browse files Browse the repository at this point in the history
Parsers are now transform streams! I'm not 100% convinced this is easier to use but it is more clear about the mechanics. I think that might make it worthwhile. It also opens up a clear example to how to make your own parser.
  • Loading branch information
reconbot committed Sep 4, 2016
1 parent 71f7fb3 commit c2e1f2e
Show file tree
Hide file tree
Showing 11 changed files with 527 additions and 240 deletions.
120 changes: 60 additions & 60 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ For getting started with node-serialport, we recommend you begin with the follow
* [`Event: "disconnect"`](#module_serialport--SerialPort+event_disconnect)
* [`Event: "close"`](#module_serialport--SerialPort+event_close)
* _static_
* [`.parsers`](#module_serialport--SerialPort.parsers) : <code>object</code>
* [`.list`](#module_serialport--SerialPort.list) : <code>function</code>
* [`.parsers`](#module_serialport--SerialPort.parsers) : <code>object</code>
* _inner_
* [`~errorCallback`](#module_serialport--SerialPort..errorCallback) : <code>function</code>
* [`~openOptions`](#module_serialport--SerialPort..openOptions) : <code>Object</code>
Expand Down Expand Up @@ -504,64 +504,6 @@ The `close` event's callback is called with no arguments when the port is closed

-

<a name="module_serialport--SerialPort.parsers"></a>

#### `SerialPort.parsers` : <code>object</code>
Parsers will process incoming data in a variety of ways and are meant to be passed to a port during construction.

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

| Name | Type | Description |
| --- | --- | --- |
| raw | <code>function</code> | emits a raw buffer as a data event as it's received. This is the default parser. |
| readline | <code>function</code> | returns a function that emits a string as a data event after a newline delimiter is received. |
| byteLength | <code>function</code> | returns a function that emits a data event as a buffer after a specific number of bytes are received. |
| byteDelimiter | <code>function</code> | returns a function that emits a data event each time a byte sequence (an array of bytes) is received. |

**Example**
To use the readline parser, you must provide a delimiter as such:

```js
var SerialPort = require('serialport');

var port = new SerialPort('/dev/tty-usbserial1', {
parser: SerialPort.parsers.readline('\n')
});
```

To use the raw parser don't specify any parser, however if you really want to you can:

```js
var SerialPort = require('serialport');

var port = new SerialPort('/dev/tty-usbserial1', {
parser: SerialPort.parsers.raw
});
```

Note that the raw parser does not guarantee that all data it receives will come in a single event.

To use the byte sequence parser, you must provide a delimiter as an array of bytes:
```js
var SerialPort = require('serialport');

var port = new SerialPort('/dev/tty-usbserial1', {
parser: SerialPort.parsers.byteDelimiter([10,13])
});
```

To use the byte length parser, you must provide a delimiter as a length in bytes:
```js
var SerialPort = require('serialport');

var port = new SerialPort('/dev/tty-usbserial1', {
parser: SerialPort.parsers.byteLength(5)
});
```

-

<a name="module_serialport--SerialPort.list"></a>

#### `SerialPort.list` : <code>function</code>
Expand Down Expand Up @@ -601,6 +543,65 @@ SerialPort.list(function (err, ports) {

-

<a name="module_serialport--SerialPort.parsers"></a>

#### `SerialPort.parsers` : <code>object</code>
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.

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

| Name | Type | Description |
| --- | --- | --- |
| ByteLength | <code>function</code> | is a transform stream that emits data each time a byte sequence is received. |
| Delimiter | <code>function</code> | is a transform stream that emits data as a buffer after a specific number of bytes are received. |
| ReadLine | <code>function</code> | is a transform stream that emits data after a newline delimiter is received. |

**Example**
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.
```js
var SerialPort = require('serialport');
var port = new SerialPort('/dev/tty-usbserial1');
var parser = new SerialPort.parsers.ReadLine();
port.pipe(parser);
parser.on('data', console.log);
port.write('ROBOT PLEASE RESPOND\n');

// this can be shortened to
var SerialPort = require('serialport');
var parser = port.pipe(new SerialPort.parsers.ReadLine());
parser.on('data', console.log);
port.write('ROBOT PLEASE RESPOND\n');
```

To use the byte length parser, you must provide the length of the number of bytes:
```js
var SerialPort = require('serialport');
var port = new SerialPort('/dev/tty-usbserial1');
var parser = port.pipe(new SerialPort.parsers.ByteLength({length: 8}));
port.pipe(parser);
parser.on('data', console.log);
```

To use the Delimiter parser you must specify, you must provide a delimiter as a string, buffer, or an array of bytes:
```js
var SerialPort = require('serialport');
var port = new SerialPort('/dev/tty-usbserial1');
var parser = new SerialPort.parsers.Delimiter({delimiter: new Buffer('EOL')});
port.pipe(parser);
parser.on('data', console.log);
```

To use the ReadLine parser, you must provide a delimiter as such:
```js
var SerialPort = require('serialport');
var SerialPort = require('serialport');
var parser = port.pipe(new SerialPort.parsers.ReadLine({delimiter: '\r\n'}));
parser.on('data', console.log);
```

-

<a name="module_serialport--SerialPort..errorCallback"></a>

#### `SerialPort~errorCallback` : <code>function</code>
Expand Down Expand Up @@ -634,7 +635,6 @@ A callback called with an error or null.
| xoff | <code>boolean</code> | <code>false</code> | flow control setting |
| xany | <code>boolean</code> | <code>false</code> | flow control setting |
| bufferSize | <code>number</code> | <code>65536</code> | Size of read buffer |
| 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. |
| platformOptions | <code>object</code> | | sets platform specific options |
| platformOptions.vmin | <code>number</code> | <code>1</code> | see [`man termios`](http://linux.die.net/man/3/termios) |
| platformOptions.vtime | <code>number</code> | <code>0</code> | see [`man termios`](http://linux.die.net/man/3/termios) |
Expand Down
45 changes: 45 additions & 0 deletions lib/parser-byte-length.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
'use strict';
var Transform = require('readable-stream').Transform;
var inherits = require('inherits');

function ByteLengthParser(options) {
if (!(this instanceof ByteLengthParser)) {
return new ByteLengthParser(options);
}
Transform.call(this, options);

options = options || {};

if (typeof options.length !== 'number') {
throw new TypeError('length is not a number');
}

if (options.length < 1) {
throw new TypeError('length is not greater than 0');
}

this.length = options.length;
this.buffer = new Buffer(0);
}

inherits(ByteLengthParser, Transform);

ByteLengthParser.prototype._transform = function(chunk, encoding, cb) {
var data = Buffer.concat([this.buffer, chunk]);
while (data.length >= this.length) {
var out = data.slice(0, this.length);
this.push(out);
data = data.slice(this.length);
}
this.buffer = data;
cb();
};

ByteLengthParser.prototype._flush = function(cb) {
this.push(this.buffer);
this.buffer = new Buffer(0);
cb();
};


module.exports = ByteLengthParser;
53 changes: 53 additions & 0 deletions lib/parser-delimiter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
'use strict';
var Transform = require('readable-stream').Transform;
var inherits = require('inherits');
var bindexOf;

if (typeof Buffer.prototype.indexOf === 'function') {
bindexOf = function(buff, search, offset, encoding) {
return buff.indexOf(search, offset, encoding);
};
} else {
bindexOf = require('buffer-indexof');
}

function DelimiterParser(options) {
if (!(this instanceof DelimiterParser)) {
return new DelimiterParser(options);
}
Transform.call(this, options);

options = options || {};

if (options.delimiter === undefined) {
throw new TypeError('delimiter is not a bufferable object');
}

if (options.delimiter.length === 0) {
throw new TypeError('delimiter has a 0 or undefined length');
}

this.delimiter = new Buffer(options.delimiter);
this.buffer = new Buffer(0);
}

inherits(DelimiterParser, Transform);

DelimiterParser.prototype._transform = function(chunk, encoding, cb) {
var data = Buffer.concat([this.buffer, chunk]);
var position;
while ((position = bindexOf(data, this.delimiter)) !== -1) {
this.push(data.slice(0, position));
data = data.slice(position + this.delimiter.length);
}
this.buffer = data;
cb();
};

DelimiterParser.prototype._flush = function(cb) {
this.push(this.buffer);
this.buffer = new Buffer(0);
cb();
};

module.exports = DelimiterParser;
24 changes: 24 additions & 0 deletions lib/parser-read-line.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use strict';
var DelimiterParser = require('./parser-delimiter');
var inherits = require('inherits');

function ReadLineParser(options) {
if (!(this instanceof ReadLineParser)) {
return new ReadLineParser(options);
}

options = options || {};

if (options.delimiter === undefined) {
options.delimiter = new Buffer('\n', 'utf8');
}

DelimiterParser.call(this, options);

var encoding = options.encoding || 'utf8';
this.delimiter = new Buffer(options.delimiter, encoding);
this.setEncoding(encoding);
}

inherits(ReadLineParser, DelimiterParser);
module.exports = ReadLineParser;
73 changes: 0 additions & 73 deletions lib/parsers.js

This file was deleted.

Loading

0 comments on commit c2e1f2e

Please sign in to comment.