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 graphsmain
parent
ef3ba96f18
commit
3eed6ea06c
|
@ -2,6 +2,9 @@
|
||||||
trigger:
|
trigger:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
|
pr:
|
||||||
|
- master
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: 'ubuntu-latest'
|
vmImage: 'ubuntu-latest'
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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", {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
};
|
||||||
|
|
Loading…
Reference in New Issue