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 modulesmain
parent
f028c332f3
commit
e8c8b901c1
|
@ -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
|
|
@ -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",
|
||||
|
|
|
@ -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 });
|
||||
|
||||
|
|
|
@ -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]);
|
||||
});
|
|
@ -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 = [];
|
||||
|
|
110
src/api/panda.js
110
src/api/panda.js
|
@ -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);
|
||||
});
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
|
64
yarn.lock
64
yarn.lock
|
@ -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"
|
||||
|
||||
|
|
Loading…
Reference in New Issue