diff --git a/package.json b/package.json index 62d69cf..b1d35d4 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "homepage": "https://community.comma.ai/cabana", "dependencies": { "@commaai/comma-api": "1.1.6", - "@commaai/log_reader": "^0.3.1", + "@commaai/log_reader": "^0.5.3", "@commaai/my-comma-auth": "^1.1.0", "@commaai/pandajs": "^0.3.4", "@craco/craco": "^5.5.0", @@ -60,6 +60,7 @@ "socket.io-client": "^2.0.3", "stream-selector": "^0.1.1", "streamsaver": "^1.0.1", + "thyming": "^0.1.1", "vega": "^5.3.4", "vega-lite": "^3.0.0", "vega-tooltip": "^0.4.0" @@ -96,11 +97,26 @@ "deploy": "npm run build && gh-pages -d build" }, "lint-staged": { - "*.{js,jsx}": ["eslint --fix", "git add"], - "*.json": ["prettier --parser json --write", "git add"], - "*.{graphql,gql}": ["prettier --parser graphql --write", "git add"], - "*.{md,markdown}": ["prettier --parser markdown --write", "git add"], - "*.scss": ["prettier --parser postcss --write", "git add"] + "*.{js,jsx}": [ + "eslint --fix", + "git add" + ], + "*.json": [ + "prettier --parser json --write", + "git add" + ], + "*.{graphql,gql}": [ + "prettier --parser graphql --write", + "git add" + ], + "*.{md,markdown}": [ + "prettier --parser markdown --write", + "git add" + ], + "*.scss": [ + "prettier --parser postcss --write", + "git add" + ] }, "jest": { "moduleNameMapper": { @@ -111,7 +127,11 @@ } }, "browserslist": { - "production": [">0.2%", "not dead", "not op_mini all"], + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], "development": [ "last 1 chrome version", "last 1 firefox version", diff --git a/src/CanExplorer.js b/src/CanExplorer.js index 7e261df..1fee46b 100644 --- a/src/CanExplorer.js +++ b/src/CanExplorer.js @@ -40,21 +40,11 @@ const MessageParser = require('./workers/message-parser.worker.js'); const CanStreamerWorker = require('./workers/CanStreamerWorker.worker.js'); export default class CanExplorer extends Component { - static propTypes = { - dongleId: PropTypes.string, - name: PropTypes.string, - dbc: PropTypes.instanceOf(DBC), - dbcFilename: PropTypes.string, - githubAuthToken: PropTypes.string, - autoplay: PropTypes.bool, - max: PropTypes.number, - url: PropTypes.string - }; - constructor(props) { super(props); this.state = { messages: {}, + thumbnails: [], selectedMessages: [], route: null, canFrameOffset: 0, @@ -317,6 +307,45 @@ export default class CanExplorer extends Component { }); } + mergeThumbnails(newThumbnails) { + const { thumbnails } = this.state; + if (!newThumbnails || !newThumbnails.length) { + return thumbnails; + } + if (!thumbnails.length) { + return newThumbnails; + } + + let oldIndex = 0; + let newIndex = 0; + + // is old immediately after new? + if (newThumbnails[0].monoTime > thumbnails[thumbnails.length - 1]) { + return thumbnails.concat(newThumbnails); + } + // is new immediately after old? + if (newThumbnails[newThumbnails.length - 1] < thumbnails[0]) { + return newThumbnails.concat(thumbnails); + } + let result = []; + while (oldIndex < thumbnails.length && newIndex < newThumbnails.length) { + if (thumbnails[oldIndex].monoTime < newThumbnails[newIndex].monoTime) { + result.push(thumbnails[oldIndex]); + oldIndex += 1; + } else { + result.push(newThumbnails[newIndex]); + newIndex += 1; + } + } + if (oldIndex < thumbnails.length) { + result = result.concat(thumbnails.slice(oldIndex)); + } else if (newIndex < newThumbnails.length) { + result = result.concat(newThumbnails.slice(newIndex)); + } + + return result; + } + addAndRehydrateMessages(newMessages, options) { // Adds new message entries to messages state // and "rehydrates" ES6 classes (message frame) @@ -442,14 +471,13 @@ export default class CanExplorer extends Component { dbcFilename, route, firstCanTime, - canFrameOffset, - maxByteStateChangeCount + canFrameOffset } = this.state; + let { maxByteStateChangeCount } = this.state; if (!prevMsgEntries) { // we have previous messages loaded const { messages } = this.state; - const canStartTime = firstCanTime - canFrameOffset; prevMsgEntries = {}; Object.keys(messages).forEach((key) => { const { entries } = messages[key]; @@ -485,7 +513,8 @@ export default class CanExplorer extends Component { return; } - let { newMessages, maxByteStateChangeCount, isFinished } = e.data; + maxByteStateChangeCount = e.data.maxByteStateChangeCount; + const { newMessages, newThumbnails, isFinished } = e.data; if (maxByteStateChangeCount > this.state.maxByteStateChangeCount) { this.setState({ maxByteStateChangeCount }); } else { @@ -497,12 +526,14 @@ export default class CanExplorer extends Component { maxByteStateChangeCount ); const prevMsgEntries = {}; - for (const key in newMessages) { + Object.keys(newMessages).forEach((key) => { prevMsgEntries[key] = newMessages[key].entries[newMessages[key].entries.length - 1]; - } + }); + + const thumbnails = this.mergeThumbnails(newThumbnails); if (!isFinished) { - this.setState({ messages }); + this.setState({ messages, thumbnails }); } else { const loadingParts = this.state.loadingParts.filter((p) => p !== part); const loadedParts = [part, ...this.state.loadedParts]; @@ -510,6 +541,7 @@ export default class CanExplorer extends Component { this.setState( { messages, + thumbnails, partsLoaded: this.state.partsLoaded + 1, loadingParts, loadedParts @@ -943,6 +975,26 @@ export default class CanExplorer extends Component { } render() { + const { + route, + messages, + selectedMessages, + currentParts, + dbcFilename, + dbcLastSaved, + seekTime, + seekIndex, + shareUrl, + maxByteStateChangeCount, + live, + thumbnails, + selectedMessage, + canFrameOffset, + firstCanTime, + currentPart, + partsLoaded + } = this.state; + return (
- {this.state.route || this.state.live ? ( + {route || live ? ( ) : null}
@@ -1063,3 +1116,14 @@ export default class CanExplorer extends Component { ); } } + +CanExplorer.propTypes = { + dongleId: PropTypes.string, + name: PropTypes.string, + dbc: PropTypes.instanceOf(DBC), + dbcFilename: PropTypes.string, + githubAuthToken: PropTypes.string, + autoplay: PropTypes.bool, + max: PropTypes.number, + url: PropTypes.string +}; diff --git a/src/__tests__/workers/rlog-utils.test.js b/src/__tests__/workers/rlog-utils.test.js new file mode 100644 index 0000000..f66956b --- /dev/null +++ b/src/__tests__/workers/rlog-utils.test.js @@ -0,0 +1,61 @@ +/* eslint-env jest */ +import { + signedShortToByteArray, + shortToByteArray, + longToByteArray, + signedLongToByteArray, + getThermalFlags, + // getHealthFlags, + // getFlags, + // getUbloxGnss, + // getEgoData, + // getCarStateControls, + // getWheelSpeeds, + // getThermalFreeSpace, + // getThermalData, + // getThermalCPU, + // getHealth +} from '../../workers/rlog-utils'; + +describe('byte array methods', () => { + test('signedShortToByteArray', () => { + expect(signedShortToByteArray(123)).toMatchObject([0, 123]); + expect(signedShortToByteArray(-123)).toMatchObject([255, 133]); + }); + test('shortToByteArray', () => { + expect(shortToByteArray(123)).toMatchObject([0, 123]); + expect(shortToByteArray(-123)).toMatchObject([255, 133]); + }); + test('longToByteArray', () => { + expect(longToByteArray(123)).toMatchObject([0, 0, 0, 123]); + expect(longToByteArray(-123)).toMatchObject([255, 255, 255, 133]); + }); + test('signedLongToByteArray', () => { + expect(signedLongToByteArray(123)).toMatchObject([0, 0, 0, 123]); + expect(signedLongToByteArray(-123)).toMatchObject([255, 255, 255, 133]); + }); +}); + +describe('flags', () => { + test('getThermalFlags', () => { + expect(getThermalFlags({ + UsbOnline: false, + Started: false + })).toBe(0x00); + + expect(getThermalFlags({ + UsbOnline: true, + Started: false + })).toBe(0x01); + + expect(getThermalFlags({ + UsbOnline: false, + Started: true + })).toBe(0x02); + + expect(getThermalFlags({ + UsbOnline: true, + Started: true + })).toBe(0x03); + }); +}); diff --git a/src/components/Explorer.js b/src/components/Explorer.js index 2096aba..d59d0a8 100644 --- a/src/components/Explorer.js +++ b/src/components/Explorer.js @@ -460,6 +460,8 @@ export default class Explorer extends Component { ? 'is-expanded' : null; + const { thumbnails, messages } = this.props; + let graphSegment = this.state.segment; if (!graphSegment.length && this.props.currentParts) { graphSegment = [ @@ -471,7 +473,7 @@ export default class Explorer extends Component { return (
- {this.props.messages[this.props.selectedMessage] + {messages[this.props.selectedMessage] ? this.renderExplorerSignals() : this.renderSelectMessagePrompt()}
@@ -485,7 +487,7 @@ export default class Explorer extends Component {

) : null} @@ -515,7 +518,7 @@ export default class Explorer extends Component { ) : null} - Loading video -
- ); + onUserSeek(ratio) { + /* ratio in [0,1] */ + + const { videoElement } = this.state; + const { onUserSeek } = this.props; + const seekTime = this.ratioTime(ratio); + const funcSeekToRatio = () => onUserSeek(seekTime); + + if (Number.isNaN(videoElement.duration)) { + this.setState({ shouldRestartHls: true }, funcSeekToRatio); + return; + } + videoElement.currentTime = seekTime; + + if (ratio === 0) { + this.setState({ shouldRestartHls: true }, funcSeekToRatio); + } else { + funcSeekToRatio(); + } + } + + onHlsRestart() { + this.setState({ shouldRestartHls: false }); + } + + onPlaySeek(offset) { + const { onPlaySeek } = this.props; + this.seekTime = offset; + onPlaySeek(offset); } onLoadStart() { @@ -132,6 +139,18 @@ export default class RouteVideoSync extends Component { }); } + loadingOverlay() { + return ( +
+ Loading video +
+ ); + } + videoLength() { if (this.props.segment.length) { return this.props.segment[1] - this.props.segment[0]; @@ -168,73 +187,73 @@ export default class RouteVideoSync extends Component { return ratio * this.videoLength() + this.startTime(); } - onVideoElementAvailable(videoElement) { - this.setState({ videoElement }); - } - - onUserSeek(ratio) { - /* ratio in [0,1] */ - - const { videoElement } = this.state; - if (isNaN(videoElement.duration)) { - this.setState({ shouldRestartHls: true }, funcSeekToRatio); - return; + nearestFrameUrl() { + const { thumbnails } = this.props; + if (!this.seekTime) { + return ''; } - const seekTime = this.ratioTime(ratio); - videoElement.currentTime = seekTime; - - const funcSeekToRatio = () => this.props.onUserSeek(seekTime); - if (ratio === 0) { - this.setState({ shouldRestartHls: true }, funcSeekToRatio); - } else { - funcSeekToRatio(); + for (let i = 0, l = thumbnails.length; i < l; ++i) { + if (Math.abs(thumbnails[i].monoTime - this.seekTime) < 5) { + const data = btoa(String.fromCharCode(...thumbnails[i].data)); + return `data:image/jpeg;base64,${data}`; + } } - } - - onHlsRestart() { - this.setState({ shouldRestartHls: false }); + return ''; } render() { + const { + isLoading, + shouldRestartHls, + shouldShowJpeg + } = this.state; + const { + userSeekTime, + url, + playSpeed, + playing, + onVideoClick, + segmentIndices + } = this.props; return (
- {this.state.isLoading ? this.loadingOverlay() : null} - {this.state.shouldShowJpeg ? ( + {isLoading ? this.loadingOverlay() : null} + {shouldShowJpeg ? ( {`Camera ) : null} entry.messages[id].byteStateChangeCounts[idx] + count + ); + + entry.messages[id].entries.push(msgEntry); +} + +function insertCanMessage(entry, logTime, msg) { + const src = msg.Src; + const address = Number(msg.Address); + const addressHexStr = address.toString(16); + const id = `${src}:${addressHexStr}`; + + if (!entry.messages[id]) { + entry.messages[id] = DbcUtils.createMessageSpec( + entry.dbc, + address, + id, + src + ); + entry.messages[id].isLogEvent = false; + } + const prevMsgEntry = getPrevMsgEntry( + entry.messages, + entry.options.prevMsgEntries, + id + ); + + const { msgEntry, byteStateChangeCounts } = DbcUtils.parseMessage( + entry.dbc, + logTime, + address, + msg.Dat, + entry.options.canStartTime, + prevMsgEntry + ); + + entry.messages[id].byteStateChangeCounts = byteStateChangeCounts.map( + (count, idx) => entry.messages[id].byteStateChangeCounts[idx] + count + ); + + entry.messages[id].entries.push(msgEntry); + + // console.log(id); +} + async function loadData(entry) { let url = null; @@ -85,9 +151,10 @@ async function loadData(entry) { } if (!url || url.indexOf('.7z') !== -1) { - return self.postMessage({ + self.postMessage({ error: 'Invalid or missing log files' }); + return; } const res = await getLogPart(entry.logUrls[entry.part]); const logReader = new LogStream(res); @@ -102,10 +169,6 @@ async function loadData(entry) { }); }); - const msgArr = []; - const startTime = Date.now(); - const i = 0; - logReader((msg) => { if (entry.ended) { console.log('You can get msgs after end', msg); @@ -191,290 +254,49 @@ async function loadData(entry) { monoTime, partial(getThermalFreeSpace, msg.Thermal) ); + } else if ('Thumbnail' in msg) { + const monoTime = msg.LogMonoTime / 1000000000 - entry.options.canStartTime; + const data = new Uint8Array(msg.Thumbnail.Thumbnail); + entry.thumbnails.push({ data, monoTime }); } else { + // console.log(Object.keys(msg)); return; } queueBatch(entry); }); } -function queueBatch(entry) { - if (!entry.batching) { - entry.batching = timeout(entry.sendBatch, DEBOUNCE_DELAY); - } +function CacheEntry(options) { + options = options || {}; + this.options = options; + + const { + route, part, dbc, logUrls + } = options; + + this.messages = {}; + this.thumbnails = []; + this.route = route; + this.part = part; + this.dbc = dbc; + this.logUrls = logUrls; + this.sendBatch = partial(sendBatch, this); + this.loadData = partial(loadData, this); } -function insertEventData(src, part, entry, logTime, getData) { - const id = `${src}:${part}`; - const address = addressForName(id); +function handleMessage(msg) { + const options = msg.data; - if (!entry.messages[id]) { - entry.messages[id] = DbcUtils.createMessageSpec( - entry.dbc, - address, - id, - src - ); - entry.messages[id].isLogEvent = true; + if (options.action === 'terminate') { + close(); + return; } - const prevMsgEntry = getPrevMsgEntry( - entry.messages, - entry.options.prevMsgEntries, - id - ); - const { msgEntry, byteStateChangeCounts } = DbcUtils.parseMessage( - entry.dbc, - logTime, - address, - getData(), - entry.options.canStartTime, - prevMsgEntry - ); + options.dbc = new DBC(options.dbcText); - entry.messages[id].byteStateChangeCounts = byteStateChangeCounts.map( - (count, idx) => entry.messages[id].byteStateChangeCounts[idx] + count - ); - - entry.messages[id].entries.push(msgEntry); + const entry = new CacheEntry(options); + // load in the data! + entry.loadData(); } -function getThermalFlags(state) { - let flags = 0x00; - - if (state.UsbOnline) { - flags |= 0x01; - } - if (state.Started) { - flags |= 0x02; - } - - return flags; -} - -function getThermalFreeSpace(state) { - return longToByteArray(state.FreeSpace * 1000000000); -} - -function getThermalData(state) { - return shortToByteArray(state.Mem) - .concat(shortToByteArray(state.Gpu)) - .concat(shortToByteArray(state.FanSpeed)) - .concat(state.BatteryPercent) - .concat(getThermalFlags(state)); -} - -function getThermalCPU(state) { - return shortToByteArray(state.Cpu0) - .concat(shortToByteArray(state.Cpu1)) - .concat(shortToByteArray(state.Cpu2)) - .concat(shortToByteArray(state.Cpu3)); -} - -function getHealth(state) { - return signedShortToByteArray(state.Voltage) - .concat(state.Current) - .concat(getHealthFlags(state)); -} - -function getHealthFlags(state) { - let flags = 0x00; - - if (state.Started) { - flags |= 0x01; - } - if (state.ControlsAllowed) { - flags |= 0x02; - } - if (state.GasInterceptorDetected) { - flags |= 0x04; - } - if (state.StartedSignalDetected) { - flags |= 0x08; - } - - return flags; -} - -function getUbloxGnss(state) { - return signedLongToByteArray(state.RcvTow / 1000) - .concat(signedShortToByteArray(state.GpsWeek)) - .concat([state.LeapSeconds]) - .concat([state.NumMeas]); -} - -function getEgoData(state) { - return signedShortToByteArray(state.VEgo * 1000) - .concat(signedShortToByteArray(state.AEgo * 1000)) - .concat(signedShortToByteArray(state.VEgoRaw * 1000)) - .concat(signedShortToByteArray(state.YawRate * 1000)); -} - -function getCarStateControls(state) { - return signedLongToByteArray(state.SteeringAngle * 1000) - .concat(signedShortToByteArray(state.Brake * 1000)) - .concat(signedShortToByteArray(state.Gas * 1000)); -} - -function getWheelSpeeds(state) { - return signedShortToByteArray(state.WheelSpeeds.Fl * 100) - .concat(signedShortToByteArray(state.WheelSpeeds.Fr * 100)) - .concat(signedShortToByteArray(state.WheelSpeeds.Rl * 100)) - .concat(signedShortToByteArray(state.WheelSpeeds.Rr * 100)); -} - -function getFlags(state) { - let flags = 0x00; - const arr = [0, 0, 0]; - - if (state.LeftBlinker) { - flags |= 0x01; - } - if (state.RightBlinker) { - flags |= 0x02; - } - if (state.GenericToggle) { - flags |= 0x04; - } - if (state.DoorOpen) { - flags |= 0x08; - } - if (state.SeatbeltUnlatched) { - flags |= 0x10; - } - if (state.GasPressed) { - flags |= 0x20; - } - if (state.BrakeLights) { - flags |= 0x40; - } - if (state.SteeringPressed) { - flags |= 0x80; - } - - arr[0] = flags; - flags = 0x00; - - if (state.Standstill) { - flags |= 0x01; - } - if (state.CruiseState.Enabled) { - flags |= 0x02; - } - if (state.CruiseState.Available) { - flags |= 0x04; - } - if (state.CruiseState.Standstill) { - flags |= 0x08; - } - if (state.GearShifter) { - flags |= state.GearShifter << 4; - } - - arr[1] = flags; - arr[2] = state.CruiseState.Speed; - return arr; -} - -function insertCanMessage(entry, logTime, msg) { - const src = msg.Src; - const address = Number(msg.Address); - const busTime = msg.BusTime; - const addressHexStr = address.toString(16); - const id = `${src}:${addressHexStr}`; - - if (!entry.messages[id]) { - entry.messages[id] = DbcUtils.createMessageSpec( - entry.dbc, - address, - id, - src - ); - entry.messages[id].isLogEvent = false; - } - const prevMsgEntry = getPrevMsgEntry( - entry.messages, - entry.options.prevMsgEntries, - id - ); - - const { msgEntry, byteStateChangeCounts } = DbcUtils.parseMessage( - entry.dbc, - logTime, - address, - msg.Dat, - entry.options.canStartTime, - prevMsgEntry - ); - - entry.messages[id].byteStateChangeCounts = byteStateChangeCounts.map( - (count, idx) => entry.messages[id].byteStateChangeCounts[idx] + count - ); - - entry.messages[id].entries.push(msgEntry); - - // console.log(id); -} - -function getPrevMsgEntry(messages, prevMsgEntries, id) { - if (messages[id].entries.length) { - return messages[id].entries[messages[id].entries.length - 1]; - } - return prevMsgEntries[id] || null; -} - -function signedShortToByteArray(short) { - const byteArray = [0, 0]; - const isNegative = short < 0; - if (isNegative) { - short += Math.pow(2, 8 * byteArray.length); - } - - for (let index = byteArray.length - 1; index >= 0; --index) { - const byte = short & 0xff; - byteArray[index] = byte; - short >>= 8; - } - - return byteArray; -} - -function shortToByteArray(short) { - const byteArray = [0, 0]; - - for (let index = byteArray.length - 1; index >= 0; --index) { - const byte = short & 0xff; - byteArray[index] = byte; - short >>= 8; - } - - return byteArray; -} - -function longToByteArray(long) { - const byteArray = [0, 0, 0, 0]; - - for (let index = byteArray.length - 1; index >= 0; --index) { - const byte = long & 0xff; - byteArray[index] = byte; - long >>= 8; - } - - return byteArray; -} - -function signedLongToByteArray(long) { - const byteArray = [0, 0, 0, 0]; - const isNegative = long < 0; - if (isNegative) { - long += Math.pow(2, 8 * byteArray.length); - } - - for (let index = byteArray.length - 1; index >= 0; --index) { - const byte = long & 0xff; - byteArray[index] = byte; - long >>= 8; - } - - return byteArray; -} +self.onmessage = handleMessage; diff --git a/src/workers/rlog-utils.js b/src/workers/rlog-utils.js new file mode 100644 index 0000000..12c89ac --- /dev/null +++ b/src/workers/rlog-utils.js @@ -0,0 +1,194 @@ +/* eslint-disable no-param-reassign, no-bitwise */ + +export function signedShortToByteArray(short) { + const byteArray = [0, 0]; + const isNegative = short < 0; + if (isNegative) { + short += 2 ** (8 * byteArray.length); + } + + for (let index = byteArray.length - 1; index >= 0; --index) { + const byte = short & 0xff; + byteArray[index] = byte; + short >>= 8; + } + + return byteArray; +} + +export function shortToByteArray(short) { + const byteArray = [0, 0]; + + for (let index = byteArray.length - 1; index >= 0; --index) { + const byte = short & 0xff; + byteArray[index] = byte; + short >>= 8; + } + + return byteArray; +} + +export function longToByteArray(long) { + const byteArray = [0, 0, 0, 0]; + + for (let index = byteArray.length - 1; index >= 0; --index) { + const byte = long & 0xff; + byteArray[index] = byte; + long >>= 8; + } + + return byteArray; +} + +export function signedLongToByteArray(long) { + const byteArray = [0, 0, 0, 0]; + const isNegative = long < 0; + if (isNegative) { + long += 2 ** (8 * byteArray.length); + } + + for (let index = byteArray.length - 1; index >= 0; --index) { + const byte = long & 0xff; + byteArray[index] = byte; + long >>= 8; + } + + return byteArray; +} + +export function getThermalFlags(state) { + let flags = 0x00; + + if (state.UsbOnline) { + flags |= 0x01; + } + if (state.Started) { + flags |= 0x02; + } + + return flags; +} + +export function getHealthFlags(state) { + let flags = 0x00; + + if (state.Started) { + flags |= 0x01; + } + if (state.ControlsAllowed) { + flags |= 0x02; + } + if (state.GasInterceptorDetected) { + flags |= 0x04; + } + if (state.StartedSignalDetected) { + flags |= 0x08; + } + + return flags; +} + +export function getFlags(state) { + let flags = 0x00; + const arr = [0, 0, 0]; + + if (state.LeftBlinker) { + flags |= 0x01; + } + if (state.RightBlinker) { + flags |= 0x02; + } + if (state.GenericToggle) { + flags |= 0x04; + } + if (state.DoorOpen) { + flags |= 0x08; + } + if (state.SeatbeltUnlatched) { + flags |= 0x10; + } + if (state.GasPressed) { + flags |= 0x20; + } + if (state.BrakeLights) { + flags |= 0x40; + } + if (state.SteeringPressed) { + flags |= 0x80; + } + + arr[0] = flags; + flags = 0x00; + + if (state.Standstill) { + flags |= 0x01; + } + if (state.CruiseState.Enabled) { + flags |= 0x02; + } + if (state.CruiseState.Available) { + flags |= 0x04; + } + if (state.CruiseState.Standstill) { + flags |= 0x08; + } + if (state.GearShifter) { + flags |= state.GearShifter << 4; + } + + arr[1] = flags; + arr[2] = state.CruiseState.Speed; + return arr; +} + +export function getUbloxGnss(state) { + return signedLongToByteArray(state.RcvTow / 1000) + .concat(signedShortToByteArray(state.GpsWeek)) + .concat([state.LeapSeconds]) + .concat([state.NumMeas]); +} + +export function getEgoData(state) { + return signedShortToByteArray(state.VEgo * 1000) + .concat(signedShortToByteArray(state.AEgo * 1000)) + .concat(signedShortToByteArray(state.VEgoRaw * 1000)) + .concat(signedShortToByteArray(state.YawRate * 1000)); +} + +export function getCarStateControls(state) { + return signedLongToByteArray(state.SteeringAngle * 1000) + .concat(signedShortToByteArray(state.Brake * 1000)) + .concat(signedShortToByteArray(state.Gas * 1000)); +} + +export function getWheelSpeeds(state) { + return signedShortToByteArray(state.WheelSpeeds.Fl * 100) + .concat(signedShortToByteArray(state.WheelSpeeds.Fr * 100)) + .concat(signedShortToByteArray(state.WheelSpeeds.Rl * 100)) + .concat(signedShortToByteArray(state.WheelSpeeds.Rr * 100)); +} + +export function getThermalFreeSpace(state) { + return longToByteArray(state.FreeSpace * 1000000000); +} + +export function getThermalData(state) { + return shortToByteArray(state.Mem) + .concat(shortToByteArray(state.Gpu)) + .concat(shortToByteArray(state.FanSpeed)) + .concat(state.BatteryPercent) + .concat(getThermalFlags(state)); +} + +export function getThermalCPU(state) { + return shortToByteArray(state.Cpu0) + .concat(shortToByteArray(state.Cpu1)) + .concat(shortToByteArray(state.Cpu2)) + .concat(shortToByteArray(state.Cpu3)); +} + +export function getHealth(state) { + return signedShortToByteArray(state.Voltage) + .concat(state.Current) + .concat(getHealthFlags(state)); +} diff --git a/yarn.lock b/yarn.lock index ad5e536..32c27ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -911,6 +911,13 @@ exec-sh "^0.3.2" minimist "^1.2.0" +"@commaai/capnp-json@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@commaai/capnp-json/-/capnp-json-0.2.0.tgz#866bba39274b02f899803dc7968a7fe79fb02321" + integrity sha512-PXqEjvoLkb7VPaeMlFV7NspHAUMfTGhd6plQ72rilrjagf1os73fdItdlhk/q2XELx+l/Vq1xRSLJT1Os0DVqQ== + dependencies: + capnp-ts "0.2.4" + "@commaai/comma-api@1.1.6": version "1.1.6" resolved "https://registry.yarnpkg.com/@commaai/comma-api/-/comma-api-1.1.6.tgz#be486443a8d8843c74a811cdd406b85694b5d526" @@ -931,21 +938,21 @@ joi-browser "^13.4.0" querystringify "^2.1.1" -"@commaai/log_reader@^0.3.1": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@commaai/log_reader/-/log_reader-0.3.1.tgz#0e2b47b621ac9b852ccf5137787e17952a34170c" - integrity sha512-WMV6BiSBOfPFOld3KKuuEHRSAPv95cpeDes4LWwy4cONYZHnnuVu91v5nYzQVbB3F6eQaCvkakmwLSzR2UQkow== +"@commaai/log_reader@^0.5.3": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@commaai/log_reader/-/log_reader-0.5.3.tgz#c3dd2c6f3b911198c32bbebd34d0e9cc862e0672" + integrity sha512-/962u7s7uXPMGLz2AH+L57w+CSXCX1uZmh0OLSwGZXzyxO/dGvimaVspYe9N78Q0BYa1B5ctv986zliaAblrmg== dependencies: - "@commaai/unbzip2-stream" "^2.0.0" + "@commaai/capnp-json" "^0.2.0" JSONStream "^1.3.2" ap "^0.2.0" - capnp-json "^0.1.2" capnp-split "^0.1.1" capnp-ts "^0.2.4" commander "^2.15.1" file-type "^7.6.0" geval "^2.2.0" stream-selector "^0.4.0" + wasm-bz2 "^0.0.2" "@commaai/my-comma-auth@^1.1.0": version "1.1.3" @@ -978,11 +985,6 @@ optionalDependencies: usb "^1.3.1" -"@commaai/unbzip2-stream@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@commaai/unbzip2-stream/-/unbzip2-stream-2.0.0.tgz#93821dc5e72f04ec5eedef5ed4e6ff57412019e0" - integrity sha512-Dqzzmos7r2OsvpJ9YtID1n50GpVwB4dw7ft41XJZM2V7bJ0L68ygeK4WktaT1ZslgWz5NzqHEn0t9nWFuopixg== - "@craco/craco@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@craco/craco/-/craco-5.5.0.tgz#081b25522d866fbc14b80fe61517f2f10e3e4499" @@ -2830,11 +2832,6 @@ canvas@^1.6: dependencies: nan "^2.10.0" -capnp-json@^0.1.2: - version "0.1.3" - resolved "https://registry.yarnpkg.com/capnp-json/-/capnp-json-0.1.3.tgz#e305dd690b9fcf53e2e19278eb6a99d187ea8047" - integrity sha512-S1bRHkHECsFVjETOZKnxIeab00ozGb7wyS7HEkNhOLUo1/9oDvcURdttp7AbOPDUSIUWM9HoPKdnnH/SSHNBWA== - capnp-split@^0.1.1: version "0.1.4" resolved "https://registry.yarnpkg.com/capnp-split/-/capnp-split-0.1.4.tgz#c3f86890645cd203c5ce23ff1ccb4fe6642ea4a3" @@ -2842,7 +2839,7 @@ capnp-split@^0.1.1: dependencies: through2 "^2.0.3" -capnp-ts@^0.2.4: +capnp-ts@0.2.4, capnp-ts@^0.2.4: version "0.2.4" resolved "https://registry.yarnpkg.com/capnp-ts/-/capnp-ts-0.2.4.tgz#da729493311f384d65d480d9afe979ceab9f41bb" integrity sha512-A9+Awl2WQDhg0fpEoyDpIF7RUQp27gpYBLRGV2zKA37a0IBEutdgIKDI7pO44C9AhzxvCfR6Ooj5W14D3TKaQA== @@ -13330,6 +13327,14 @@ walker@^1.0.7, walker@~1.0.5: dependencies: makeerror "1.0.x" +wasm-bz2@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wasm-bz2/-/wasm-bz2-0.0.2.tgz#ff5c4e3ed660169ec14f00bd9f4650c43c142a13" + integrity sha512-OjWSwiIvz8yacBBqajiZTSVI9Yom1nHFjBuXJFTOwa9rkHIQFNmFaEG65kmnUlFMUNU6mxmGGKAwF8BGMesZ6g== + dependencies: + ap "^0.2.0" + weakmap-event "^2.0.7" + watchpack@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00"