Refactor can plot spec, fix charts not receiving new data

main
Chris Vickery 2019-11-06 14:14:26 -08:00
parent eb37fcc7e1
commit 79035c5990
5 changed files with 406 additions and 263 deletions

View File

@ -566,7 +566,7 @@ export default class CanExplorer extends Component {
// handles merging the data in correct order
options = options || {};
const { messages } = this.state;
const messages = { ...this.state.messages };
Object.keys(newMessages).forEach((key) => {
// add message

View File

@ -0,0 +1,10 @@
/* eslint-env jest */
import { demoLogUrls, demoProps, demoRoute } from '../demo';
import { getLogPart } from '../api/rlog';
describe('demo fixtures', () => {
it('demo log urls are valid', async () => {
const data = await getLogPart(demoLogUrls[0]);
expect(data.statusCode).toBe(200);
});
});

View File

@ -165,7 +165,7 @@ export default class Signal {
}
getColors(messageId) {
const parts = messageId.split(':').map((p) => Number.parseInt(p, 16) % 255);
const parts = messageId.split(':').map((p) => ((3 + Number.parseInt(p, 16)) * 3) % 253);
const colors = this._colors || this.generateColors();
return colors.map((c) => parts.reduce((m, v) => m ^ v, c));

View File

@ -32,6 +32,7 @@ function _calcGraphData(msg, signalUid, firstCanTime) {
}
const colors = signal.getColors(msg.id);
signalUid = msg.id + signalUid;
// sorting these doesn't fix the phantom lines
let lastEntry = samples[0].relTime;
return samples

View File

@ -1,340 +1,472 @@
export default {
$schema: 'https://vega.github.io/schema/vega/v5.6.json',
width: 400,
height: 200,
padding: {
top: 5,
left: 30,
right: 5,
bottom: 10
"$schema": "https://vega.github.io/schema/vega/v5.json",
"width": 400,
"height": 200,
"padding": {
"top": 5,
"left": 30,
"right": 5,
"bottom": 10
},
signals: [
"data": [
{
name: 'tipTime',
on: [
"name": "table"
},
{
"name": "segment"
},
{
"name": "tooltip",
"source": "table",
"transform": [
{
events: 'mousemove',
update: "invert('xrelscale', x())"
}
]
},
{
name: 'clickTime',
on: [
{
events: 'click',
update: "invert('xrelscale', x())"
}
]
},
{ name: 'videoTime' },
{
name: 'segment',
value: { data: 'table', field: 'relTime' }
}
],
data: [
{
name: 'table'
},
{
name: 'segment'
},
{
name: 'tooltip',
source: 'table',
transform: [
{
type: 'filter',
expr: 'abs(datum.relTime - tipTime) <= 0.01'
"type": "filter",
"expr": "abs(datum.relTime - tipTime) <= 0.05"
},
{
type: 'aggregate',
fields: ['relTime', 'y', 'unit'],
ops: ['min', 'argmin', 'argmin'],
as: ['min', 'argmin', 'argmin']
"type": "aggregate",
"groupby": ["color"],
"fields": [
"relTime",
"y",
"unit"
],
"ops": [
"min",
"argmin",
"argmin"
],
"as": [
"min",
"argmin",
"argmin"
]
}
]
},
{
name: 'ySegmentScale',
source: 'table',
transform: [
"name": "ySegmentScale",
"source": "table",
"transform": [
{
type: 'filter',
expr:
'length(segment) != 2 || (datum.relTime >= segment[0] && datum.relTime <= segment[1])'
"type": "filter",
"expr": "length(segment) != 2 || (datum.relTime >= segment[0] && datum.relTime <= segment[1])"
},
{ type: 'extent', field: 'y', signal: 'ySegment' }
{
"type": "extent",
"field": "y",
"signal": "ySegment"
}
]
}
],
scales: [
"scales": [
{
name: 'xscale',
type: 'linear',
range: 'width',
domain: { data: 'table', field: 'x' },
zero: false
"name": "xscale",
"type": "linear",
"range": "width",
"domain": {
"data": "table",
"field": "x"
},
"zero": false
},
{
name: 'xrelscale',
type: 'linear',
range: 'width',
domain: { data: 'table', field: 'relTime' },
zero: false,
clamp: true,
domainRaw: { signal: 'segment' }
"name": "xrelscale",
"type": "linear",
"range": "width",
"domain": {
"data": "table",
"field": "relTime"
},
"zero": false,
"clamp": true,
"nice": true,
"domainRaw": {
"signal": "segment"
}
},
{
name: 'yscale',
type: 'linear',
range: 'height',
clamp: true,
zero: false,
domain: { signal: 'ySegment' }
"name": "yscale",
"type": "linear",
"range": "height",
"clamp": true,
"zero": false,
"nice": true,
"domain": {
"signal": "ySegment"
}
},
{
name: 'color',
type: 'ordinal',
domain: { data: 'table', field: 'color' },
range: { data: 'table', field: 'color' }
"name": "color",
"type": "ordinal",
"domain": {
"data": "table",
"field": "color"
},
"range": {
"data": "table",
"field": "color"
}
}
],
axes: [
{ orient: 'bottom', scale: 'xrelscale', labelOverlap: true },
{ orient: 'left', scale: 'yscale' }
],
marks: [
"axes": [
{
type: 'group',
name: 'plot',
interactive: true,
encode: {
enter: {
width: { signal: 'width' },
height: { signal: 'height' },
fill: { value: 'transparent' }
"orient": "bottom",
"scale": "xrelscale",
"labelOverlap": true
},
{
"orient": "left",
"scale": "yscale"
}
],
"signals": [
{
"name": "tipTime",
"on": [
{
"events": "mousemove",
"update": "invert('xrelscale', x())"
},
{
"events": "mouseout",
"update": "null"
}
]
},
{
"name": "clickTime",
"on": [
{
"events": "click",
"update": "invert('xrelscale', x())"
}
]
},
{
"name": "videoTime"
},
{
"name": "segment",
"value": {
"data": "table",
"field": "relTime"
}
}
],
"marks": [
{
"type": "group",
"name": "plot",
"interactive": true,
"encode": {
"enter": {
"width": {
"signal": "width"
},
"height": {
"signal": "height"
},
"fill": {
"value": "transparent"
}
}
},
signals: [
"signals": [
{
name: 'brush',
value: 0,
on: [
"name": "brush",
"value": 0,
"on": [
{
events: '@boundingRect:mousedown',
update: '[x(), x()]'
"events": "@boundingRect:mousedown",
"update": "[x(), x()]"
},
{
events:
'[@boundingRect:mousedown, window:mouseup] > window:mousemove!',
update: '[brush[0], clamp(x(), 0, width)]'
"events": "[@boundingRect:mousedown, window:mouseup] > window:mousemove!",
"update": "[brush[0], clamp(x(), 0, width)]"
},
{
events: { signal: 'delta' },
update:
'clampRange([anchor[0] + delta, anchor[1] + delta], 0, width)'
"events": {
"signal": "delta"
},
"update": "clampRange([anchor[0] + delta, anchor[1] + delta], 0, width)"
}
]
},
{
name: 'anchor',
value: null,
on: [{ events: '@brush:mousedown', update: 'slice(brush)' }]
},
{
name: 'xdown',
value: 0,
on: [{ events: '@brush:mousedown', update: 'x()' }]
},
{
name: 'xup',
value: 0,
on: [{ events: '@brush:mouseup', update: 'x()' }]
},
{
name: 'delta',
value: 0,
on: [
"name": "anchor",
"value": null,
"on": [
{
events: '[@brush:mousedown, window:mouseup] > window:mousemove!',
update: 'x() - xdown'
"events": "@brush:mousedown",
"update": "slice(brush)"
}
]
},
{
name: 'segment',
push: 'outer',
on: [
"name": "xdown",
"value": 0,
"on": [
{
events: 'window:mouseup',
update:
"span(brush) && span(brush) > 15 ? invert('xrelscale', brush) : segment"
"events": "@brush:mousedown",
"update": "x()"
}
]
},
{
"name": "xup",
"value": 0,
"on": [
{
"events": "@brush:mouseup",
"update": "x()"
}
]
},
{
"name": "delta",
"value": 0,
"on": [
{
"events": "[@brush:mousedown, window:mouseup] > window:mousemove!",
"update": "x() - xdown"
}
]
},
{
"name": "segment",
"push": "outer",
"on": [
{
"events": "window:mouseup",
"update": "span(brush) && span(brush) > 15 ? invert('xrelscale', brush) : segment"
}
]
}
],
marks: [
"marks": [
{
type: 'group',
from: {
facet: {
name: 'series',
data: 'table',
groupby: 'signalUid'
"type": "group",
"from": {
"facet": {
"name": "series",
"data": "table",
"groupby": "signalUid"
}
},
marks: {
type: 'line',
name: 'lineMark',
from: { data: 'series' },
interactive: true,
encode: {
update: {
interpolate: { value: 'step' },
x: { scale: 'xrelscale', field: 'relTime' },
y: { scale: 'yscale', field: 'y' }
},
hover: {
fillOpacity: { value: 0.5 }
},
enter: {
clip: { value: true },
stroke: { scale: 'color', field: 'color' },
strokeWidth: { value: 2 }
}
}
}
},
{
type: 'rect',
interactive: true,
name: 'brush',
encode: {
enter: {
y: { value: 0 },
height: { signal: 'height' },
fill: { value: '#333' },
fillOpacity: { value: 0.2 }
"marks": [{
"type": "line",
"name": "lineMark",
"from": {
"data": "series"
},
update: {
x: { signal: 'brush[0]' },
x2: { signal: 'brush[1]' }
"interactive": true,
"encode": {
"update": {
"interpolate": {
"value": "natural",
"tension": 1
},
"x": {
"scale": "xrelscale",
"field": "relTime"
},
"y": {
"scale": "yscale",
"field": "y"
},
"detail": {
"field": "signalUid"
}
},
"hover": {
"fillOpacity": {
"value": 0.5
}
},
"enter": {
"clip": {
"value": true
},
"stroke": {
"scale": "color",
"field": "color"
},
"strokeWidth": {
"value": 2
}
}
}
}
}]
},
// {
// "type": "rect",
// "interactive": false,
// "encode": {
// "enter": {
// "y": {"value": 0},
// "height": {"value": 200},
// "width": {"value": 2},
// "fill": {"value": "firebrick"}
// },
// "update": {
// "x": {"signal": "brush[0]"}
// }
// }
// },
// {
// "type": "rect",
// "interactive": false,
// "encode": {
// "enter": {
// "y": {"value": 0},
// "height": {"value": 200},
// "width": {"value": 2},
// "fill": {"value": "firebrick"}
// },
// "update": {
// "x": {"signal": "brush[1]"}
// }
// }
// },
{
type: 'rule',
encode: {
update: {
y: { value: 0 },
y2: { field: { group: 'height' } },
stroke: { value: '#000' },
strokeWidth: { value: 2 },
x: {
scale: 'xrelscale',
signal: 'videoTime',
offset: 0.5
"type": "rect",
"interactive": true,
"name": "brush",
"encode": {
"enter": {
"y": {
"value": 0
},
"height": {
"signal": "height"
},
"fill": {
"value": "#333"
},
"fillOpacity": {
"value": 0.2
}
},
"update": {
"x": {
"signal": "brush[0]"
},
"x2": {
"signal": "brush[1]"
}
}
}
},
{
type: 'symbol',
from: { data: 'tooltip' },
encode: {
update: {
x: { scale: 'xrelscale', field: 'argmin.relTime' },
y: { scale: 'yscale', field: 'argmin.y' },
size: { value: 50 },
fill: { value: 'black' }
"type": "rule",
"encode": {
"update": {
"y": {
"value": 0
},
"y2": {
"field": {
"group": "height"
}
},
"stroke": {
"value": "#000"
},
"strokeWidth": {
"value": 2
},
"x": {
"scale": "xrelscale",
"signal": "videoTime",
"offset": 0.5
}
}
}
},
{
type: 'group',
from: { data: 'tooltip' },
interactive: false,
name: 'tooltipGroup',
encode: {
update: {
x: [
"type": "symbol",
"from": {
"data": "tooltip"
},
"encode": {
"update": {
"x": {
"scale": "xrelscale",
"field": "argmin.relTime"
},
"y": {
"scale": "yscale",
"field": "argmin.y"
},
"size": {
"value": 50
},
"fill": {
"value": "black"
}
}
}
},
{
"type": "group",
"from": {
"data": "tooltip"
},
"interactive": false,
"name": "tooltipGroup",
"encode": {
"update": {
"x": [
{
test:
"inrange(datum.argmin.relTime + 80, domain('xrelscale'))",
scale: 'xrelscale',
field: 'argmin.relTime'
"test": "inrange(datum.argmin.relTime + 80, domain('xrelscale'))",
"scale": "xrelscale",
"field": "argmin.relTime"
},
{ scale: 'xrelscale', field: 'argmin.relTime', offset: -80 }
{
"scale": "xrelscale",
"field": "argmin.relTime",
"offset": -80
}
],
y: { scale: 'yscale', field: 'argmin.y' },
height: { value: 20 },
width: { value: 80 },
fill: { value: '#fff' },
fillOpacity: { value: 0.85 },
stroke: { value: '#aaa' },
strokeWidth: { value: 0.5 }
"y": {
"scale": "yscale",
"field": "argmin.y"
},
"height": {
"value": 20
},
"width": {
"value": 80
},
"fill": {
"value": "#fff"
},
"fillOpacity": {
"value": 0.85
},
"stroke": {
"value": "#aaa"
},
"strokeWidth": {
"value": 0.5
}
}
},
marks: [
"marks": [
{
type: 'text',
interactive: false,
encode: {
update: {
text: {
signal:
"format(parent.argmin.relTime, ',.2f') + ': ' + format(parent.argmin.y, ',.2f') + ' ' + parent.argmin.unit"
"type": "text",
"interactive": false,
"encode": {
"update": {
"text": {
"signal": "format(parent.argmin.relTime, ',.2f') + ': ' + format(parent.argmin.y, ',.2f') + ' ' + parent.argmin.unit"
},
fill: { value: 'black' },
fontWeight: { value: 'bold' },
y: { value: 20 }
"fill": {
"value": "black"
},
"fontWeight": {
"value": "bold"
},
"y": {
"value": 20
}
}
}
}
]
},
{
type: 'rect',
name: 'boundingRect',
interactive: true,
encode: {
enter: {
width: { signal: 'width' },
height: { signal: 'height' },
fill: { value: 'transparent' }
"type": "rect",
"name": "boundingRect",
"interactive": true,
"encode": {
"enter": {
"width": {
"signal": "width"
},
"height": {
"signal": "height"
},
"fill": {
"value": "transparent"
}
}
}
}