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-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-05-29 17:52:17 -06:00
|
|
|
|
|
|
|
export default class CanExplorer extends Component {
|
|
|
|
static propTypes = {
|
2017-06-13 18:40:05 -06:00
|
|
|
dongleId: PropTypes.string,
|
|
|
|
routeName: PropTypes.string,
|
|
|
|
dbc: PropTypes.instanceOf(DBC)
|
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-13 18:40:05 -06:00
|
|
|
partsLoaded: 0,
|
|
|
|
showLoadDbc: false,
|
|
|
|
showSaveDbc: false,
|
|
|
|
dbc: null,
|
|
|
|
dbcFilename: null,
|
|
|
|
dbcLastSaved: null
|
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);
|
|
|
|
|
|
|
|
this.onDbcSelected = this.onDbcSelected.bind(this);
|
|
|
|
this.onDbcSaved = this.onDbcSaved.bind(this);
|
|
|
|
this.onConfirmedSignalChange = this.onConfirmedSignalChange.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];
|
|
|
|
|
|
|
|
if(this.props.dbc !== undefined) {
|
2017-06-13 19:28:08 -06:00
|
|
|
this.setState({dbc: this.props.dbc, dbcFilename: 'acura_ilx_2016.dbc', route}, () => {
|
2017-06-14 20:07:02 -06:00
|
|
|
this.spawnWorker(0, 2)
|
2017-06-13 18:40:05 -06:00
|
|
|
});
|
|
|
|
}
|
|
|
|
this.setState({route})
|
|
|
|
}
|
|
|
|
})
|
2017-05-29 17:52:17 -06:00
|
|
|
}
|
|
|
|
|
2017-06-13 18:40:05 -06:00
|
|
|
onDbcSelected(filename, dbcInstance) {
|
|
|
|
this.hideLoadDbc();
|
|
|
|
this.setState({dbc: dbcInstance, dbcFilename: filename}, () => {
|
|
|
|
const {route} = this.state;
|
|
|
|
|
|
|
|
// Pass DBC text to webworker b/c can't pass instance of es6 class
|
2017-06-14 20:07:02 -06:00
|
|
|
this.spawnWorker(0, 2);
|
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-13 18:40:05 -06:00
|
|
|
updateCanFrameOffset(firstCanPart, firstCanPartEntries) {
|
|
|
|
/*
|
|
|
|
firstCanPart is >= 0
|
|
|
|
firstCanPartEntries is an array of entries
|
|
|
|
*/
|
|
|
|
const firstCanTime = firstCanPartEntries[0].time;
|
|
|
|
const firstPartLastCanTime = firstCanPartEntries[firstCanPartEntries.length - 1].time;
|
2017-05-29 17:52:17 -06:00
|
|
|
|
2017-06-13 18:40:05 -06:00
|
|
|
const canFrameOffsetFloat = (60 * firstCanPart
|
|
|
|
- (60 - (firstPartLastCanTime - firstCanTime)));
|
|
|
|
const canFrameOffset = Math.round(canFrameOffsetFloat);
|
|
|
|
|
|
|
|
this.setState({canFrameOffset, firstCanTime});
|
|
|
|
}
|
|
|
|
|
|
|
|
earliestCanEntries(messages) {
|
|
|
|
const messagesSortedByStartTime = Object.values(messages).sort(
|
|
|
|
(msg1, msg2) => {
|
|
|
|
const firstMessageFirstTime = msg1.entries[0].time;
|
|
|
|
const secondMessageFirstTime = msg2.entries[0].time;
|
|
|
|
if(firstMessageFirstTime < secondMessageFirstTime) {
|
|
|
|
return -1;
|
|
|
|
} else if(firstMessageFirstTime === secondMessageFirstTime) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return messagesSortedByStartTime[0].entries;
|
|
|
|
}
|
|
|
|
|
|
|
|
spawnWorker(part, max) {
|
|
|
|
var worker = new CanFetcher();
|
|
|
|
|
|
|
|
worker.onmessage = (e) => {
|
|
|
|
const {messages} = this.state;
|
|
|
|
// Ensure signal consistency with DBC state.
|
|
|
|
// possible that during worker run, local DBC changed
|
|
|
|
// so we might have to re-parse the part.
|
|
|
|
|
|
|
|
const newMessages = e.data;
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(Object.keys(newMessages).length > 0 && this.state.canFrameOffset < 0) {
|
|
|
|
// this part has messages, and we haven't encountered a can part yet
|
|
|
|
this.updateCanFrameOffset(part, this.earliestCanEntries(newMessages));
|
|
|
|
}
|
|
|
|
this.setState({messages,
|
|
|
|
partsLoaded: this.state.partsLoaded + 1}, () => {
|
|
|
|
if(part < max) {
|
|
|
|
this.spawnWorker(part + 1, max);
|
|
|
|
}
|
2017-05-29 17:52:17 -06:00
|
|
|
})
|
2017-06-13 18:40:05 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
worker.postMessage({dbcText: this.state.dbc.text(),
|
|
|
|
base: this.state.route.url,
|
|
|
|
num: part,
|
|
|
|
canStartTime: this.state.firstCanTime});
|
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;
|
|
|
|
const {dbc} = this.state;
|
|
|
|
|
|
|
|
dbc.setSignals(message.address, message.signals);
|
|
|
|
this.setState({dbc});
|
|
|
|
|
|
|
|
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;
|
|
|
|
this.setState({messages})
|
|
|
|
}
|
|
|
|
|
|
|
|
worker.postMessage({message,
|
|
|
|
dbcText: dbc.text(),
|
|
|
|
canStartTime: this.state.firstCanTime});
|
2017-05-29 17:52:17 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
return (<div className={css(Styles.root)}>
|
2017-06-13 18:40:05 -06:00
|
|
|
<Meta url={this.state.route.url}
|
2017-05-29 17:52:17 -06:00
|
|
|
messages={this.state.messages}
|
2017-06-13 18:40:05 -06:00
|
|
|
partsLoaded={this.state.partsLoaded}
|
|
|
|
partsCount={this.state.route ? this.state.route.proclog : 0}
|
|
|
|
onMessageSelected={(msg) => {this.setState({selectedMessage: msg})}}
|
|
|
|
showLoadDbc={this.showLoadDbc}
|
|
|
|
showSaveDbc={this.showSaveDbc}
|
|
|
|
dbcFilename={this.state.dbcFilename}
|
|
|
|
dbcLastSaved={this.state.dbcLastSaved} />
|
2017-06-14 18:30:50 -06:00
|
|
|
{Object.keys(this.state.messages).length > 0
|
|
|
|
&& this.state.selectedMessage ?
|
2017-06-13 18:40:05 -06:00
|
|
|
<Explorer
|
|
|
|
url={this.state.route.url}
|
2017-05-29 17:52:17 -06:00
|
|
|
messages={this.state.messages}
|
2017-06-13 18:40:05 -06:00
|
|
|
selectedMessage={this.state.selectedMessage}
|
|
|
|
partsLoaded={this.state.partsLoaded}
|
|
|
|
onConfirmedSignalChange={this.onConfirmedSignalChange}
|
|
|
|
canFrameOffset={this.state.canFrameOffset}
|
|
|
|
firstCanTime={this.state.firstCanTime} /> : null}
|
|
|
|
|
|
|
|
{this.state.showLoadDbc ? <LoadDbcModal
|
|
|
|
onDbcSelected={this.onDbcSelected}
|
|
|
|
onCancel={this.hideLoadDbc} /> : null}
|
|
|
|
{this.state.showSaveDbc ? <SaveDbcModal
|
|
|
|
dbc={this.state.dbc}
|
|
|
|
sourceDbcFilename={this.state.dbcFilename}
|
|
|
|
onDbcSaved={this.onDbcSaved}
|
|
|
|
onCancel={this.hideSaveDbc} /> : null}
|
|
|
|
</div>);
|
2017-05-29 17:52:17 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const Styles = StyleSheet.create({
|
|
|
|
root: {
|
|
|
|
flexDirection: 'row',
|
|
|
|
display: 'flex',
|
|
|
|
}
|
|
|
|
});
|