Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transform parsers into streams #922

Merged
merged 1 commit into from
Sep 9, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 29 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -507,57 +507,58 @@ 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.
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.

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.

**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. |
| ByteLength | <code>Class</code> | is a transform stream that emits data each time a byte sequence is received. |
| Delimiter | <code>Class</code> | is a transform stream that emits data as a buffer after a specific number of bytes are received. |
| ReadLine | <code>Class</code> | is a transform stream that emits data after a newline delimiter is received. |

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

```js
var SerialPort = require('serialport');
var ReadLine = SerialPort.parsers.ReadLine;
var port = new SerialPort('/dev/tty-usbserial1');
var parser = new ReadLine();
port.pipe(parser);
parser.on('data', console.log);
port.write('ROBOT PLEASE RESPOND\n');

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

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

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', {
parser: SerialPort.parsers.raw
});
var ByteLength = SerialPort.parsers.ByteLength
var port = new SerialPort('/dev/tty-usbserial1');
var parser = port.pipe(new ByteLength({length: 8}));
parser.on('data', console.log);
```

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:
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', {
parser: SerialPort.parsers.byteDelimiter([10,13])
});
var Delimiter = SerialPort.parsers.Delimiter;
var port = new SerialPort('/dev/tty-usbserial1');
var parser = port.pipe(new Delimiter({delimiter: new Buffer('EOL')}));
parser.on('data', console.log);
```

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

var port = new SerialPort('/dev/tty-usbserial1', {
parser: SerialPort.parsers.byteLength(5)
});
var ReadLine = SerialPort.parsers.ReadLine;
var port = new SerialPort('/dev/tty-usbserial1');
var parser = port.pipe(ReadLine({delimiter: '\r\n'}));
parser.on('data', console.log);
```

-
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;
115 changes: 50 additions & 65 deletions lib/parsers.js
Original file line number Diff line number Diff line change
@@ -1,73 +1,58 @@
'use strict';

// Copyright 2011 Chris Williams <chris@iterativedesigns.com>
/**
* 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.

function isEqual(arr1, arr2) {
var isArray = Array.isArray;
if (!isArray(arr1) || !isArray(arr2) || arr1.length !== arr2.length) {
return false;
}
var l = arr1.length;
for (var i = 0; i < l; i += 1) {
if (arr1[i] !== arr2[i]) {
return false;
}
}
return true;
}
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.
* @name module:serialport.parsers
* @type {object}
* @property {Class} [ByteLength] is a transform stream that emits data each time a byte sequence is received.
* @property {Class} [Delimiter] is a transform stream that emits data as a buffer after a specific number of bytes are received.
* @property {Class} [ReadLine] is a transform stream that emits data after a newline delimiter is received.
* @example
```js
var SerialPort = require('serialport');
var ReadLine = SerialPort.parsers.ReadLine;
var port = new SerialPort('/dev/tty-usbserial1');
var parser = new ReadLine();
port.pipe(parser);
parser.on('data', console.log);
port.write('ROBOT PLEASE RESPOND\n');

module.exports = {
raw: function(emitter, buffer) {
emitter.emit('data', buffer);
},
// creating the parser and piping can be shortened to
var parser = port.pipe(new ReadLine());
```

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

// encoding: ascii utf8 utf16le ucs2 base64 binary hex
// More: http://nodejs.org/api/buffer.html#buffer_buffer
readline: function(delimiter, encoding) {
if (typeof delimiter === 'undefined' || delimiter === null) { delimiter = '\r' }
if (typeof encoding === 'undefined' || encoding === null) { encoding = 'utf8' }
// Delimiter buffer saved in closure
var data = '';
return function(emitter, buffer) {
// Collect data
data += buffer.toString(encoding);
// Split collected data by delimiter
var parts = data.split(delimiter);
data = parts.pop();
parts.forEach(function(part) {
emitter.emit('data', part);
});
};
},
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 Delimiter = SerialPort.parsers.Delimiter;
var port = new SerialPort('/dev/tty-usbserial1');
var parser = port.pipe(new Delimiter({delimiter: new Buffer('EOL')}));
parser.on('data', console.log);
```

// Emit a data event every `length` bytes
byteLength: function(length) {
var data = new Buffer(0);
return function(emitter, buffer) {
data = Buffer.concat([data, buffer]);
while (data.length >= length) {
var out = data.slice(0, length);
data = data.slice(length);
emitter.emit('data', out);
}
};
},
To use the ReadLine parser, you may provide a delimiter (defaults to '\n')
```js
var SerialPort = require('serialport');
var ReadLine = SerialPort.parsers.ReadLine;
var port = new SerialPort('/dev/tty-usbserial1');
var parser = port.pipe(ReadLine({delimiter: '\r\n'}));
parser.on('data', console.log);
```
*/

// Emit a data event each time a byte sequence (delimiter is an array of byte) is found
// Sample usage : byteDelimiter([10, 13])
byteDelimiter: function(delimiter) {
if (!Array.isArray(delimiter)) {
delimiter = [ delimiter ];
}
var buf = [];
return function(emitter, buffer) {
for (var i = 0; i < buffer.length; i++) {
buf[buf.length] = buffer[i];
if (isEqual(delimiter, buf.slice(-delimiter.length))) {
emitter.emit('data', buf);
buf = [];
}
}
};
}
module.exports = {
ReadLine: require('./parser-read-line'),
Delimiter: require('./parser-delimiter'),
ByteLength: require('./parser-byte-length')
};
Loading