1 | 'use strict';
|
2 |
|
3 | var _ = require('lodash');
|
4 | var util = require('util');
|
5 | var bs2 = require('bs2-programmer');
|
6 | var nodefn = require('when/node');
|
7 | var bluebird = require('bluebird');
|
8 | var Bs2Programmer = bs2.Programmer;
|
9 | var reemit = require('re-emitter');
|
10 | var bs2tokenizer = require('pbasic-tokenizer');
|
11 | var EventEmitter = require('events').EventEmitter;
|
12 | var Bs2SerialProtocol = require('bs2-serial-protocol');
|
13 |
|
14 | function internalCompile(source){
|
15 | var TModuleRec = bs2tokenizer.compile(source, false);
|
16 |
|
17 | var program = {
|
18 | data: TModuleRec.PacketBuffer.slice(0, TModuleRec.PacketCount * 18),
|
19 | name: TModuleRec.TargetModuleName,
|
20 | error: !TModuleRec.Succeeded ? TModuleRec.Error : null,
|
21 | raw: TModuleRec
|
22 | };
|
23 |
|
24 | return program;
|
25 | }
|
26 |
|
27 | function generateEmptyPort(port, err){
|
28 | return {
|
29 | name: null,
|
30 | path: port,
|
31 | type: 'bs2',
|
32 | match: false,
|
33 | program: null,
|
34 | displayError: err,
|
35 | board: {
|
36 | path: port
|
37 | }
|
38 | };
|
39 | }
|
40 |
|
41 | function listPorts(options){
|
42 | var reject = _.get(options, 'reject') || [];
|
43 | var predicate = reject;
|
44 | if(Array.isArray(reject)){
|
45 | predicate = function(port){
|
46 | return _.some(reject, function(entry){
|
47 | if(typeof entry === 'string'){
|
48 | return port === entry;
|
49 | }
|
50 | return entry.test(port);
|
51 | });
|
52 | };
|
53 | }
|
54 | return Bs2SerialProtocol.listPorts()
|
55 | .then(function(portList){
|
56 | return _.reject(portList, predicate);
|
57 | });
|
58 | }
|
59 |
|
60 | function Board(options){
|
61 | if(!options){
|
62 | throw new Error('Options error: no options');
|
63 | }
|
64 |
|
65 | if(!options.path){
|
66 | throw new Error('Options error: no path');
|
67 | }
|
68 |
|
69 | EventEmitter.call(this);
|
70 |
|
71 | this._revision = options.revision || 'bs2';
|
72 | this._protocol = new Bs2SerialProtocol(options);
|
73 | this._programmer = new Bs2Programmer({
|
74 | protocol: this._protocol,
|
75 | revision: this._revision
|
76 | });
|
77 |
|
78 | reemit(this._protocol, this, ['terminal', 'transmit', 'open', 'close']);
|
79 |
|
80 | var self = this;
|
81 | this._programmer.on('bootloadProgress', function(progress){
|
82 | self.emit('progress', progress);
|
83 | });
|
84 | }
|
85 |
|
86 | util.inherits(Board, EventEmitter);
|
87 |
|
88 | Board.search = function(options, cb){
|
89 | var revisions = _.keys(bs2.revisions);
|
90 | var result = listPorts(options)
|
91 | .then(function(portList){
|
92 | return bluebird.reduce(portList, function(boardList, path){
|
93 |
|
94 | var protocol = new Bs2SerialProtocol({ path: path });
|
95 | return bluebird.reduce(revisions, function(current, rev){
|
96 | if(current.revision){
|
97 | return current;
|
98 | }
|
99 |
|
100 | var boardOpts = {
|
101 | protocol: protocol,
|
102 | revision: rev
|
103 | };
|
104 |
|
105 | return bs2.identify(boardOpts)
|
106 | .then(function(result){
|
107 | return _.assign({ path: path, revision: rev }, result);
|
108 | })
|
109 | .otherwise(function(err){
|
110 | if(err.message === 'Serialport not open.'){
|
111 |
|
112 |
|
113 | err = new Error('Port could not be opened.');
|
114 | }
|
115 | return {
|
116 | displayError: err.message
|
117 | };
|
118 | });
|
119 |
|
120 | }, {})
|
121 | .then(function(board){
|
122 | if(board.revision != null){
|
123 | var details = {
|
124 | name: board.name,
|
125 | path: board.path,
|
126 | version: board.version,
|
127 | board: _.cloneDeep(board),
|
128 | match: false,
|
129 | program: null
|
130 | };
|
131 | if(options.source){
|
132 | details.program = internalCompile(options.source);
|
133 | details.match = details.program.name === board.name;
|
134 | }
|
135 | boardList.push(details);
|
136 | }else{
|
137 | boardList.push(generateEmptyPort(path, board.displayError));
|
138 | }
|
139 | return boardList;
|
140 | });
|
141 | }, [])
|
142 | .then(function(boards){
|
143 | return _.sortBy(boards, 'name');
|
144 | });
|
145 | });
|
146 |
|
147 | return nodefn.bindCallback(result, cb);
|
148 | };
|
149 |
|
150 | Board.prototype.bootload = function(program, cb){
|
151 | if(typeof program === 'string'){
|
152 | program = internalCompile(program);
|
153 | }
|
154 | if(program && program.error){
|
155 | return bluebird.reject(program.error).nodeify(cb);
|
156 | }
|
157 | if(!program || !program.data){
|
158 | return bluebird.reject(new Error('Options error: no program data')).nodeify(cb);
|
159 | }
|
160 |
|
161 | return this._programmer.bootload(program.data, cb);
|
162 | };
|
163 |
|
164 | Board.compile = function(source){
|
165 | return internalCompile(source);
|
166 | };
|
167 |
|
168 | Board.prototype.isOpen = function(){
|
169 | return this._protocol.isOpen();
|
170 | };
|
171 |
|
172 | Board.prototype.write = function(data, cb){
|
173 | if(typeof data === 'number'){
|
174 | data = new Buffer([data]);
|
175 | }else if(Array.isArray(data) || typeof data === 'string'){
|
176 | data = new Buffer(data);
|
177 | }
|
178 | return this._protocol.write(data, cb);
|
179 | };
|
180 |
|
181 | Board.prototype.open = function(cb){
|
182 | return this._protocol.open(cb);
|
183 | };
|
184 |
|
185 | Board.prototype.close = function(cb){
|
186 | return this._protocol.close(cb);
|
187 | };
|
188 |
|
189 | Board.getRevisions = function(){
|
190 | return bs2.revisions;
|
191 | };
|
192 |
|
193 | module.exports = Board;
|