2017-05-29 17:52:17 -06:00
|
|
|
import React, { Component } from 'react';
|
|
|
|
import { css, StyleSheet } from 'aphrodite/no-important';
|
2017-06-13 18:40:05 -06:00
|
|
|
import Moment from 'moment';
|
|
|
|
import PropTypes from 'prop-types';
|
2017-05-29 17:52:17 -06:00
|
|
|
|
2017-06-16 23:28:51 -06:00
|
|
|
import * as GithubAuth from './api/github-auth';
|
2017-06-13 18:40:05 -06:00
|
|
|
import DBC from './models/can/dbc';
|
2017-05-29 17:52:17 -06:00
|
|
|
import Meta from './components/meta';
|
|
|
|
import Explorer from './components/explorer';
|
2017-06-13 18:40:05 -06:00
|
|
|
import * as Routes from './api/routes';
|
|
|
|
import SaveDbcModal from './components/SaveDbcModal';
|
|
|
|
import LoadDbcModal from './components/LoadDbcModal';
|
|
|
|
const CanFetcher = require('./workers/can-fetcher.worker.js');
|
|
|
|
const MessageParser = require("./workers/message-parser.worker.js");
|
2017-06-19 00:00:33 -06:00
|
|
|
const CanOffsetFinder = require('./workers/can-offset-finder.worker.js');
|
2017-06-19 17:35:10 -06:00
|
|
|
import debounce from './utils/debounce';
|
2017-06-19 21:40:07 -06:00
|
|
|
import EditMessageModal from './components/EditMessageModal';
|
2017-06-27 17:28:42 -06:00
|
|
|
import LoadingBar from './components/LoadingBar';
|
2017-06-28 19:03:21 -06:00
|
|
|
import {persistDbc} from './api/localstorage';
|
2017-06-28 21:28:39 -06:00
|
|
|
import OpenDbc from './api/opendbc';
|
2017-06-28 19:03:21 -06:00
|
|
|
|
2017-05-29 17:52:17 -06:00
|
|
|
export default class CanExplorer extends Component {
|
|
|
|
static propTypes = {
|
2017-06-28 19:03:21 -06:00
|
|
|
dongleId: PropTypes.string.isRequired,
|
2017-06-29 14:24:36 -06:00
|
|
|
name: PropTypes.string.isRequired,
|
2017-06-28 19:03:21 -06:00
|
|
|
dbc: PropTypes.instanceOf(DBC),
|
2017-06-28 19:39:52 -06:00
|
|
|
dbcFilename: PropTypes.string,
|
|
|
|
githubAuthToken: PropTypes.string
|
2017-05-29 17:52:17 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
this.state = {
|
|
|
|
messages: {},
|
2017-06-13 18:40:05 -06:00
|
|
|
route: {},
|
|
|
|
canFrameOffset: -1,
|
|
|
|
firstCanTime: null,
|
2017-05-29 17:52:17 -06:00
|
|
|
selectedMessage: null,
|
2017-06-15 00:22:52 -06:00
|
|
|
currentParts: [0,0],
|
2017-06-13 18:40:05 -06:00
|
|
|
showLoadDbc: false,
|
|
|
|
showSaveDbc: false,
|
2017-06-19 21:40:07 -06:00
|
|
|
showEditMessageModal: false,
|
|
|
|
editMessageModalMessage: null,
|
2017-06-21 23:56:52 -06:00
|
|
|
dbc: new DBC(),
|
|
|
|
dbcFilename: 'New DBC',
|
2017-06-22 21:38:38 -06:00
|
|
|
dbcLastSaved: null,
|
2017-06-23 15:17:35 -06:00
|
|
|
seekTime: 0,
|
|
|
|
seekIndex: 0,
|
2017-06-27 17:28:42 -06:00
|
|
|
maxByteStateChangeCount: 0,
|
2017-06-28 21:38:08 -06:00
|
|
|
isLoading: true
|
2017-05-29 17:52:17 -06:00
|
|
|
};
|
2017-06-28 21:28:39 -06:00
|
|
|
this.openDbcClient = new OpenDbc(props.githubAuthToken);
|
2017-05-29 17:52:17 -06:00
|
|
|
|
2017-06-13 18:40:05 -06:00
|
|
|
this.showLoadDbc = this.showLoadDbc.bind(this);
|
|
|
|
this.hideLoadDbc = this.hideLoadDbc.bind(this);
|
|
|
|
this.showSaveDbc = this.showSaveDbc.bind(this);
|
|
|
|
this.hideSaveDbc = this.hideSaveDbc.bind(this);
|
2017-06-19 21:40:07 -06:00
|
|
|
this.showEditMessageModal = this.showEditMessageModal.bind(this);
|
|
|
|
this.hideEditMessageModal = this.hideEditMessageModal.bind(this);
|
2017-06-13 18:40:05 -06:00
|
|
|
this.onDbcSelected = this.onDbcSelected.bind(this);
|
|
|
|
this.onDbcSaved = this.onDbcSaved.bind(this);
|
|
|
|
this.onConfirmedSignalChange = this.onConfirmedSignalChange.bind(this);
|
2017-06-15 00:22:52 -06:00
|
|
|
this.onPartChange = this.onPartChange.bind(this);
|
2017-06-20 17:46:07 -06:00
|
|
|
this.onMessageFrameEdited = this.onMessageFrameEdited.bind(this);
|
2017-06-23 15:17:35 -06:00
|
|
|
this.onSeek = this.onSeek.bind(this);
|
2017-06-23 17:06:34 -06:00
|
|
|
this.onMessageSelected = this.onMessageSelected.bind(this);
|
2017-06-28 19:03:21 -06:00
|
|
|
this.initCanData = this.initCanData.bind(this);
|
2017-05-29 17:52:17 -06:00
|
|
|
}
|
|
|
|
|
2017-06-13 18:40:05 -06:00
|
|
|
componentWillMount() {
|
|
|
|
const {dongleId, name} = this.props;
|
|
|
|
Routes.fetchRoutes(dongleId).then((routes) => {
|
|
|
|
if(routes) {
|
|
|
|
const route = routes[name];
|
2017-06-28 19:03:21 -06:00
|
|
|
const newState = {route, currentParts: [0,2]};
|
2017-06-13 18:40:05 -06:00
|
|
|
if(this.props.dbc !== undefined) {
|
2017-06-28 19:03:21 -06:00
|
|
|
newState.dbc = this.props.dbc;
|
|
|
|
newState.dbcFilename = this.props.dbcFilename;
|
2017-06-21 23:56:52 -06:00
|
|
|
}
|
2017-06-28 19:03:21 -06:00
|
|
|
this.setState(newState, this.initCanData);
|
2017-06-13 18:40:05 -06:00
|
|
|
}
|
2017-06-19 00:00:33 -06:00
|
|
|
});
|
2017-05-29 17:52:17 -06:00
|
|
|
}
|
|
|
|
|
2017-06-28 19:03:21 -06:00
|
|
|
initCanData() {
|
|
|
|
const {route} = this.state;
|
|
|
|
|
|
|
|
const offsetFinder = new CanOffsetFinder();
|
|
|
|
offsetFinder.postMessage({partCount: route.proclog,
|
|
|
|
base: route.url});
|
|
|
|
|
|
|
|
offsetFinder.onmessage = (e) => {
|
|
|
|
const {canFrameOffset, firstCanTime} = e.data;
|
|
|
|
|
|
|
|
this.setState({canFrameOffset, firstCanTime}, () => {
|
|
|
|
this.spawnWorker(this.state.currentParts);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:40:05 -06:00
|
|
|
onDbcSelected(filename, dbcInstance) {
|
|
|
|
this.hideLoadDbc();
|
2017-06-16 23:28:51 -06:00
|
|
|
this.setState({dbc: dbcInstance,
|
|
|
|
dbcFilename: filename,
|
|
|
|
currentParts: [0,2],
|
|
|
|
selectedMessage: null,
|
|
|
|
messages: {}}, () => {
|
2017-06-13 18:40:05 -06:00
|
|
|
const {route} = this.state;
|
|
|
|
|
|
|
|
// Pass DBC text to webworker b/c can't pass instance of es6 class
|
2017-06-15 00:22:52 -06:00
|
|
|
this.spawnWorker(this.state.currentParts);
|
2017-06-13 19:28:08 -06:00
|
|
|
});
|
2017-05-29 17:52:17 -06:00
|
|
|
}
|
|
|
|
|
2017-06-13 18:40:05 -06:00
|
|
|
onDbcSaved(dbcFilename) {
|
|
|
|
const dbcLastSaved = Moment();
|
|
|
|
this.setState({dbcLastSaved, dbcFilename})
|
|
|
|
this.hideSaveDbc();
|
2017-05-29 17:52:17 -06:00
|
|
|
}
|
|
|
|
|
2017-06-23 17:41:05 -06:00
|
|
|
spawnWorker(parts, part, prevMsgEntries) {
|
2017-06-27 17:28:42 -06:00
|
|
|
if(!this.state.isLoading) {
|
|
|
|
this.setState({isLoading: true});
|
|
|
|
}
|
2017-06-15 00:22:52 -06:00
|
|
|
const [minPart, maxPart] = parts;
|
|
|
|
if(part === undefined) {
|
|
|
|
part = minPart;
|
|
|
|
}
|
2017-06-23 17:41:05 -06:00
|
|
|
if(prevMsgEntries === undefined) {
|
|
|
|
prevMsgEntries = {};
|
|
|
|
}
|
2017-06-15 00:22:52 -06:00
|
|
|
|
2017-06-16 23:28:51 -06:00
|
|
|
const {dbc, dbcFilename, route, firstCanTime} = this.state;
|
2017-06-13 18:40:05 -06:00
|
|
|
var worker = new CanFetcher();
|
|
|
|
|
|
|
|
worker.onmessage = (e) => {
|
2017-06-19 17:35:10 -06:00
|
|
|
if(JSON.stringify(parts) != JSON.stringify(this.state.currentParts)) {
|
|
|
|
// Parts changed, stop spawning workers.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:40:05 -06:00
|
|
|
const {messages} = this.state;
|
2017-06-16 23:28:51 -06:00
|
|
|
if(this.state.dbcFilename != dbcFilename) {
|
|
|
|
// DBC changed while this worker was running
|
|
|
|
// -- don't update messages and halt recursion.
|
|
|
|
return;
|
|
|
|
}
|
2017-06-13 18:40:05 -06:00
|
|
|
|
2017-06-23 19:29:31 -06:00
|
|
|
const {newMessages, maxByteStateChangeCount} = e.data;
|
|
|
|
if(maxByteStateChangeCount > this.state.maxByteStateChangeCount) {
|
|
|
|
this.setState({maxByteStateChangeCount});
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:40:05 -06:00
|
|
|
for(var key in newMessages) {
|
|
|
|
if (key in messages) {
|
|
|
|
messages[key].entries = messages[key].entries.concat(newMessages[key].entries);
|
|
|
|
} else {
|
|
|
|
messages[key] = newMessages[key];
|
|
|
|
messages[key].signals = this.state.dbc.getSignals(messages[key].address);
|
2017-06-21 23:56:52 -06:00
|
|
|
messages[key].frame = this.state.dbc.messages.get(messages[key].address);
|
2017-06-13 18:40:05 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-23 17:41:05 -06:00
|
|
|
const prevMsgEntries = {};
|
|
|
|
for(let key in newMessages) {
|
|
|
|
const msg = newMessages[key];
|
|
|
|
prevMsgEntries[key] = msg.entries[msg.entries.length - 1];
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:40:05 -06:00
|
|
|
this.setState({messages,
|
|
|
|
partsLoaded: this.state.partsLoaded + 1}, () => {
|
2017-06-15 00:22:52 -06:00
|
|
|
if(part < maxPart) {
|
2017-06-23 17:41:05 -06:00
|
|
|
this.spawnWorker(parts, part + 1, prevMsgEntries);
|
2017-06-27 17:28:42 -06:00
|
|
|
} else {
|
|
|
|
this.setState({isLoading: false});
|
2017-06-13 18:40:05 -06:00
|
|
|
}
|
2017-05-29 17:52:17 -06:00
|
|
|
})
|
2017-06-13 18:40:05 -06:00
|
|
|
}
|
|
|
|
|
2017-06-16 23:28:51 -06:00
|
|
|
worker.postMessage({dbcText: dbc.text(),
|
|
|
|
base: route.url,
|
2017-06-13 18:40:05 -06:00
|
|
|
num: part,
|
2017-06-23 15:17:35 -06:00
|
|
|
canStartTime: firstCanTime,
|
2017-06-23 17:41:05 -06:00
|
|
|
prevMsgEntries
|
2017-06-23 15:17:35 -06:00
|
|
|
});
|
2017-05-29 17:52:17 -06:00
|
|
|
}
|
2017-06-13 18:40:05 -06:00
|
|
|
|
|
|
|
showLoadDbc() {
|
|
|
|
this.setState({showLoadDbc: true});
|
|
|
|
document.body.style.overflow = 'hidden';
|
|
|
|
}
|
|
|
|
|
|
|
|
hideLoadDbc() {
|
|
|
|
this.setState({showLoadDbc: false});
|
|
|
|
document.body.style.overflow = '';
|
|
|
|
}
|
|
|
|
|
|
|
|
showSaveDbc() {
|
|
|
|
this.setState({showSaveDbc: true})
|
|
|
|
document.body.style.overflow = 'hidden';
|
|
|
|
}
|
|
|
|
|
|
|
|
hideSaveDbc() {
|
|
|
|
this.setState({showSaveDbc: false})
|
|
|
|
document.body.style.overflow = '';
|
|
|
|
}
|
|
|
|
|
|
|
|
onConfirmedSignalChange(message) {
|
|
|
|
const signals = message.signals;
|
2017-06-28 19:03:21 -06:00
|
|
|
const {dbc, dbcFilename, route} = this.state;
|
2017-06-13 18:40:05 -06:00
|
|
|
|
|
|
|
dbc.setSignals(message.address, message.signals);
|
2017-06-28 19:03:21 -06:00
|
|
|
persistDbc(route.fullname,
|
|
|
|
{dbcFilename, dbc});
|
|
|
|
|
2017-06-27 17:28:42 -06:00
|
|
|
this.setState({dbc, isLoading: true});
|
2017-06-13 18:40:05 -06:00
|
|
|
|
|
|
|
var worker = new MessageParser();
|
|
|
|
worker.onmessage = (e) => {
|
|
|
|
const newMessage = e.data;
|
|
|
|
newMessage.signals = dbc.getSignals(newMessage.address);
|
|
|
|
|
|
|
|
const messages = {};
|
|
|
|
Object.assign(messages, this.state.messages);
|
|
|
|
messages[message.id] = newMessage;
|
2017-06-27 17:28:42 -06:00
|
|
|
this.setState({messages, isLoading: false})
|
2017-06-13 18:40:05 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
worker.postMessage({message,
|
|
|
|
dbcText: dbc.text(),
|
|
|
|
canStartTime: this.state.firstCanTime});
|
2017-05-29 17:52:17 -06:00
|
|
|
}
|
|
|
|
|
2017-06-19 17:35:10 -06:00
|
|
|
onPartChange = debounce((part) => {
|
2017-06-19 00:00:33 -06:00
|
|
|
let {currentParts} = this.state;
|
2017-06-27 16:12:18 -06:00
|
|
|
|
|
|
|
const currentPartSpan = currentParts[1] - currentParts[0] + 1;
|
2017-06-20 23:52:57 -06:00
|
|
|
currentParts = [part, part + currentPartSpan - 1];
|
2017-06-19 00:00:33 -06:00
|
|
|
|
|
|
|
this.setState({currentParts, selectedMessage: null, messages: {}}, () => {
|
|
|
|
this.spawnWorker(currentParts);
|
2017-06-15 16:40:36 -06:00
|
|
|
});
|
2017-06-19 17:35:10 -06:00
|
|
|
}, 500);
|
2017-06-15 00:22:52 -06:00
|
|
|
|
2017-06-19 21:40:07 -06:00
|
|
|
showEditMessageModal(msgKey) {
|
2017-06-20 17:46:07 -06:00
|
|
|
const msg = this.state.messages[msgKey];
|
|
|
|
if(!msg.frame) {
|
|
|
|
msg.frame = this.state.dbc.createFrame(msg.address);
|
|
|
|
}
|
|
|
|
|
2017-06-19 21:40:07 -06:00
|
|
|
this.setState({showEditMessageModal: true,
|
2017-06-20 17:46:07 -06:00
|
|
|
editMessageModalMessage: msgKey,
|
|
|
|
messages: this.state.messages});
|
2017-06-19 21:40:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
hideEditMessageModal() {
|
|
|
|
this.setState({showEditMessageModal: false});
|
|
|
|
}
|
|
|
|
|
2017-06-20 17:46:07 -06:00
|
|
|
onMessageFrameEdited(messageFrame) {
|
2017-06-28 19:03:21 -06:00
|
|
|
const {messages,
|
|
|
|
route,
|
|
|
|
dbcFilename,
|
|
|
|
dbc,
|
|
|
|
editMessageModalMessage} = this.state;
|
|
|
|
|
|
|
|
const message = Object.assign({}, messages[editMessageModalMessage]);
|
2017-06-19 21:40:07 -06:00
|
|
|
message.frame = messageFrame;
|
2017-06-28 19:03:21 -06:00
|
|
|
dbc.messages.set(messageFrame.address, messageFrame);
|
|
|
|
persistDbc(route.fullname,
|
|
|
|
{dbcFilename, dbc});
|
2017-06-20 17:46:07 -06:00
|
|
|
|
2017-06-28 19:03:21 -06:00
|
|
|
messages[editMessageModalMessage] = message;
|
2017-06-20 17:46:07 -06:00
|
|
|
this.setState({messages});
|
2017-06-19 21:40:07 -06:00
|
|
|
this.hideEditMessageModal();
|
|
|
|
}
|
|
|
|
|
2017-06-23 15:17:35 -06:00
|
|
|
onSeek(seekIndex, seekTime) {
|
|
|
|
this.setState({seekIndex, seekTime});
|
|
|
|
}
|
|
|
|
|
|
|
|
onMessageSelected(msgKey) {
|
|
|
|
let {seekTime, seekIndex, messages} = this.state;
|
|
|
|
const msg = messages[msgKey];
|
2017-06-23 17:06:34 -06:00
|
|
|
|
2017-06-23 15:17:35 -06:00
|
|
|
if(seekTime > 0) {
|
|
|
|
seekIndex = msg.entries.findIndex((e) => e.relTime >= seekTime);
|
|
|
|
if(seekIndex === -1) {
|
|
|
|
seekIndex = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
seekTime = msg.entries[seekIndex].relTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.setState({seekTime, seekIndex, selectedMessage: msgKey});
|
2017-06-22 21:38:38 -06:00
|
|
|
}
|
|
|
|
|
2017-05-29 17:52:17 -06:00
|
|
|
render() {
|
|
|
|
return (<div className={css(Styles.root)}>
|
2017-06-27 17:28:42 -06:00
|
|
|
{this.state.isLoading ?
|
|
|
|
<LoadingBar
|
|
|
|
isLoading={this.state.isLoading}
|
|
|
|
/> : null}
|
|
|
|
<div className={css(Styles.content)}>
|
|
|
|
<Meta url={this.state.route.url}
|
2017-06-20 16:46:19 -06:00
|
|
|
messages={this.state.messages}
|
2017-06-27 17:28:42 -06:00
|
|
|
partsLoaded={this.state.currentParts}
|
2017-06-29 14:24:36 -06:00
|
|
|
partsCount={this.state.route.proclog || 0}
|
2017-06-27 17:28:42 -06:00
|
|
|
onMessageSelected={this.onMessageSelected}
|
|
|
|
showLoadDbc={this.showLoadDbc}
|
|
|
|
showSaveDbc={this.showSaveDbc}
|
|
|
|
dbcFilename={this.state.dbcFilename}
|
|
|
|
dbcLastSaved={this.state.dbcLastSaved}
|
|
|
|
onPartChange={this.onPartChange}
|
|
|
|
showEditMessageModal={this.showEditMessageModal}
|
|
|
|
dongleId={this.props.dongleId}
|
|
|
|
name={this.props.name}
|
|
|
|
route={this.state.route}
|
2017-06-23 15:17:35 -06:00
|
|
|
seekTime={this.state.seekTime}
|
2017-06-28 19:39:52 -06:00
|
|
|
maxByteStateChangeCount={this.state.maxByteStateChangeCount}
|
|
|
|
githubAuthToken={this.props.githubAuthToken} />
|
2017-06-27 17:28:42 -06:00
|
|
|
<div className={css(Styles.right)}>
|
2017-06-29 14:24:36 -06:00
|
|
|
{this.state.route.url ?
|
|
|
|
<Explorer
|
|
|
|
url={this.state.route.url}
|
|
|
|
messages={this.state.messages}
|
|
|
|
selectedMessage={this.state.selectedMessage}
|
|
|
|
onConfirmedSignalChange={this.onConfirmedSignalChange}
|
|
|
|
onSeek={this.onSeek}
|
|
|
|
canFrameOffset={this.state.canFrameOffset}
|
|
|
|
firstCanTime={this.state.firstCanTime}
|
|
|
|
seekTime={this.state.seekTime}
|
|
|
|
seekIndex={this.state.seekIndex}
|
|
|
|
partsLoaded={this.state.currentParts}
|
|
|
|
/>
|
|
|
|
: null}
|
2017-06-27 17:28:42 -06:00
|
|
|
</div>
|
2017-06-20 16:46:19 -06:00
|
|
|
</div>
|
2017-06-13 18:40:05 -06:00
|
|
|
|
|
|
|
{this.state.showLoadDbc ? <LoadDbcModal
|
|
|
|
onDbcSelected={this.onDbcSelected}
|
2017-06-16 23:28:51 -06:00
|
|
|
onCancel={this.hideLoadDbc}
|
2017-06-28 21:28:39 -06:00
|
|
|
openDbcClient={this.openDbcClient}
|
|
|
|
/> : null}
|
2017-06-13 18:40:05 -06:00
|
|
|
{this.state.showSaveDbc ? <SaveDbcModal
|
|
|
|
dbc={this.state.dbc}
|
|
|
|
sourceDbcFilename={this.state.dbcFilename}
|
|
|
|
onDbcSaved={this.onDbcSaved}
|
2017-06-28 21:28:39 -06:00
|
|
|
onCancel={this.hideSaveDbc}
|
|
|
|
openDbcClient={this.openDbcClient} /> : null}
|
2017-06-19 21:40:07 -06:00
|
|
|
{this.state.showEditMessageModal ?
|
|
|
|
<EditMessageModal
|
|
|
|
onCancel={this.hideEditMessageModal}
|
2017-06-20 17:46:07 -06:00
|
|
|
onMessageFrameEdited={this.onMessageFrameEdited}
|
|
|
|
message={this.state.messages[this.state.editMessageModalMessage]} /> : null}
|
2017-06-13 18:40:05 -06:00
|
|
|
</div>);
|
2017-05-29 17:52:17 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const Styles = StyleSheet.create({
|
|
|
|
root: {
|
2017-06-27 17:28:42 -06:00
|
|
|
height: '100%',
|
|
|
|
},
|
|
|
|
content: {
|
|
|
|
flexDirection: 'row',
|
|
|
|
display: 'flex',
|
|
|
|
fontFamily: `apple-system, BlinkMacSystemFont,
|
|
|
|
"Segoe UI", "Roboto", "Oxygen",
|
|
|
|
"Ubuntu", "Cantarell", "Fira Sans",
|
|
|
|
"Droid Sans", "Helvetica Neue", sans-serif`,
|
|
|
|
height: '100%'
|
2017-06-20 16:46:19 -06:00
|
|
|
},
|
|
|
|
right: {
|
|
|
|
flex: 8,
|
2017-05-29 17:52:17 -06:00
|
|
|
}
|
|
|
|
});
|