cabana/src/models/graph-data.js

186 lines
5.6 KiB
JavaScript

import ArrayUtils from '../utils/array';
import { CAN_GRAPH_MAX_POINTS } from '../config';
function signalColors(signal, msg) {
return signal.colors.map((v) => (v ^ msg.address) ^ msg.bus);
}
function _calcGraphData(msg, signalUid, firstCanTime) {
if (!msg) return null;
const signal = Object.values(msg.frame.signals).find(
(s) => s.uid === signalUid
);
if (!signal) {
console.warn('_calcGraphData: no signal', signalUid, msg);
return null;
}
let samples = [];
const skip = Math.floor(msg.entries.length / CAN_GRAPH_MAX_POINTS);
if (skip === 0) {
samples = msg.entries;
} else {
for (let i = 0; i < msg.entries.length; i += skip) {
samples.push(msg.entries[i]);
}
// Always include last message entry, which faciliates graphData comparison
samples.push(msg.entries[msg.entries.length - 1]);
}
if (!samples.length) {
return [];
}
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
.filter((e) => e.signals[signal.name] !== undefined)
.map((entry) => {
if (entry.relTime < lastEntry) {
console.log(msg);
console.error('Found out of order messages');
debugger;
}
if (entry.relTime - lastEntry > 2) {
signalUid = Math.random().toString(36);
}
lastEntry = entry.relTime;
// console.log(entry.relTime - lastEntry);
return {
x: entry.time,
relTime: entry.relTime,
y: parseInt(entry.signals[signal.name]),
unit: signal.unit,
color: `rgba(${colors.join(',')}, 0.5)`,
signalName: signal.name,
signalUid
};
});
}
function appendNewGraphData(plottedSignals, graphData, messages, firstCanTime) {
const messagesPerPlot = plottedSignals.map((plottedMessages) => plottedMessages.reduce((messages, { messageId, signalUid }) => {
messages.push(messageId);
return messages;
}, []));
const extendedPlots = messagesPerPlot
.map((plottedMessageIds, index) => ({ plottedMessageIds, index })) // preserve index so we can look up graphData
.filter(({ plottedMessageIds, index }) => {
if (index < graphData.length) {
let maxGraphTime = 0;
const { series } = graphData[index];
if (series.length > 0) {
maxGraphTime = series[graphData[index].series.length - 1].relTime;
}
return plottedMessageIds.some(
(messageId) => (messages[messageId].entries.length > 0 && series.length === 0)
|| messages[messageId].entries.some((e) => e.relTime > maxGraphTime)
);
}
return false;
})
.map(({ plottedMessageIds, index }) => {
plottedMessageIds = plottedMessageIds.reduce((arr, messageId) => {
if (arr.indexOf(messageId) === -1) {
arr.push(messageId);
}
return arr;
}, []);
return { plottedMessageIds, index };
});
extendedPlots.forEach(({ plottedMessageIds, index }) => {
const signalUidsByMessageId = plottedSignals[index].reduce(
(obj, { messageId, signalUid }) => {
if (!obj[messageId]) {
obj[messageId] = [];
}
obj[messageId].push(signalUid);
return obj;
},
{}
);
const { series } = graphData[index];
const graphDataMaxMessageTimes = plottedMessageIds.reduce(
(obj, messageId) => {
const signalUids = signalUidsByMessageId[messageId];
const maxIndex = ArrayUtils.findIndexRight(
series,
(entry) => signalUids.indexOf(entry.signalUid) !== -1
);
if (maxIndex) {
obj[messageId] = series[maxIndex].relTime;
} else if (series.length > 0) {
obj[messageId] = series[series.length - 1].relTime;
} else {
// Graph data is empty
obj[messageId] = -1;
}
return obj;
},
{}
);
let newGraphData = [];
plottedMessageIds
.map((messageId) => ({ messageId, entries: messages[messageId].entries }))
.filter(
(
{ messageId, entries } // Filter to only messages with stale graphData
) => entries[entries.length - 1].relTime
> graphDataMaxMessageTimes[messageId]
)
.forEach(({ messageId, entries }) => {
// Compute and append new graphData
const firstNewEntryIdx = entries.findIndex(
(entry) => entry.relTime > graphDataMaxMessageTimes[messageId]
);
const newEntries = entries.slice(firstNewEntryIdx);
signalUidsByMessageId[messageId].forEach((signalUid) => {
const signalGraphData = _calcGraphData(
{
...messages[messageId],
entries: newEntries
},
signalUid,
firstCanTime
);
newGraphData = newGraphData.concat(signalGraphData);
});
});
const messageIdOutOfBounds = series.length > 0
&& plottedMessageIds.find(
(messageId) => messages[messageId].entries.length > 0
&& series[0].relTime < messages[messageId].entries[0].relTime
);
graphData[index] = {
series: graphData[index].series.concat(newGraphData),
updated: Date.now()
};
if (messageIdOutOfBounds) {
const graphDataLowerBound = graphData[index].series.findIndex(
(e) => e.relTime > messages[messageIdOutOfBounds].entries[0].relTime
);
if (graphDataLowerBound) {
graphData[index].series = graphData[index].series.slice(
graphDataLowerBound
);
}
}
});
return [...graphData];
}
export default { _calcGraphData, appendNewGraphData, signalColors };