Switch to use @commaai/pandajs for webusb communications

Squashed commit of the following:

commit c5d1f64a7072d1eeee6cd52b02aed69515275e22
Author: Chris Vickery <chrisinajar@gmail.com>
Date:   Sun Mar 18 17:45:14 2018 -0700

    Change to scoped pandajs

commit 4ebc842bfc957b32eb694b74517bf3aedf5194c7
Author: Chris Vickery <chrisinajar@gmail.com>
Date:   Sun Mar 18 17:37:09 2018 -0700

    Use pandajs lib to communicate with panda modules
main
Chris Vickery 2018-03-18 17:46:30 -07:00
parent f028c332f3
commit e8c8b901c1
9 changed files with 118 additions and 165 deletions

12
.editorconfig 100644
View File

@ -0,0 +1,12 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

View File

@ -4,6 +4,7 @@
"private": true,
"homepage": "https://community.comma.ai/cabana",
"dependencies": {
"@commaai/pandajs": "^0.1.0",
"aphrodite": "^1.2.1",
"babel-preset-stage-0": "^6.24.1",
"base64-inline-loader": "^1.1.0",
@ -46,7 +47,8 @@
"socket.io-client": "^2.0.3",
"streamsaver": "^1.0.1",
"vega": "3.0.0",
"vega-tooltip": "^0.4.0"
"vega-tooltip": "^0.4.0",
"xtend": "^4.0.1"
},
"devDependencies": {
"connect-history-api-fallback": "1.3.0",

View File

@ -1,11 +1,12 @@
import React, { Component } from "react";
import Moment from "moment";
import PropTypes from "prop-types";
import cx from "classnames";
import { createWriteStream } from "streamsaver";
import Panda from "@commaai/pandajs";
import { USE_UNLOGGER, PART_SEGMENT_LENGTH, STREAMING_WINDOW } from "./config";
import * as GithubAuth from "./api/github-auth";
import cx from "classnames";
import { createWriteStream } from "streamsaver";
import auth from "./api/comma-auth";
import DBC from "./models/can/dbc";
@ -25,7 +26,6 @@ import {
} from "./api/localstorage";
import OpenDbc from "./api/OpenDbc";
import UnloggerClient from "./api/unlogger";
import PandaReader from "./api/panda-reader";
import * as ObjectUtils from "./utils/object";
import { hash } from "./utils/string";
@ -86,8 +86,6 @@ export default class CanExplorer extends Component {
this.unloggerClient = new UnloggerClient();
}
this.pandaReader = new PandaReader();
this.showOnboarding = this.showOnboarding.bind(this);
this.hideOnboarding = this.hideOnboarding.bind(this);
this.showLoadDbc = this.showLoadDbc.bind(this);
@ -118,6 +116,9 @@ export default class CanExplorer extends Component {
this.lastMessageEntriesById = this.lastMessageEntriesById.bind(this);
this.githubSignOut = this.githubSignOut.bind(this);
this.downloadLogAsCSV = this.downloadLogAsCSV.bind(this);
this.pandaReader = new Panda();
this.pandaReader.onMessage(this.processStreamedCanMessages);
}
componentWillMount() {
@ -754,34 +755,33 @@ export default class CanExplorer extends Component {
this._onStreamedCanMessagesProcessed(e.data);
}
handlePandaConnect(e) {
async handlePandaConnect(e) {
this.setState({ attemptingPandaConnection: true, live: true });
const persistedDbc = fetchPersistedDbc("live");
if (persistedDbc) {
const { dbc, dbcText } = persistedDbc;
let { dbc, dbcText } = persistedDbc;
this.setState({ dbc, dbcText });
}
this.canStreamerWorker = new CanStreamerWorker();
this.canStreamerWorker.onmessage = this.onStreamedCanMessagesProcessed;
this.pandaReader.setOnMessagesReceivedCallback(
this.processStreamedCanMessages
// if any errors go off during connection, mark as not trying to connect anymore...
let unlisten = this.pandaReader.onError(err =>
this.setState({ attemptingPandaConnection: false })
);
this.pandaReader
.connect()
.then(() => {
this.pandaReader.readLoop();
this.setState({ attemptingPandaConnection: false });
this.setState({ showOnboarding: false });
this.setState({ showLoadDbc: true });
})
.catch(err => {
console.log(err);
this.setState({ attemptingPandaConnection: false });
try {
await this.pandaReader.start();
this.setState({
showOnboarding: false,
showLoadDbc: true
});
} catch (e) {}
this.setState({ attemptingPandaConnection: false });
unlisten();
}
githubSignOut(e) {
githubSignOut(e, dataArray) {
unpersistGithubAuthToken();
this.setState({ isGithubAuthenticated: false });

View File

@ -1,22 +0,0 @@
import Panda from "../../api/panda";
function arrayBufferFromHex(hex) {
const buffer = Buffer.from(hex, "hex");
const arrayBuffer = new ArrayBuffer(buffer.length);
const view = new Uint8Array(arrayBuffer);
for (let i = 0; i < buffer.length; i++) {
view[i] = buffer[i];
}
return arrayBuffer;
}
test("parseCanBuffer correctly parses a message", () => {
const panda = new Panda();
// 16 byte buffer
const arrayBuffer = arrayBufferFromHex("abababababababababababababababab");
const messages = panda.parseCanBuffer(arrayBuffer);
expect(messages.length).toEqual(1);
expect(messages[0]).toEqual([1373, 43947, "abababababababab", 10]);
});

View File

@ -1,10 +1,16 @@
import Panda from "./panda";
import CloudLog from "../logging/CloudLog";
import Panda from "pandajs";
///@TODO move this file into pandajs and fix up the API
// it should handle read loops, event management, pausing, unpausing, etc...
export default class PandaReader {
static ERROR_NO_DEVICE_SELECTED = 8;
constructor() {
this.panda = new Panda();
this.panda.onError(CloudLog.error.bind(CloudLog));
this.isReading = false;
this.onMessagesReceived = () => {};
this.callbackQueue = [];

View File

@ -1,110 +0,0 @@
import CloudLog from "../logging/CloudLog";
const PANDA_VENDOR_ID = 0xbbaa;
//const PANDA_PRODUCT_ID = 0xddcc;
const BUFFER_SIZE = 0x10 * 256;
export default class Panda {
constructor() {
this.device = null;
}
connect() {
// Must be called via a mouse click handler, per Chrome restrictions.
return navigator.usb
.requestDevice({ filters: [{ vendorId: PANDA_VENDOR_ID }] })
.then(device => {
this.device = device;
return device.open();
})
.then(() => this.device.selectConfiguration(1))
.then(() => this.device.claimInterface(0));
}
async health() {
const controlParams = {
requestType: "vendor",
recipient: "device",
request: 0xd2,
value: 0,
index: 0
};
try {
return await this.device.controlTransferIn(controlParams, 13);
} catch (err) {
CloudLog.error({ event: "Panda.health failed", error: err });
}
}
parseCanBuffer(buffer) {
const messages = [];
for (let i = 0; i < buffer.byteLength; i += 0x10) {
const dat = buffer.slice(i, i + 0x10);
const datView = Buffer.from(dat);
const f1 = datView.readInt32LE(0),
f2 = datView.readInt32LE(4);
const address = f1 >>> 21;
const busTime = f2 >>> 16;
const data = new Buffer(dat.slice(8, 8 + (f2 & 0xf)));
const source = (f2 >> 4) & 0xf & 0xff;
messages.push([
address,
busTime,
data.toString("hex").padEnd(16, "0"),
source
]);
}
return messages;
}
async mockCanRecv() {
const promise = new Promise(resolve =>
setTimeout(
() =>
resolve({
time: performance.now() / 1000,
canMessages: [[0, Math.random() * 65000, "".padEnd(16, "0"), 0]]
}),
100
)
);
return await promise;
}
async canRecv() {
let result = null,
receiptTime = null,
attempts = 0;
while (result === null) {
try {
result = await this.device.transferIn(1, BUFFER_SIZE);
receiptTime = performance.now() / 1000;
} catch (err) {
console.warn("can_recv failed, retrying");
attempts = Math.min(++attempts, 10);
await wait(attempts * 100);
}
}
await wait(0);
return {
time: receiptTime,
canMessages: this.parseCanBuffer(result.data.buffer)
};
}
}
async function wait(ms) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, ms);
});
}

View File

@ -6,17 +6,18 @@ function findMaxByteStateChangeCount(messages) {
}
function addCanMessage(
[address, busTime, data, source],
canMessage,
dbc,
canStartTime,
messages,
prevMsgEntries,
byteStateChangeCountsByMessage
) {
var id = source + ":" + address.toString(16);
var { address, busTime, data, bus } = canMessage;
var id = bus + ":" + address.toString(16);
if (messages[id] === undefined)
messages[id] = createMessageSpec(dbc, address, id, source);
messages[id] = createMessageSpec(dbc, address, id, bus);
const prevMsgEntry =
messages[id].entries.length > 0

View File

@ -2,6 +2,7 @@
/* eslint-disable no-restricted-globals */
import DBC from "../models/can/dbc";
import DbcUtils from "../utils/dbc";
import extend from "xtend";
function processStreamedCanMessages(
newCanMessages,
@ -30,7 +31,8 @@ function processStreamedCanMessages(
let busTimeSum = 0;
for (let i = 0; i < canMessages.length; i++) {
let [address, busTime, data, source] = canMessages[i];
let { address, busTime, data, bus } = canMessages[i];
let prevBusTime;
if (i === 0) {
if (lastBusTime === null) {
@ -39,7 +41,7 @@ function processStreamedCanMessages(
prevBusTime = lastBusTime;
}
} else {
prevBusTime = canMessages[i - 1][1];
prevBusTime = canMessages[i - 1].busTime;
}
if (busTime >= prevBusTime) {
@ -47,11 +49,11 @@ function processStreamedCanMessages(
} else {
busTimeSum += 0x10000 - prevBusTime + busTime;
}
const message = [...canMessages[i]];
message[1] = time + busTimeSum / 500000.0;
const message = extend(canMessages[i]);
message.busTime = time + busTimeSum / 500000.0;
if (firstCanTime === 0) {
firstCanTime = message[1];
firstCanTime = message.busTime;
}
const msgEntry = DbcUtils.addCanMessage(
@ -67,7 +69,7 @@ function processStreamedCanMessages(
}
}
lastBusTime = canMessages[canMessages.length - 1][1];
lastBusTime = canMessages[canMessages.length - 1].busTime;
const newMaxByteStateChangeCount = DbcUtils.findMaxByteStateChangeCount(
messages
);

View File

@ -17,6 +17,18 @@
lodash "^4.2.0"
to-fast-properties "^2.0.0"
"@commaai/pandajs@^0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@commaai/pandajs/-/pandajs-0.1.0.tgz#55e7fcb880f22f2f2d814dd2437cddb88f4d69ce"
dependencies:
ap "^0.2.0"
can-message "^0.1.0"
is-browser "^2.0.1"
performance-now "^2.1.0"
raf "^3.4.0"
thyming "^0.1.1"
weakmap-event "^2.0.7"
"@types/node@*":
version "8.5.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.5.1.tgz#4ec3020bcdfe2abffeef9ba3fbf26fca097514b5"
@ -157,6 +169,10 @@ anymatch@^1.3.0:
micromatch "^2.1.5"
normalize-path "^2.0.0"
ap@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/ap/-/ap-0.2.0.tgz#ae0942600b29912f0d2b14ec60c45e8f330b6110"
aphrodite@^1.2.1:
version "1.2.5"
resolved "https://registry.yarnpkg.com/aphrodite/-/aphrodite-1.2.5.tgz#8358c36c80bb03aee9b97165aaa70186225b4983"
@ -208,6 +224,10 @@ arr-flatten@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
array-differ@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031"
array-equal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
@ -281,6 +301,10 @@ asn1@~0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86"
assert-function@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assert-function/-/assert-function-1.0.0.tgz#aeb2ad0fe00888b41254a8e3934a562968d77905"
assert-plus@1.0.0, assert-plus@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
@ -1462,6 +1486,10 @@ camelcase@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
can-message@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/can-message/-/can-message-0.1.0.tgz#288862d67a0dbd85dbc56da0a44f6e2bf1dfb030"
caniuse-api@^1.5.2:
version "1.6.1"
resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c"
@ -3395,6 +3423,10 @@ getpass@^0.1.1:
dependencies:
assert-plus "^1.0.0"
geval@~2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/geval/-/geval-2.2.0.tgz#5622b10a28028284198afe351f6b417042c33dda"
github-api@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/github-api/-/github-api-3.0.0.tgz#2832f98d0d3a83f1485e2db32c9259e4b9c40a75"
@ -3978,6 +4010,10 @@ is-binary-path@^1.0.0:
dependencies:
binary-extensions "^1.0.0"
is-browser@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/is-browser/-/is-browser-2.0.1.tgz#8bf0baf799a9c62fd9de5bcee4cf3397c3e7529a"
is-buffer@^1.1.5:
version "1.1.6"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
@ -4648,6 +4684,13 @@ jsx-ast-utils@^2.0.0:
dependencies:
array-includes "^3.0.3"
key-difference@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/key-difference/-/key-difference-1.0.0.tgz#7ede6f69bfff08010b5612885303b688a30008cc"
dependencies:
array-differ "~1.0.0"
xtend "~4.0.0"
killable@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.0.tgz#da8b84bd47de5395878f95d64d02f2449fe05e6b"
@ -7437,6 +7480,12 @@ thunky@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/thunky/-/thunky-0.1.0.tgz#bf30146824e2b6e67b0f2d7a4ac8beb26908684e"
thyming@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/thyming/-/thyming-0.1.1.tgz#1c30f4fe168e6aae59b4c4cc23385accf4199205"
dependencies:
assert-function "^1.0.0"
time-stamp@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-2.0.0.tgz#95c6a44530e15ba8d6f4a3ecb8c3a3fac46da357"
@ -8038,6 +8087,19 @@ wbuf@^1.1.0, wbuf@^1.7.2:
dependencies:
minimalistic-assert "^1.0.0"
weakmap-event@^2.0.7:
version "2.0.7"
resolved "https://registry.yarnpkg.com/weakmap-event/-/weakmap-event-2.0.7.tgz#3f65318aba89e022981e8249bfae339a4ad6945a"
dependencies:
geval "~2.2.0"
key-difference "~1.0.0"
weakmap-shim "~1.1.0"
xtend "~4.0.0"
weakmap-shim@~1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/weakmap-shim/-/weakmap-shim-1.1.1.tgz#d65afd784109b2166e00ff571c33150ec2a40b49"
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
@ -8287,7 +8349,7 @@ xmlhttprequest@1:
version "1.8.0"
resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc"
xtend@^4.0.0, xtend@^4.0.1:
xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"