-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is a major change. To `read()`, the poller and the bindings layer, there isn’t a great way to do this piecemeal. The goal here is to make bindings a plugin layer, our c++ bindings isolated and tested, and make serialport a valid streams object. - Move all bindings into platform specific files - Provide mock bindings as a tested first class bindings layer - Separate out the binary bindings from the SerialPort JS so it can be required without bindings - Make Bindings manage .isOpen and FD as file descriptors are an implementation detail - Make Bindings an instantiated object so they can isolate their own state, have setup code, etc. - Since all integration tests now require an arduino ditch the "integration-light" tests and test with both native and mock bindings TODO - Figure out how we’re going to do _read - ensure docs are up to date with sp methods - ensure bindings are documented - redo parsers as transform streams - update parser documentation - performance test - 🎉 We're currently providing `push` to the binding constructor and asking the binding to provide a `_read` implementation. Considering a `read(function(data){});` interface instead. The c++ bindings use .open to create some global state around write queues and read pollers. Now that the stream will only have a single call to write out at any time this is probably not necessary. For Reads on the other hands it might absolutely be necessary. In any case, hiding this state in a “global scope” is bad practice.
- Loading branch information
Showing
22 changed files
with
1,531 additions
and
1,323 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
'use strict'; | ||
|
||
switch (process.platform) { | ||
case 'win32': | ||
module.exports = require('./bindings-win32'); | ||
break; | ||
case 'darwin': | ||
module.exports = require('./bindings-darwin'); | ||
break; | ||
default: | ||
module.exports = require('./bindings-unix'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
'use strict'; | ||
|
||
var binding = require('bindings')('serialport.node'); | ||
|
||
function DarwinBinding(opt) { | ||
if (typeof opt.disconnect !== 'function') { | ||
throw new TypeError('options.disconnect is not a function'); | ||
} | ||
this.disconnect = opt.disconnect; | ||
this.fd = null; | ||
}; | ||
|
||
DarwinBinding.prototype.open = function(path, options, cb) { | ||
binding.open(path, options, function(err, fd) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
this.fd = fd; | ||
cb(null); | ||
}.bind(this)); | ||
}; | ||
|
||
DarwinBinding.prototype.close = function(cb) { | ||
if (!this.isOpen) { | ||
return cb(new Error('Already closed')); | ||
} | ||
|
||
binding.close(this.fd, function(err) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
this.fd = null; | ||
cb(null); | ||
}.bind(this)); | ||
}; | ||
|
||
DarwinBinding.prototype.set = function(opt, cb) { | ||
if (typeof opt !== 'object') { | ||
throw new TypeError('options is not an object'); | ||
} | ||
|
||
if (!this.isOpen) { | ||
return cb(new Error('Port is not open')); | ||
} | ||
binding.set(this.fd, opt, cb); | ||
}; | ||
|
||
DarwinBinding.prototype.write = function(buffer, cb) { | ||
if (!Buffer.isBuffer(buffer)) { | ||
throw new TypeError('buffer is not a Buffer'); | ||
} | ||
|
||
if (!this.isOpen) { | ||
return cb(new Error('Port is not open')); | ||
} | ||
|
||
binding.write(this.fd, buffer, cb); | ||
}; | ||
|
||
var commonMethods = [ | ||
'drain', | ||
'flush', | ||
'update', | ||
'read' | ||
]; | ||
|
||
commonMethods.map(function(methodName) { | ||
DarwinBinding.prototype[methodName] = function() { | ||
var args = Array.prototype.slice.apply(arguments); | ||
if (!this.isOpen) { | ||
var cb = args.pop(); | ||
return cb(new Error('Port is not open')); | ||
} | ||
args.unshift(this.fd); | ||
binding[methodName].apply(binding, args); | ||
}; | ||
}); | ||
|
||
Object.defineProperty(DarwinBinding.prototype, 'isOpen', { | ||
enumerable: true, | ||
get: function() { | ||
return this.fd !== null; | ||
} | ||
}); | ||
|
||
DarwinBinding.list = binding.list; | ||
module.exports = DarwinBinding; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
'use strict'; | ||
|
||
var util = require('util'); | ||
var processNextTick = require('process-nextick-args'); | ||
|
||
function MissingPortError(message) { | ||
this.message = message || 'Port does not exist - please call hardware.createPort(path) first'; | ||
this.name = 'MissingPortError'; | ||
Error.captureStackTrace(this, MissingPortError); | ||
} | ||
util.inherits(MissingPortError, Error); | ||
|
||
var ports = {}; | ||
|
||
function MockBindings(opt) { | ||
if (typeof opt.disconnect !== 'function') { | ||
throw new TypeError('options.disconnect is not a function'); | ||
} | ||
this.disconnectedCallback = opt.disconnect; | ||
this.isOpen = false; | ||
}; | ||
|
||
MockBindings.reset = function() { | ||
ports = {}; | ||
}; | ||
|
||
MockBindings.createPort = function(path, options) { | ||
var echo = (options || {}).echo; | ||
ports[path] = { | ||
data: new Buffer(0), | ||
lastWrite: null, | ||
echo: echo, | ||
info: { | ||
comName: path, | ||
manufacturer: 'The J5 Robotics Company', | ||
serialNumber: undefined, | ||
pnpId: undefined, | ||
locationId: undefined, | ||
vendorId: undefined, | ||
productId: undefined | ||
} | ||
}; | ||
}; | ||
|
||
MockBindings.list = function(cb) { | ||
var info = Object.keys(ports).map(function(path) { | ||
return ports[path].info; | ||
}); | ||
processNextTick(cb, null, info); | ||
}; | ||
|
||
MockBindings.prototype.emitData = function(data) { | ||
if (!this.port) { | ||
return; | ||
} | ||
this.port.data = Buffer.concat([this.port.data, data]); | ||
if (this.pendingRead) { | ||
processNextTick(this.finishRead.bind(this)); | ||
} | ||
}; | ||
|
||
MockBindings.prototype.disconnect = function() { | ||
var err = new Error('disconnected'); | ||
this.disconnectedCallback(err); | ||
}; | ||
|
||
MockBindings.prototype.open = function(path, opt, cb) { | ||
var port = this.port = ports[path]; | ||
if (!port) { | ||
return cb(new MissingPortError(path)); | ||
} | ||
|
||
if (port.openOpt && port.openOpt.lock) { | ||
return cb(new Error('port is locked cannot open')); | ||
} | ||
port.openOpt = opt; | ||
processNextTick(function() { | ||
this.isOpen = true; | ||
processNextTick(function() { | ||
cb(null); | ||
if (port.echo) { | ||
processNextTick(function() { | ||
this.emitData(new Buffer('READY')); | ||
}.bind(this)); | ||
} | ||
}.bind(this)); | ||
}.bind(this)); | ||
}; | ||
|
||
MockBindings.prototype.close = function(cb) { | ||
var port = this.port; | ||
if (!port) { | ||
return processNextTick(cb, new Error('port is already closed')); | ||
} | ||
processNextTick(function() { | ||
delete port.openOpt; | ||
|
||
// reset data on close | ||
port.data = new Buffer(0); | ||
|
||
delete this.port; | ||
this.isOpen = false; | ||
processNextTick(cb, null); | ||
}.bind(this)); | ||
}; | ||
|
||
MockBindings.prototype.update = function(opt, cb) { | ||
if (typeof opt !== 'object') { | ||
throw new TypeError('options is not an object'); | ||
} | ||
|
||
if (!opt.baudRate) { | ||
throw new Error('Missing baudRate'); | ||
} | ||
|
||
if (!this.port) { | ||
return processNextTick(cb, new MissingPortError()); | ||
} | ||
this.port.openOpt.baudRate = opt.baudRate; | ||
processNextTick(cb, null); | ||
}; | ||
|
||
MockBindings.prototype.set = function(opt, cb) { | ||
if (typeof opt !== 'object') { | ||
throw new TypeError('options is not an object'); | ||
} | ||
|
||
if (!this.port) { | ||
return processNextTick(cb, new MissingPortError()); | ||
} | ||
processNextTick(cb, null); | ||
}; | ||
|
||
MockBindings.prototype.write = function(buffer, cb) { | ||
if (!Buffer.isBuffer(buffer)) { | ||
throw new TypeError('buffer is not a Buffer'); | ||
} | ||
|
||
var port = this.port; | ||
if (!port) { | ||
return processNextTick(cb, new MissingPortError()); | ||
} | ||
|
||
port.lastWrite = new Buffer(buffer); // copy | ||
processNextTick(cb, null); | ||
|
||
if (port.echo) { | ||
processNextTick(this.emitData.bind(this), port.lastWrite); | ||
} | ||
}; | ||
|
||
MockBindings.prototype.read = function(cb) { | ||
var port = this.port; | ||
if (!port) { | ||
return processNextTick(cb, new MissingPortError()); | ||
} | ||
if (this.pendingRead) { | ||
return processNextTick(cb, new Error('Already reading')); | ||
} | ||
var data = port.data; | ||
port.data = new Buffer(0); | ||
if (data.length > 0) { | ||
return processNextTick(cb, null, data); | ||
} | ||
this.pendingRead = cb; | ||
}; | ||
|
||
MockBindings.prototype.finishRead = function() { | ||
var cb = this.pendingRead; | ||
delete this.pendingRead; | ||
processNextTick(this.read.bind(this), cb); | ||
}; | ||
|
||
MockBindings.prototype.flush = function(cb) { | ||
if (!this.port) { | ||
return processNextTick(cb, new MissingPortError()); | ||
} | ||
processNextTick(cb, null); | ||
}; | ||
|
||
MockBindings.prototype.drain = function(cb) { | ||
if (!this.port) { | ||
return processNextTick(cb, new MissingPortError()); | ||
} | ||
processNextTick(cb, null); | ||
}; | ||
|
||
module.exports = MockBindings; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
'use strict'; | ||
|
||
var bindings = require('bindings')('serialport.node'); | ||
var listLinux = require('./list-linux'); | ||
|
||
module.exports = { | ||
close: bindings.close, | ||
drain: bindings.drain, | ||
flush: bindings.flush, | ||
list: listLinux, | ||
open: bindings.open, | ||
set: bindings.set, | ||
update: bindings.update, | ||
write: bindings.write, | ||
read: bindings.read, | ||
dataAvailable: bindings.dataAvailable | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
'use strict'; | ||
|
||
var bindings = require('bindings')('serialport.node'); | ||
|
||
module.exports = { | ||
close: bindings.close, | ||
drain: bindings.drain, | ||
flush: bindings.flush, | ||
list: bindings.list, | ||
open: bindings.open, | ||
set: bindings.set, | ||
update: bindings.update, | ||
write: bindings.write, | ||
read: bindings.read, | ||
dataAvailable: bindings.dataAvailable | ||
}; |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
'use strict'; | ||
|
||
/** | ||
* @module serialport | ||
* @copyright Chris Williams <chris@iterativedesigns.com> | ||
*/ | ||
|
||
var SerialPort = require('./serialport'); | ||
var Binding = require('./bindings-auto-detect'); | ||
|
||
SerialPort.Binding = Binding; | ||
module.exports = SerialPort; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
'use strict'; | ||
// var fs = require('fs'); | ||
|
||
// module.export = function read(size) { | ||
// var push = this.push.bind(this); | ||
// var fd = this.fd; | ||
// }; | ||
|
||
|
||
// bindings.ondata(fd, function(chunk) { | ||
// // if push() returns false, then stop reading from source | ||
// if (!this.push(chunk)) { | ||
// this._source.readStop(); | ||
// } | ||
// }); |
Oops, something went wrong.