cabana/src/api/panda.js

111 lines
2.5 KiB
JavaScript

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);
});
}