cabana/src/CanExplorer.js

377 lines
13 KiB
JavaScript
Raw Normal View History

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-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';
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");
const CanOffsetFinder = require('./workers/can-offset-finder.worker.js');
import debounce from './utils/debounce';
import EditMessageModal from './components/EditMessageModal';
2017-06-27 17:28:42 -06:00
import LoadingBar from './components/LoadingBar';
import {persistDbc} from './api/localstorage';
import OpenDbc from './api/opendbc';
export default class CanExplorer extends Component {
static propTypes = {
dongleId: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
dbc: PropTypes.instanceOf(DBC),
dbcFilename: PropTypes.string,
githubAuthToken: PropTypes.string
};
constructor(props) {
super(props);
this.state = {
messages: {},
2017-06-13 18:40:05 -06:00
route: {},
canFrameOffset: -1,
firstCanTime: null,
selectedMessage: null,
currentParts: [0,0],
2017-06-13 18:40:05 -06:00
showLoadDbc: false,
showSaveDbc: false,
showEditMessageModal: false,
editMessageModalMessage: null,
dbc: new DBC(),
dbcFilename: 'New DBC',
dbcLastSaved: null,
seekTime: 0,
seekIndex: 0,
2017-06-27 17:28:42 -06:00
maxByteStateChangeCount: 0,
2017-06-28 21:38:08 -06:00
isLoading: true
};
this.openDbcClient = new OpenDbc(props.githubAuthToken);
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.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);
this.onPartChange = this.onPartChange.bind(this);
2017-06-20 17:46:07 -06:00
this.onMessageFrameEdited = this.onMessageFrameEdited.bind(this);
this.onSeek = this.onSeek.bind(this);
this.onMessageSelected = this.onMessageSelected.bind(this);
this.initCanData = this.initCanData.bind(this);
}
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];
const newState = {route, currentParts: [0,2]};
2017-06-13 18:40:05 -06:00
if(this.props.dbc !== undefined) {
newState.dbc = this.props.dbc;
newState.dbcFilename = this.props.dbcFilename;
}
this.setState(newState, this.initCanData);
2017-06-13 18:40:05 -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
this.spawnWorker(this.state.currentParts);
2017-06-13 19:28:08 -06:00
});
}
2017-06-13 18:40:05 -06:00
onDbcSaved(dbcFilename) {
const dbcLastSaved = Moment();
this.setState({dbcLastSaved, dbcFilename})
this.hideSaveDbc();
}
spawnWorker(parts, part, prevMsgEntries) {
2017-06-27 17:28:42 -06:00
if(!this.state.isLoading) {
this.setState({isLoading: true});
}
const [minPart, maxPart] = parts;
if(part === undefined) {
part = minPart;
}
if(prevMsgEntries === undefined) {
prevMsgEntries = {};
}
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) => {
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
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);
messages[key].frame = this.state.dbc.messages.get(messages[key].address);
2017-06-13 18:40: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}, () => {
if(part < maxPart) {
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-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,
canStartTime: firstCanTime,
prevMsgEntries
});
}
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, dbcFilename, route} = this.state;
2017-06-13 18:40:05 -06:00
dbc.setSignals(message.address, message.signals);
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});
}
onPartChange = debounce((part) => {
let {currentParts} = this.state;
const currentPartSpan = currentParts[1] - currentParts[0] + 1;
currentParts = [part, part + currentPartSpan - 1];
this.setState({currentParts, selectedMessage: null, messages: {}}, () => {
this.spawnWorker(currentParts);
});
}, 500);
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);
}
this.setState({showEditMessageModal: true,
2017-06-20 17:46:07 -06:00
editMessageModalMessage: msgKey,
messages: this.state.messages});
}
hideEditMessageModal() {
this.setState({showEditMessageModal: false});
}
2017-06-20 17:46:07 -06:00
onMessageFrameEdited(messageFrame) {
const {messages,
route,
dbcFilename,
dbc,
editMessageModalMessage} = this.state;
const message = Object.assign({}, messages[editMessageModalMessage]);
message.frame = messageFrame;
dbc.messages.set(messageFrame.address, messageFrame);
persistDbc(route.fullname,
{dbcFilename, dbc});
2017-06-20 17:46:07 -06:00
messages[editMessageModalMessage] = message;
2017-06-20 17:46:07 -06:00
this.setState({messages});
this.hideEditMessageModal();
}
onSeek(seekIndex, seekTime) {
this.setState({seekIndex, seekTime});
}
onMessageSelected(msgKey) {
let {seekTime, seekIndex, messages} = this.state;
const msg = messages[msgKey];
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});
}
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}
messages={this.state.messages}
2017-06-27 17:28:42 -06:00
partsLoaded={this.state.currentParts}
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}
seekTime={this.state.seekTime}
maxByteStateChangeCount={this.state.maxByteStateChangeCount}
githubAuthToken={this.props.githubAuthToken} />
2017-06-27 17:28:42 -06:00
<div className={css(Styles.right)}>
{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>
</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}
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}
onCancel={this.hideSaveDbc}
openDbcClient={this.openDbcClient} /> : null}
{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>);
}
}
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%'
},
right: {
flex: 8,
}
});