Fix cabana graphs and upgrade react-vega (#22)

* Fix cabana graphs by upgrading react-vega

* Fix test cases

* Ensure tests on prs

* Remove actions menu from graphs
main
Chris Vickery 2019-09-25 15:31:59 -07:00 committed by GitHub
parent ef3ba96f18
commit 3eed6ea06c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 1093 additions and 359 deletions

View File

@ -2,6 +2,9 @@
trigger: trigger:
- master - master
pr:
- master
pool: pool:
vmImage: 'ubuntu-latest' vmImage: 'ubuntu-latest'

View File

@ -46,19 +46,21 @@
"react-infinite": "^0.11.0", "react-infinite": "^0.11.0",
"react-list": "^0.8.6", "react-list": "^0.8.6",
"react-measure": "^2.0.2", "react-measure": "^2.0.2",
"react-scripts": "1.0.17", "react-scripts": "^1.1.2",
"react-test-renderer": "^16.2.0", "react-test-renderer": "^16.2.0",
"react-vega": "^3.0.0", "react-vega": "^7.0.0",
"react-visibility-sensor": "^3.10.1", "react-visibility-sensor": "^3.10.1",
"right-pad": "^1.0.1", "right-pad": "^1.0.1",
"simple-statistics": "^4.1.0", "simple-statistics": "^4.1.0",
"socket.io-client": "^2.0.3", "socket.io-client": "^2.0.3",
"stream-selector": "^0.1.1", "stream-selector": "^0.1.1",
"streamsaver": "^1.0.1", "streamsaver": "^1.0.1",
"vega": "3.0.10", "vega": "^5.3.4",
"vega-lite": "^3.0.0",
"vega-tooltip": "^0.4.0" "vega-tooltip": "^0.4.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/plugin-transform-regenerator": "^7.4.5",
"connect-history-api-fallback": "1.3.0", "connect-history-api-fallback": "1.3.0",
"cross-spawn": "4.0.2", "cross-spawn": "4.0.2",
"detect-port": "1.1.0", "detect-port": "1.1.0",

View File

@ -2,10 +2,11 @@ import React, { Component } from "react";
import Measure from "react-measure"; import Measure from "react-measure";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import cx from "classnames"; import cx from "classnames";
import { Vega } from "react-vega";
import Signal from "../models/can/signal"; import Signal from "../models/can/signal";
import GraphData from "../models/graph-data"; import GraphData from "../models/graph-data";
import CanPlot from "../vega/CanPlot"; import CanPlotSpec from "../vega/CanPlot";
import debounce from "../utils/debounce"; import debounce from "../utils/debounce";
const DefaultPlotInnerStyle = { const DefaultPlotInnerStyle = {
@ -106,19 +107,27 @@ export default class CanGraph extends Component {
} }
onPlotResize({ bounds }) { onPlotResize({ bounds }) {
this.setState({ bounds }); if (bounds) {
this.setState({ bounds });
} else {
bounds = this.state.bounds;
}
if (!this.view) {
console.log("Cannot bounds");
return;
}
this.view.signal("width", bounds.width - 70); this.view.signal("width", bounds.width - 70);
this.view.signal("height", 0.4 * (bounds.width - 70)); // 5:2 aspect ratio this.view.signal("height", 0.4 * (bounds.width - 70)); // 5:2 aspect ratio
this.view.run(); this.view.run();
} }
shouldComponentUpdate(nextProps, nextState) { componentWillUpdate(nextProps, nextState) {
if (this.view) { if (this.view) {
let segmentChanged = this.segmentIsNew(nextProps.segment);
this.view.runAfter(() => { this.view.runAfter(() => {
// only update if segment is new // only update if segment is new
let segmentChanged = false; if (segmentChanged) {
if (this.segmentIsNew(nextProps.segment)) {
if (nextProps.segment.length > 0) { if (nextProps.segment.length > 0) {
// Set segmented domain // Set segmented domain
this.view.signal("segment", nextProps.segment); this.view.signal("segment", nextProps.segment);
@ -141,14 +150,17 @@ export default class CanGraph extends Component {
this.view.runAsync(); this.view.runAsync();
} }
}); });
return false;
} }
return true; return true;
} }
insertData = debounce(() => { insertData = debounce(() => {
if (!this.view) {
console.log("Cannot insertData");
return;
}
let { series } = this.state.data; let { series } = this.state.data;
// adding plot points by diff isn't faster since it basically has to be n^2 // adding plot points by diff isn't faster since it basically has to be n^2
@ -198,7 +210,7 @@ export default class CanGraph extends Component {
this.view = view; this.view = view;
if (this.state.bounds) { if (this.state.bounds) {
this.onPlotResize({ bounds: this.state.bounds }); this.onPlotResize();
} }
if (this.props.segment.length > 0) { if (this.props.segment.length > 0) {
view.signal("segment", this.props.segment); view.signal("segment", this.props.segment);
@ -220,6 +232,11 @@ export default class CanGraph extends Component {
this.props.onSegmentChanged(this.props.messageId, segment); this.props.onSegmentChanged(this.props.messageId, segment);
if (!this.view) {
console.log("Cannot insertData");
return;
}
this.view.runAfter(() => { this.view.runAfter(() => {
const state = this.view.getState(); const state = this.view.getState();
state.subcontext[0].signals.brush = 0; state.subcontext[0].signals.brush = 0;
@ -336,12 +353,19 @@ export default class CanGraph extends Component {
ref={measureRef} ref={measureRef}
className="cabana-explorer-visuals-plot-container" className="cabana-explorer-visuals-plot-container"
> >
<CanPlot <Vega
logLevel={1}
onNewView={this.onNewView} onNewView={this.onNewView}
onSignalClickTime={this.onSignalClickTime} logLevel={1}
onSignalSegment={this.onSignalSegment} signalListeners={{
clickTime: this.onSignalClickTime,
segment: this.onSignalSegment
}}
renderer={"canvas"} renderer={"canvas"}
spec={CanPlotSpec}
actions={false}
data={{
values: this.state.data.series
}}
/> />
</div> </div>
); );

View File

@ -11,7 +11,6 @@ import Entries from "../models/can/entries";
import debounce from "../utils/debounce"; import debounce from "../utils/debounce";
import PartSelector from "./PartSelector"; import PartSelector from "./PartSelector";
import PlaySpeedSelector from "./PlaySpeedSelector"; import PlaySpeedSelector from "./PlaySpeedSelector";
import GraphData from "../models/graph-data";
export default class Explorer extends Component { export default class Explorer extends Component {
static propTypes = { static propTypes = {
@ -33,7 +32,6 @@ export default class Explorer extends Component {
this.state = { this.state = {
plottedSignals: [], plottedSignals: [],
graphData: [],
segment: [], segment: [],
segmentIndices: [], segmentIndices: [],
shouldShowAddSignal: true, shouldShowAddSignal: true,
@ -56,7 +54,6 @@ export default class Explorer extends Component {
this.onSignalPlotChange = this.onSignalPlotChange.bind(this); this.onSignalPlotChange = this.onSignalPlotChange.bind(this);
this._onKeyDown = this._onKeyDown.bind(this); this._onKeyDown = this._onKeyDown.bind(this);
this.mergePlots = this.mergePlots.bind(this); this.mergePlots = this.mergePlots.bind(this);
this.refreshGraphData = this.refreshGraphData.bind(this);
this.toggleShouldShowAddSignal = this.toggleShouldShowAddSignal.bind(this); this.toggleShouldShowAddSignal = this.toggleShouldShowAddSignal.bind(this);
this.changePlaySpeed = this.changePlaySpeed.bind(this); this.changePlaySpeed = this.changePlaySpeed.bind(this);
} }
@ -107,7 +104,7 @@ export default class Explorer extends Component {
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
const nextMessage = nextProps.messages[nextProps.selectedMessage]; const nextMessage = nextProps.messages[nextProps.selectedMessage];
const curMessage = this.props.messages[this.props.selectedMessage]; const curMessage = this.props.messages[this.props.selectedMessage];
let { plottedSignals, graphData } = this.state; let { plottedSignals } = this.state;
if (Object.keys(nextProps.messages).length === 0) { if (Object.keys(nextProps.messages).length === 0) {
this.resetSegment(); this.resetSegment();
@ -183,43 +180,6 @@ export default class Explorer extends Component {
); );
this.setState({ segment, segmentIndices }); this.setState({ segment, segmentIndices });
} }
const partsDidChange =
JSON.stringify(nextProps.currentParts) !==
JSON.stringify(this.props.currentParts);
if (plottedSignals.length > 0) {
if (graphData.length !== plottedSignals.length || partsDidChange) {
this.refreshGraphData(nextProps.messages, plottedSignals);
} else if (graphData.length === plottedSignals.length) {
if (
plottedSignals.some(plot =>
plot.some(({ messageId, signalUid }) => {
/* const signalName = Object.values(
* this.props.messages[messageId].frame.signals
* ).find(s => s.uid === signalUid);
*/
return (
nextProps.messages[messageId].entries.length > 0 &&
this.props.messages[messageId].entries.length > 0 &&
nextProps.messages[messageId].entries[0].updated !==
this.props.messages[messageId].entries[0].updated
);
})
)
) {
this.refreshGraphData(nextProps.messages, plottedSignals);
} else {
graphData = GraphData.appendNewGraphData(
plottedSignals,
graphData,
nextProps.messages,
nextProps.firstCanTime
);
this.setState({ graphData });
}
}
}
} }
changePlaySpeed(value) { changePlaySpeed(value) {
@ -248,56 +208,12 @@ export default class Explorer extends Component {
} else return ""; } else return "";
} }
sortGraphData(graphData) {
return graphData.sort((entry1, entry2) => {
if (entry1.relTime < entry2.relTime) {
return -1;
} else if (entry1.relTime > entry2.relTime) {
return 1;
} else {
return 0;
}
});
}
calcGraphData(plottedSignals, messages) {
const { firstCanTime } = this.props;
if (typeof messages === "undefined") {
messages = this.props.messages;
}
const series = this.sortGraphData(
plottedSignals
.map(({ messageId, signalUid }) =>
GraphData._calcGraphData(messages[messageId], signalUid, firstCanTime)
)
.reduce((combined, signalData) => combined.concat(signalData), [])
);
return { series, updated: Date.now() };
}
onSignalPlotPressed(messageId, signalUid) { onSignalPlotPressed(messageId, signalUid) {
let { plottedSignals, graphData } = this.state; let { plottedSignals } = this.state;
graphData = [this.calcGraphData([{ messageId, signalUid }]), ...graphData];
plottedSignals = [[{ messageId, signalUid }], ...plottedSignals]; plottedSignals = [[{ messageId, signalUid }], ...plottedSignals];
this.setState({ plottedSignals, graphData }); this.setState({ plottedSignals });
}
refreshGraphData(messages, plottedSignals) {
if (typeof messages === "undefined") {
messages = this.props.messages;
}
if (typeof plottedSignals === "undefined") {
plottedSignals = this.state.plottedSignals;
}
let graphData = plottedSignals.map((plotSignals, index) =>
this.calcGraphData(plotSignals, messages)
);
this.setState({ graphData });
} }
onSignalUnplotPressed(messageId, signalUid) { onSignalUnplotPressed(messageId, signalUid) {
@ -311,10 +227,7 @@ export default class Explorer extends Component {
) )
.filter(plot => plot.length > 0); .filter(plot => plot.length > 0);
this.setState( this.setState({ plottedSignals: newPlottedSignals });
{ plottedSignals: newPlottedSignals },
this.refreshGraphData(this.props.messages, newPlottedSignals)
);
} }
updateSegment = debounce((messageId, segment) => { updateSegment = debounce((messageId, segment) => {
@ -526,9 +439,9 @@ export default class Explorer extends Component {
} }
mergePlots({ fromPlot, toPlot }) { mergePlots({ fromPlot, toPlot }) {
let { plottedSignals, graphData } = this.state; let { plottedSignals } = this.state;
// remove fromPlot from plottedSignals, graphData // remove fromPlot from plottedSignals
const fromPlotIdx = plottedSignals.findIndex(plot => const fromPlotIdx = plottedSignals.findIndex(plot =>
plot.some( plot.some(
signal => signal =>
@ -537,10 +450,6 @@ export default class Explorer extends Component {
) )
); );
plottedSignals.splice(fromPlotIdx, 1); plottedSignals.splice(fromPlotIdx, 1);
graphData.splice(fromPlotIdx, 1);
// calc new graph data
const newGraphData = this.calcGraphData([fromPlot, toPlot]);
const toPlotIdx = plottedSignals.findIndex(plot => const toPlotIdx = plottedSignals.findIndex(plot =>
plot.some( plot.some(
@ -549,10 +458,9 @@ export default class Explorer extends Component {
signal.messageId === toPlot.messageId signal.messageId === toPlot.messageId
) )
); );
graphData[toPlotIdx] = newGraphData;
plottedSignals[toPlotIdx] = [fromPlot, toPlot]; plottedSignals[toPlotIdx] = [fromPlot, toPlot];
this.setState({ graphData, plottedSignals }); this.setState({ plottedSignals });
} }
render() { render() {

View File

@ -1,6 +1,4 @@
import { createClassFromSpec } from "react-vega"; export default {
export default createClassFromSpec("CanPlot", {
$schema: "https://vega.github.io/schema/vega/v3.0.json", $schema: "https://vega.github.io/schema/vega/v3.0.json",
width: 400, width: 400,
height: 200, height: 200,
@ -335,4 +333,4 @@ export default createClassFromSpec("CanPlot", {
] ]
} }
] ]
}); };

1281
yarn.lock

File diff suppressed because it is too large Load Diff