1 | 'use strict';
|
2 |
|
3 | var _ = require('lodash');
|
4 | var util = require('util');
|
5 | var when = require('when');
|
6 | var nodefn = require('when/node');
|
7 | var reemit = require('re-emitter');
|
8 | var EventEmitter = require('events').EventEmitter;
|
9 |
|
10 | var Transport = require('./transport');
|
11 |
|
12 | var TerminalStreamParser = require('./lib/terminal-stream-parser');
|
13 | var TransmitStreamParser = require('./lib/transmit-stream-parser');
|
14 |
|
15 | function Protocol(options){
|
16 | EventEmitter.call(this);
|
17 |
|
18 | this._terminal = new TerminalStreamParser();
|
19 | this._transmit = new TransmitStreamParser();
|
20 |
|
21 | var TransportCtor = options.transport || Transport;
|
22 |
|
23 | this._options = {
|
24 | echo: options.echo || false
|
25 | };
|
26 |
|
27 | this._transport = new TransportCtor(_.omit(options, 'transport'));
|
28 |
|
29 | reemit(this._transport, this, ['open', 'close', 'data']);
|
30 | this.on('data', this._emitTerminal.bind(this));
|
31 | this._terminalEvents = true;
|
32 | }
|
33 |
|
34 | util.inherits(Protocol, EventEmitter);
|
35 |
|
36 | Protocol.prototype.isOpen = function isOpen(){
|
37 | return this._transport.isOpen();
|
38 | };
|
39 |
|
40 | Protocol.prototype.open = function(cb){
|
41 | var promise;
|
42 | if(this.isOpen()){
|
43 | promise = when.resolve();
|
44 | }else{
|
45 | promise = this._transport.open();
|
46 | }
|
47 | return nodefn.bindCallback(promise, cb);
|
48 | };
|
49 |
|
50 | Protocol.prototype.close = function(cb){
|
51 | return nodefn.bindCallback(this._transport.close(), cb);
|
52 | };
|
53 |
|
54 | Protocol.prototype.enterProgramming = function(options, cb){
|
55 | var self = this;
|
56 | var transport = this._transport;
|
57 |
|
58 | if(typeof options === 'function'){
|
59 | cb = options;
|
60 | options = {};
|
61 | }else{
|
62 | options = options || {};
|
63 | }
|
64 |
|
65 | this._terminalEvents = false;
|
66 |
|
67 | var promise = transport.open()
|
68 | .then(function(){
|
69 | transport.autoRecover = false;
|
70 | return transport.setBreak();
|
71 | })
|
72 | .then(function(){
|
73 | return transport.flush();
|
74 | })
|
75 | .then(function(){
|
76 | if(transport.isPaused()){
|
77 | return transport.unpause();
|
78 | }
|
79 | })
|
80 | .then(function(){
|
81 | return transport.set({ dtr: false });
|
82 | })
|
83 | .then(function(){
|
84 | return transport.set({ dtr: true }).delay(60);
|
85 | })
|
86 | .then(function(){
|
87 | return transport.clearBreak();
|
88 | })
|
89 | .then(function(){
|
90 | transport.autoRecover = true;
|
91 | if(transport.isPaused()){
|
92 | return transport.unpause();
|
93 | }
|
94 | })
|
95 | .then(function(){
|
96 | return transport.flush();
|
97 | });
|
98 |
|
99 | return nodefn.bindCallback(promise, cb);
|
100 | };
|
101 |
|
102 | Protocol.prototype.exitProgramming = function(options, cb){
|
103 | var self = this;
|
104 | var transport = this._transport;
|
105 |
|
106 | if(typeof options === 'function'){
|
107 | cb = options;
|
108 | options = {};
|
109 | }else{
|
110 | options = options || {};
|
111 | }
|
112 |
|
113 | var promise = this.signoff()
|
114 | .then(function(){
|
115 | self._terminal.clearIgnore();
|
116 | if(!options.keepOpen){
|
117 | return transport.close();
|
118 | }
|
119 | })
|
120 | .otherwise(function(err){
|
121 |
|
122 | return transport.close()
|
123 | .then(function(){
|
124 | throw err;
|
125 | });
|
126 | })
|
127 | .ensure(function(){
|
128 | if(options.listen){
|
129 | self._terminalEvents = true;
|
130 | }
|
131 | });
|
132 |
|
133 | return nodefn.bindCallback(promise, cb);
|
134 | };
|
135 |
|
136 | Protocol.prototype._emitTerminal = function _emitTerminal(chunk){
|
137 | if(this._terminalEvents){
|
138 | this.emit('terminal', this._terminal.parseStreamChunk(chunk));
|
139 | }
|
140 | };
|
141 |
|
142 | Protocol.prototype.send = function send(data, cb){
|
143 | var self = this;
|
144 |
|
145 | var responseLength = data.length + 1;
|
146 |
|
147 | var defer = when.defer();
|
148 |
|
149 | var buffer = new Buffer(0);
|
150 | function onChunk(chunk){
|
151 | buffer = Buffer.concat([buffer, chunk]);
|
152 |
|
153 | if(buffer.length < responseLength){
|
154 |
|
155 | return;
|
156 | }
|
157 | if (buffer.length > responseLength) {
|
158 |
|
159 | defer.reject(new Error('buffer overflow ' + buffer.length + ' > ' + responseLength));
|
160 | return;
|
161 | }
|
162 | if (buffer.length === responseLength) {
|
163 | defer.resolve(buffer[data.length]);
|
164 | }
|
165 | }
|
166 |
|
167 | this.on('data', onChunk);
|
168 |
|
169 | this._transport.send(data)
|
170 | .catch(defer.reject);
|
171 |
|
172 | var promise = defer.promise.finally(function(){
|
173 | self.removeListener('data', onChunk);
|
174 | });
|
175 |
|
176 | return nodefn.bindCallback(promise, cb);
|
177 | };
|
178 |
|
179 | Protocol.prototype.write = function(data, cb){
|
180 | var transmitEvents = this._transmit.parseStreamChunk(data);
|
181 | this.emit('transmit', transmitEvents);
|
182 |
|
183 | if(!this._options.echo){
|
184 | this._terminal.ignore(data);
|
185 | }
|
186 | var promise = this._transport.send(data);
|
187 |
|
188 | return nodefn.bindCallback(promise, cb);
|
189 | };
|
190 |
|
191 | Protocol.prototype.reset = function reset(cb){
|
192 | var transport = this._transport;
|
193 |
|
194 | var promise = transport.set({ dtr: true })
|
195 | .delay(2)
|
196 | .then(function(){
|
197 | return transport.set({ dtr: false });
|
198 | });
|
199 |
|
200 | return nodefn.bindCallback(promise, cb);
|
201 | };
|
202 |
|
203 | Protocol.prototype.signoff = function signoff(cb){
|
204 | var signoffBit = new Buffer([0]);
|
205 |
|
206 | var promise = this._transport.send(signoffBit);
|
207 |
|
208 | return nodefn.bindCallback(promise, cb);
|
209 | };
|
210 |
|
211 | Protocol.prototype.setEcho = function setEcho(echo){
|
212 | this._options.echo = echo;
|
213 | this._terminal.clearIgnore();
|
214 | };
|
215 |
|
216 | Protocol.listPorts = function(cb){
|
217 |
|
218 | return nodefn.bindCallback(Transport.listPorts(), cb);
|
219 | };
|
220 |
|
221 | module.exports = Protocol;
|