UNPKG

22.5 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6
7var _slicedToArray2 = require('babel-runtime/helpers/slicedToArray');
8
9var _slicedToArray3 = _interopRequireDefault(_slicedToArray2);
10
11var _regenerator = require('babel-runtime/regenerator');
12
13var _regenerator2 = _interopRequireDefault(_regenerator);
14
15var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
16
17var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
18
19var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
20
21var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
22
23var _createClass2 = require('babel-runtime/helpers/createClass');
24
25var _createClass3 = _interopRequireDefault(_createClass2);
26
27var _canMessage = require('can-message');
28
29var _weakmapEvent = require('weakmap-event');
30
31var _weakmapEvent2 = _interopRequireDefault(_weakmapEvent);
32
33var _ap = require('ap');
34
35var _performanceNow = require('performance-now');
36
37var _performanceNow2 = _interopRequireDefault(_performanceNow);
38
39var _raf = require('raf');
40
41var _raf2 = _interopRequireDefault(_raf);
42
43var _thyming = require('thyming');
44
45var _delay = require('./delay');
46
47var _delay2 = _interopRequireDefault(_delay);
48
49function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
50
51// how many messages to batch at maximum when reading as fast as we can
52var MAX_MESSAGE_QUEUE = 5000;
53
54var MessageEvent = (0, _weakmapEvent2.default)();
55var ErrorEvent = (0, _weakmapEvent2.default)();
56var ConnectEvent = (0, _weakmapEvent2.default)();
57var DisconnectEvent = (0, _weakmapEvent2.default)();
58
59var Panda = function () {
60 function Panda(options) {
61 (0, _classCallCheck3.default)(this, Panda);
62
63 // setup event handlers
64 this.onMessage = (0, _ap.partial)(MessageEvent.listen, this);
65 this.onError = (0, _ap.partial)(ErrorEvent.listen, this);
66 this.onConnect = (0, _ap.partial)(ConnectEvent.listen, this);
67 this.onDisconnect = (0, _ap.partial)(DisconnectEvent.listen, this);
68
69 // initialize device object
70 this.device = options.device;
71 this.device.onError((0, _ap.partial)(ErrorEvent.broadcast, this));
72 this.device.onConnect(this.connectHandler.bind(this));
73 this.device.onDisconnect(this.disconnectHandler.bind(this));
74
75 // member variables
76 this.paused = true;
77 this.messageQueue = [];
78
79 // function binding
80 this.readLoop = this.readLoop.bind(this);
81 this.flushMessageQueue = this.flushMessageQueue.bind(this);
82 }
83
84 // state getters
85
86
87 (0, _createClass3.default)(Panda, [{
88 key: 'isConnected',
89 value: function isConnected() {
90 return !!this.connected;
91 }
92 }, {
93 key: 'isPaused',
94 value: function isPaused() {
95 return !!this.paused;
96 }
97
98 // methods
99
100 }, {
101 key: 'connect',
102 value: function () {
103 var _ref = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee() {
104 var serialNumber;
105 return _regenerator2.default.wrap(function _callee$(_context) {
106 while (1) {
107 switch (_context.prev = _context.next) {
108 case 0:
109 if (!this.isConnected()) {
110 _context.next = 2;
111 break;
112 }
113
114 return _context.abrupt('return', this.connected);
115
116 case 2:
117 _context.next = 4;
118 return this.device.connect();
119
120 case 4:
121 _context.next = 6;
122 return this.getSerialNumber();
123
124 case 6:
125 serialNumber = _context.sent;
126
127 this.connectHandler(serialNumber);
128
129 return _context.abrupt('return', serialNumber);
130
131 case 9:
132 case 'end':
133 return _context.stop();
134 }
135 }
136 }, _callee, this);
137 }));
138
139 function connect() {
140 return _ref.apply(this, arguments);
141 }
142
143 return connect;
144 }()
145 }, {
146 key: 'disconnect',
147 value: function () {
148 var _ref2 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee2() {
149 return _regenerator2.default.wrap(function _callee2$(_context2) {
150 while (1) {
151 switch (_context2.prev = _context2.next) {
152 case 0:
153 if (this.isConnected()) {
154 _context2.next = 2;
155 break;
156 }
157
158 return _context2.abrupt('return', false);
159
160 case 2:
161 return _context2.abrupt('return', this.device.disconnect());
162
163 case 3:
164 case 'end':
165 return _context2.stop();
166 }
167 }
168 }, _callee2, this);
169 }));
170
171 function disconnect() {
172 return _ref2.apply(this, arguments);
173 }
174
175 return disconnect;
176 }()
177 }, {
178 key: 'start',
179 value: function () {
180 var _ref3 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee3() {
181 return _regenerator2.default.wrap(function _callee3$(_context3) {
182 while (1) {
183 switch (_context3.prev = _context3.next) {
184 case 0:
185 _context3.next = 2;
186 return this.connect();
187
188 case 2:
189 return _context3.abrupt('return', this.unpause());
190
191 case 3:
192 case 'end':
193 return _context3.stop();
194 }
195 }
196 }, _callee3, this);
197 }));
198
199 function start() {
200 return _ref3.apply(this, arguments);
201 }
202
203 return start;
204 }()
205 }, {
206 key: 'pause',
207 value: function () {
208 var _ref4 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee4() {
209 var wasPaused;
210 return _regenerator2.default.wrap(function _callee4$(_context4) {
211 while (1) {
212 switch (_context4.prev = _context4.next) {
213 case 0:
214 wasPaused = this.isPaused();
215
216 this.paused = true;
217
218 return _context4.abrupt('return', !wasPaused);
219
220 case 3:
221 case 'end':
222 return _context4.stop();
223 }
224 }
225 }, _callee4, this);
226 }));
227
228 function pause() {
229 return _ref4.apply(this, arguments);
230 }
231
232 return pause;
233 }()
234 }, {
235 key: 'resume',
236 value: function () {
237 var _ref5 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee5() {
238 return _regenerator2.default.wrap(function _callee5$(_context5) {
239 while (1) {
240 switch (_context5.prev = _context5.next) {
241 case 0:
242 return _context5.abrupt('return', this.unpause());
243
244 case 1:
245 case 'end':
246 return _context5.stop();
247 }
248 }
249 }, _callee5, this);
250 }));
251
252 function resume() {
253 return _ref5.apply(this, arguments);
254 }
255
256 return resume;
257 }()
258 }, {
259 key: 'unpause',
260 value: function () {
261 var _ref6 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee6() {
262 var wasPaused;
263 return _regenerator2.default.wrap(function _callee6$(_context6) {
264 while (1) {
265 switch (_context6.prev = _context6.next) {
266 case 0:
267 wasPaused = this.isPaused();
268
269 if (wasPaused) {
270 _context6.next = 3;
271 break;
272 }
273
274 return _context6.abrupt('return', false);
275
276 case 3:
277
278 this.paused = false;
279 this.startReading();
280
281 return _context6.abrupt('return', wasPaused);
282
283 case 6:
284 case 'end':
285 return _context6.stop();
286 }
287 }
288 }, _callee6, this);
289 }));
290
291 function unpause() {
292 return _ref6.apply(this, arguments);
293 }
294
295 return unpause;
296 }()
297
298 // vendor API methods
299
300 }, {
301 key: 'getHealth',
302 value: function () {
303 var _ref7 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee7() {
304 var buf, voltage, current, isStarted, controlsAreAllowed, isGasInterceptorDetector, isStartSignalDetected, isStartedAlt;
305 return _regenerator2.default.wrap(function _callee7$(_context7) {
306 while (1) {
307 switch (_context7.prev = _context7.next) {
308 case 0:
309 _context7.next = 2;
310 return this.vendorRequest('health', {
311 request: 0xd2,
312 value: 0,
313 index: 0
314 }, 13);
315
316 case 2:
317 buf = _context7.sent;
318 voltage = buf.readUInt32LE(0) / 1000;
319 current = buf.readUInt32LE(4) / 1000;
320 isStarted = buf.readInt8(8) === 1;
321 controlsAreAllowed = buf.readInt8(9) === 1;
322 isGasInterceptorDetector = buf.readInt8(10) === 1;
323 isStartSignalDetected = buf.readInt8(11) === 1;
324 isStartedAlt = buf.readInt8(12) === 1;
325 return _context7.abrupt('return', {
326 voltage: voltage,
327 current: current,
328 isStarted: isStarted,
329 controlsAreAllowed: controlsAreAllowed,
330 isGasInterceptorDetector: isGasInterceptorDetector,
331 isStartSignalDetected: isStartSignalDetected,
332 isStartedAlt: isStartedAlt
333 });
334
335 case 11:
336 case 'end':
337 return _context7.stop();
338 }
339 }
340 }, _callee7, this);
341 }));
342
343 function getHealth() {
344 return _ref7.apply(this, arguments);
345 }
346
347 return getHealth;
348 }()
349 }, {
350 key: 'getDeviceMetadata',
351 value: function () {
352 var _ref8 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee8() {
353 var buf, serial, secret, hashSig;
354 return _regenerator2.default.wrap(function _callee8$(_context8) {
355 while (1) {
356 switch (_context8.prev = _context8.next) {
357 case 0:
358 _context8.next = 2;
359 return this.vendorRequest('getDeviceMetadata', {
360 request: 0xd0,
361 value: 0,
362 index: 0
363 }, 0x20);
364
365 case 2:
366 buf = _context8.sent;
367 serial = buf.slice(0, 0x10); // serial is the wifi style serial
368
369 secret = buf.slice(0x10, 0x10 + 10);
370 hashSig = buf.slice(0x1c);
371 return _context8.abrupt('return', [serial.toString(), secret.toString()]);
372
373 case 7:
374 case 'end':
375 return _context8.stop();
376 }
377 }
378 }, _callee8, this);
379 }));
380
381 function getDeviceMetadata() {
382 return _ref8.apply(this, arguments);
383 }
384
385 return getDeviceMetadata;
386 }()
387 }, {
388 key: 'getSerialNumber',
389 value: function () {
390 var _ref9 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee9() {
391 var _ref10, _ref11, serial, secret;
392
393 return _regenerator2.default.wrap(function _callee9$(_context9) {
394 while (1) {
395 switch (_context9.prev = _context9.next) {
396 case 0:
397 _context9.next = 2;
398 return this.getDeviceMetadata();
399
400 case 2:
401 _ref10 = _context9.sent;
402 _ref11 = (0, _slicedToArray3.default)(_ref10, 2);
403 serial = _ref11[0];
404 secret = _ref11[1];
405 return _context9.abrupt('return', serial);
406
407 case 7:
408 case 'end':
409 return _context9.stop();
410 }
411 }
412 }, _callee9, this);
413 }));
414
415 function getSerialNumber() {
416 return _ref9.apply(this, arguments);
417 }
418
419 return getSerialNumber;
420 }()
421 }, {
422 key: 'getSecret',
423 value: function () {
424 var _ref12 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee10() {
425 var _ref13, _ref14, serial, secret;
426
427 return _regenerator2.default.wrap(function _callee10$(_context10) {
428 while (1) {
429 switch (_context10.prev = _context10.next) {
430 case 0:
431 _context10.next = 2;
432 return this.getDeviceMetadata();
433
434 case 2:
435 _ref13 = _context10.sent;
436 _ref14 = (0, _slicedToArray3.default)(_ref13, 2);
437 serial = _ref14[0];
438 secret = _ref14[1];
439 return _context10.abrupt('return', secret);
440
441 case 7:
442 case 'end':
443 return _context10.stop();
444 }
445 }
446 }, _callee10, this);
447 }));
448
449 function getSecret() {
450 return _ref12.apply(this, arguments);
451 }
452
453 return getSecret;
454 }()
455 }, {
456 key: 'getVersion',
457 value: function () {
458 var _ref15 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee11() {
459 var buf;
460 return _regenerator2.default.wrap(function _callee11$(_context11) {
461 while (1) {
462 switch (_context11.prev = _context11.next) {
463 case 0:
464 _context11.next = 2;
465 return this.vendorRequest('getVersion', {
466 request: 0xd6,
467 value: 0,
468 index: 0
469 }, 0x40);
470
471 case 2:
472 buf = _context11.sent;
473 return _context11.abrupt('return', buf.toString());
474
475 case 4:
476 case 'end':
477 return _context11.stop();
478 }
479 }
480 }, _callee11, this);
481 }));
482
483 function getVersion() {
484 return _ref15.apply(this, arguments);
485 }
486
487 return getVersion;
488 }()
489 }, {
490 key: 'isGrey',
491 value: function () {
492 var _ref16 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee12() {
493 var buf;
494 return _regenerator2.default.wrap(function _callee12$(_context12) {
495 while (1) {
496 switch (_context12.prev = _context12.next) {
497 case 0:
498 _context12.next = 2;
499 return this.vendorRequest('isGrey', {
500 request: 0xc1,
501 value: 0,
502 index: 0
503 }, 0x40);
504
505 case 2:
506 buf = _context12.sent;
507 return _context12.abrupt('return', !!(buf.length && buf[0] === 1));
508
509 case 4:
510 case 'end':
511 return _context12.stop();
512 }
513 }
514 }, _callee12, this);
515 }));
516
517 function isGrey() {
518 return _ref16.apply(this, arguments);
519 }
520
521 return isGrey;
522 }()
523 }, {
524 key: 'setSafetyMode',
525 value: function () {
526 var _ref17 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee13(mode) {
527 var buf;
528 return _regenerator2.default.wrap(function _callee13$(_context13) {
529 while (1) {
530 switch (_context13.prev = _context13.next) {
531 case 0:
532 _context13.next = 2;
533 return this.vendorWrite('setSafetyMode', {
534 request: 0xdc,
535 value: 0,
536 index: 0
537 });
538
539 case 2:
540 buf = _context13.sent;
541 return _context13.abrupt('return', buf.toString());
542
543 case 4:
544 case 'end':
545 return _context13.stop();
546 }
547 }
548 }, _callee13, this);
549 }));
550
551 function setSafetyMode(_x) {
552 return _ref17.apply(this, arguments);
553 }
554
555 return setSafetyMode;
556 }()
557
558 // i/o wrappers
559
560 }, {
561 key: 'vendorRequest',
562 value: function () {
563 var _ref18 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee14(event, controlParams, length) {
564 var result;
565 return _regenerator2.default.wrap(function _callee14$(_context14) {
566 while (1) {
567 switch (_context14.prev = _context14.next) {
568 case 0:
569 _context14.prev = 0;
570 _context14.next = 3;
571 return this.device.vendorRequest(controlParams, length);
572
573 case 3:
574 result = _context14.sent;
575 return _context14.abrupt('return', result.data);
576
577 case 7:
578 _context14.prev = 7;
579 _context14.t0 = _context14['catch'](0);
580
581 ErrorEvent.broadcast(this, { event: 'Panda.' + event + ' failed', error: _context14.t0 });
582 throw _context14.t0;
583
584 case 11:
585 case 'end':
586 return _context14.stop();
587 }
588 }
589 }, _callee14, this, [[0, 7]]);
590 }));
591
592 function vendorRequest(_x2, _x3, _x4) {
593 return _ref18.apply(this, arguments);
594 }
595
596 return vendorRequest;
597 }()
598 }, {
599 key: 'vendorWrite',
600 value: function () {
601 var _ref19 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee15(event, controlParams, message) {
602 var result;
603 return _regenerator2.default.wrap(function _callee15$(_context15) {
604 while (1) {
605 switch (_context15.prev = _context15.next) {
606 case 0:
607 if (!message || !message.length) {
608 message = Buffer.from([]);
609 }
610 _context15.prev = 1;
611 _context15.next = 4;
612 return this.device.vendorWrite(controlParams, message);
613
614 case 4:
615 result = _context15.sent;
616 return _context15.abrupt('return', result.data);
617
618 case 8:
619 _context15.prev = 8;
620 _context15.t0 = _context15['catch'](1);
621
622 ErrorEvent.broadcast(this, { event: 'Panda.' + event + ' failed', error: _context15.t0 });
623 throw _context15.t0;
624
625 case 12:
626 case 'end':
627 return _context15.stop();
628 }
629 }
630 }, _callee15, this, [[1, 8]]);
631 }));
632
633 function vendorWrite(_x5, _x6, _x7) {
634 return _ref19.apply(this, arguments);
635 }
636
637 return vendorWrite;
638 }()
639
640 // event handlers
641
642 }, {
643 key: 'connectHandler',
644 value: function connectHandler(usbId) {
645 this.connected = usbId;
646 ConnectEvent.broadcast(this, usbId);
647 }
648 }, {
649 key: 'disconnectHandler',
650 value: function disconnectHandler() {
651 var previousConnection = this.connected;
652 this.connected = false;
653 this.paused = true;
654 DisconnectEvent.broadcast(this, previousConnection);
655 }
656
657 // message queueing and flushing
658
659 }, {
660 key: 'needsFlushMessageQueue',
661 value: function needsFlushMessageQueue() {
662 var _this = this;
663
664 this.needsFlush = true;
665 if (this.flushEvent) {
666 return this.flushEvent;
667 }
668
669 var unlisten = (0, _raf2.default)(this.flushMessageQueue);
670
671 this.flushEvent = function () {
672 _raf2.default.cancel(unlisten);
673 _this.flushEvent = false;
674 };
675
676 return this.flushEvent;
677 }
678 }, {
679 key: 'flushMessageQueue',
680 value: function flushMessageQueue() {
681 this.flushEvent();
682
683 if (this.needsFlush && this.messageQueue.length) {
684 var messageQueue = this.messageQueue;
685 this.messageQueue = [];
686 this.needsFlush = false;
687 MessageEvent.broadcast(this, messageQueue);
688 }
689 }
690 // internal reading loop
691
692 }, {
693 key: 'startReading',
694 value: function startReading() {
695 if (this.isReading) {
696 return true;
697 }
698 if (this.isPaused()) {
699 return false;
700 }
701
702 // start loop!
703 this.isReading = true;
704 this.readLoop();
705 }
706 }, {
707 key: 'readLoop',
708 value: function () {
709 var _ref20 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee16() {
710 var i, data, receiptTime, canMessages;
711 return _regenerator2.default.wrap(function _callee16$(_context16) {
712 while (1) {
713 switch (_context16.prev = _context16.next) {
714 case 0:
715 if (!this.isPaused()) {
716 _context16.next = 3;
717 break;
718 }
719
720 this.isReading = false;
721 return _context16.abrupt('return', false);
722
723 case 3:
724 this.isReading = true;
725
726 i = 0;
727
728 case 5:
729 if (!(i < MAX_MESSAGE_QUEUE)) {
730 _context16.next = 20;
731 break;
732 }
733
734 _context16.next = 8;
735 return this.device.nextMessage();
736
737 case 8:
738 data = _context16.sent;
739 receiptTime = (0, _performanceNow2.default)() / 1000;
740 canMessages = (0, _canMessage.unpackCAN)(data);
741
742 if (canMessages.length) {
743 _context16.next = 15;
744 break;
745 }
746
747 _context16.next = 14;
748 return (0, _delay2.default)(1);
749
750 case 14:
751 return _context16.abrupt('continue', 17);
752
753 case 15:
754 this.messageQueue.push({
755 time: receiptTime,
756 canMessages: canMessages
757 });
758 this.needsFlushMessageQueue();
759
760 case 17:
761 ++i;
762 _context16.next = 5;
763 break;
764
765 case 20:
766 this.needsFlushMessageQueue();
767
768 // repeat!
769 (0, _thyming.timeout)(this.readLoop);
770
771 case 22:
772 case 'end':
773 return _context16.stop();
774 }
775 }
776 }, _callee16, this);
777 }));
778
779 function readLoop() {
780 return _ref20.apply(this, arguments);
781 }
782
783 return readLoop;
784 }()
785 }]);
786 return Panda;
787}();
788
789exports.default = Panda;
\No newline at end of file