UNPKG

4.92 kBJavaScriptView Raw
1'use strict';
2
3var _ = require('lodash');
4var util = require('util');
5var when = require('when');
6var EventEmitter = require('events').EventEmitter;
7
8function resolveCallback(resolve, reject){
9 return function(result){
10 if(chrome.runtime.lastError){
11 console.log('error', chrome.runtime.lastError);
12 reject(new Error(chrome.runtime.lastError.message));
13 }else{
14 resolve(result);
15 }
16 };
17}
18
19function promisify(api){
20 return _.reduce(api, function(result, method, name){
21 if(_.isFunction(method)){
22 result[name] = function(){
23 var args = _.toArray(arguments);
24 return when.promise(function(resolve, reject){
25 method.apply(api, args.concat(resolveCallback(resolve, reject)));
26 });
27 };
28 }
29 return result;
30 }, {});
31}
32
33var serial = promisify(chrome.serial);
34
35function toBuffer(ab) {
36 var buffer = new Buffer(ab.byteLength);
37 var view = new Uint8Array(ab);
38 for (var i = 0; i < buffer.length; ++i) {
39 buffer[i] = view[i];
40 }
41 return buffer;
42}
43
44// Convert string to ArrayBuffer
45function str2ab(str) {
46 var buf = new ArrayBuffer(str.length);
47 var bufView = new Uint8Array(buf);
48 for (var i = 0; i < str.length; i++) {
49 bufView[i] = str.charCodeAt(i);
50 }
51 return buf;
52}
53
54// Convert buffer to ArrayBuffer
55function buffer2ArrayBuffer(buffer) {
56 var buf = new ArrayBuffer(buffer.length);
57 var bufView = new Uint8Array(buf);
58 for (var i = 0; i < buffer.length; i++) {
59 bufView[i] = buffer[i];
60 }
61 return buf;
62}
63
64
65function Transport(options) {
66 EventEmitter.call(this);
67
68 this._connectionId = -1;
69 this._paused = false;
70 this.autoRecover = true;
71
72 this._resuming = false;
73
74 if(!options.path){
75 throw new Error('Not path in Transport options');
76 }
77
78 this._options = _.cloneDeep(options);
79
80 this.onError = this._onError.bind(this);
81 this.onReceive = this._onReceive.bind(this);
82}
83
84util.inherits(Transport, EventEmitter);
85
86Transport.prototype._onReceive = function onReceive(info){
87 this.emit('data', toBuffer(info.data));
88};
89
90Transport.prototype._onError = function onError(err){
91 var self = this;
92
93 switch(err.error){
94 case 'disconnected':
95 case 'device_lost':
96 if(self._connectionId >= 0){
97 //hard error, close connection
98 self.close();
99 }
100 break;
101 case 'timeout':
102 //ignore, timeout will not pause connection
103 break;
104 case 'frame_error':
105 case 'system_error':
106 case 'overrun':
107 case 'buffer_overflow':
108 case 'parity_error':
109 case 'break':
110 self._paused = true;
111 if(self.autoRecover){
112 //attempt to unpause automatically
113 self.unpause();
114 }
115 break;
116 }
117};
118
119Transport.prototype.isOpen = function isOpen(){
120 return this._connectionId >= 0;
121};
122
123Transport.prototype.isPaused = function isPaused(){
124 return this._paused;
125};
126
127Transport.prototype.open = function(){
128 if(this.isOpen()){
129 return when.resolve(true);
130 }
131
132 var self = this;
133 var path = this._options.path;
134 var opts = {
135 bitrate: this._options.baudrate || 9600
136 };
137
138 return serial.connect(path, opts)
139 .then(function(connInfo){
140 self._connectionId = connInfo.connectionId;
141 chrome.serial.onReceiveError.addListener(self.onError);
142 chrome.serial.onReceive.addListener(self.onReceive);
143 self.emit('open');
144 });
145};
146
147Transport.prototype.close = function(){
148 var self = this;
149
150 return serial.disconnect(self._connectionId)
151 .ensure(function(){
152 self._connectionId = -1;
153 chrome.serial.onReceiveError.removeListener(self.onError);
154 chrome.serial.onReceive.removeListener(self.onReceive);
155 self.emit('close');
156 });
157};
158
159Transport.prototype.set = function(options){
160 return serial.setControlSignals(this._connectionId, options);
161};
162
163Transport.prototype.setBreak = function(){
164 return serial.setBreak(this._connectionId);
165};
166
167Transport.prototype.clearBreak = function(){
168 return serial.clearBreak(this._connectionId);
169};
170
171Transport.prototype.flush = function(){
172 return serial.flush(this._connectionId);
173};
174
175Transport.prototype.pause = function(){
176 var self = this;
177 return serial.setPaused(self._connectionId, true)
178 .then(function(){
179 self._paused = true;
180 self._resuming = false;
181 });
182};
183
184Transport.prototype.unpause = function(){
185 var self = this;
186 self._resuming = serial.setPaused(self._connectionId, false)
187 .then(function(){
188 self._paused = false;
189 self._resuming = false;
190 });
191 return self._resuming;
192};
193
194Transport.prototype.send = function write(data){
195 var self = this;
196
197 if (typeof data === 'string') {
198 data = str2ab(buffer);
199 }
200 if (data instanceof ArrayBuffer === false) {
201 data = buffer2ArrayBuffer(data);
202 }
203
204 return serial.send(self._connectionId, data);
205};
206
207Transport.listPorts = function(){
208 return serial.getDevices()
209 .then(function(devices){
210 return _.pluck(devices, 'path');
211 });
212};
213
214module.exports = Transport;