Merge branch 'auto_detect_cable_summary' into dev_environment

This commit is contained in:
csawatzky 2025-04-29 12:09:58 -06:00
commit 68ec1217c6
4 changed files with 236 additions and 227 deletions

View file

@ -96,6 +96,7 @@ export default function DevicePage() {
} }
} }
}); });
setDiagnosticComponents(diagComponents)
setComponents(rComponents) setComponents(rComponents)
let interactions: Interaction[] = []; let interactions: Interaction[] = [];
resp.data.interactions.forEach((interaction: pond.Interaction) => { resp.data.interactions.forEach((interaction: pond.Interaction) => {

View file

@ -1,7 +1,7 @@
import { import {
GraphComponent, // GraphComponent,
GraphOrientation, // GraphOrientation,
GraphPoint, // GraphPoint,
GraphType, GraphType,
InteractionLine InteractionLine
} from "common/Graph"; } from "common/Graph";
@ -41,15 +41,15 @@ import {
Weight, Weight,
CapacitorCable CapacitorCable
} from "pbHelpers/ComponentTypes"; } from "pbHelpers/ComponentTypes";
import { multilineGrainCableData } from "pbHelpers/ComponentTypes/GrainCable"; //import { multilineGrainCableData } from "pbHelpers/ComponentTypes/GrainCable";
import { multilineCapCableData } from "./ComponentTypes/CapacitorCable"; //import { multilineCapCableData } from "./ComponentTypes/CapacitorCable";
import { findInteractionsAsSource } from "pbHelpers/Interaction"; import { findInteractionsAsSource } from "pbHelpers/Interaction";
import { pond } from "protobuf-ts/pond"; import { pond } from "protobuf-ts/pond";
import { quack } from "protobuf-ts/quack"; import { quack } from "protobuf-ts/quack";
import { notNull, or } from "utils/types"; import { notNull, or } from "utils/types";
import { emptyComponentId } from "./Component"; //import { emptyComponentId } from "./Component";
import { multilinePressureCableData } from "./ComponentTypes/PressureCable"; //import { multilinePressureCableData } from "./ComponentTypes/PressureCable";
import { describeMeasurement, MeasurementDescriber } from "./MeasurementDescriber"; import { describeMeasurement, MeasurementDescriber } from "./MeasurementDescriber";
import { convertedUnitMeasurement, MeasurementsFor, UnitMeasurement } from "models/UnitMeasurement"; import { convertedUnitMeasurement, MeasurementsFor, UnitMeasurement } from "models/UnitMeasurement";
import { Sen5x } from "./ComponentTypes/Sen5x"; import { Sen5x } from "./ComponentTypes/Sen5x";
@ -622,194 +622,194 @@ export function GetComponentIcon(
} }
//TODO: deprecated function, new graphs are handled in measurementChart (using reCharts instead of victory) //TODO: deprecated function, new graphs are handled in measurementChart (using reCharts instead of victory)
export function getGraphProps( // export function getGraphProps(
componentType: quack.ComponentType, // componentType: quack.ComponentType,
subtype: number, // subtype: number,
measurements: pond.Measurement[], // measurements: pond.Measurement[],
interactions: Interaction[], // interactions: Interaction[],
overlays: pond.ComponentOverlays[], // overlays: pond.ComponentOverlays[],
orientation: GraphOrientation, // orientation: GraphOrientation,
filters?: GraphFilters // filters?: GraphFilters
): any { // ): any {
const componentID: quack.IComponentID = quack.ComponentID.fromObject( // const componentID: quack.IComponentID = quack.ComponentID.fromObject(
or(measurements[0].measurement, { // or(measurements[0].measurement, {
id: emptyComponentId() // id: emptyComponentId()
}).id as any // }).id as any
); // );
let graphProps = { // let graphProps = {
orientation: orientation // orientation: orientation
} as any; // } as any;
let ext = extension(componentType, subtype); // let ext = extension(componentType, subtype);
ext.measurements.forEach((componentMeasurement: ComponentMeasurement, index: number) => { // ext.measurements.forEach((componentMeasurement: ComponentMeasurement, index: number) => {
let describer = describeMeasurement( // let describer = describeMeasurement(
componentMeasurement.measurementType, // componentMeasurement.measurementType,
componentType, // componentType,
subtype // subtype
); // );
let range = // let range =
filters && filters.ranges && filters.ranges[index] ? filters.ranges[index] : undefined; // filters && filters.ranges && filters.ranges[index] ? filters.ranges[index] : undefined;
let selectedInteractions = // let selectedInteractions =
filters && filters.selectedInteractions ? filters.selectedInteractions : []; // filters && filters.selectedInteractions ? filters.selectedInteractions : [];
let interactionLines = getInteractionLines( // let interactionLines = getInteractionLines(
componentType, // componentType,
subtype, // subtype,
componentMeasurement.measurementType, // componentMeasurement.measurementType,
componentID, // componentID,
interactions, // interactions,
selectedInteractions // selectedInteractions
); // );
let selectedOverlays = filters?.selectedOverlays ?? []; // let selectedOverlays = filters?.selectedOverlays ?? [];
let overlayAreas = getOverlayAreas( // let overlayAreas = getOverlayAreas(
componentMeasurement.measurementType, // componentMeasurement.measurementType,
overlays, // overlays,
selectedOverlays // selectedOverlays
); // );
graphProps["graphComponent" + (index + 1).toString()] = { // graphProps["graphComponent" + (index + 1).toString()] = {
label: componentMeasurement.label, // label: componentMeasurement.label,
data: [], // data: [],
tick: describer.unit(), // tick: describer.unit(),
colour: componentMeasurement.colour, // colour: componentMeasurement.colour,
type: componentMeasurement.graphType, // type: componentMeasurement.graphType,
range: range, // range: range,
isBoolean: // isBoolean:
componentMeasurement.measurementType === quack.MeasurementType.MEASUREMENT_TYPE_BOOLEAN, // componentMeasurement.measurementType === quack.MeasurementType.MEASUREMENT_TYPE_BOOLEAN,
isPercent: // isPercent:
componentMeasurement.measurementType === quack.MeasurementType.MEASUREMENT_TYPE_PERCENT || // componentMeasurement.measurementType === quack.MeasurementType.MEASUREMENT_TYPE_PERCENT ||
(componentMeasurement.measurementType === quack.MeasurementType.MEASUREMENT_TYPE_ANALOG && // (componentMeasurement.measurementType === quack.MeasurementType.MEASUREMENT_TYPE_ANALOG &&
subtype === quack.AnalogInputSubtype.ANALOG_INPUT_SUBTYPE_FUEL), // subtype === quack.AnalogInputSubtype.ANALOG_INPUT_SUBTYPE_FUEL),
interactionLines: interactionLines, // interactionLines: interactionLines,
states: ext.states, // states: ext.states,
decimals: describer.decimals(), // decimals: describer.decimals(),
overlays: overlayAreas // overlays: overlayAreas
} as GraphComponent; // } as GraphComponent;
}); // });
//for new cables add here to get the data for multiline // //for new cables add here to get the data for multiline
if (showMultilineCable(componentType, filters)) { // if (showMultilineCable(componentType, filters)) {
let cableData: any = {}; // let cableData: any = {};
if (componentType === quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE) { // if (componentType === quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE) {
cableData = multilineGrainCableData(measurements, filters); // cableData = multilineGrainCableData(measurements, filters);
} else if (componentType === quack.ComponentType.COMPONENT_TYPE_PRESSURE_CABLE) { // } else if (componentType === quack.ComponentType.COMPONENT_TYPE_PRESSURE_CABLE) {
cableData = multilinePressureCableData(measurements, filters); // cableData = multilinePressureCableData(measurements, filters);
} else if (componentType === quack.ComponentType.COMPONENT_TYPE_CAPACITOR_CABLE) { // } else if (componentType === quack.ComponentType.COMPONENT_TYPE_CAPACITOR_CABLE) {
cableData = multilineCapCableData(measurements, filters); // cableData = multilineCapCableData(measurements, filters);
} // }
//will loop through the keys in cableData // //will loop through the keys in cableData
//IMPORTANT keys must be data1, data2, etc in the multiline function in the component // //IMPORTANT keys must be data1, data2, etc in the multiline function in the component
for (let i = 1; i <= Object.keys(cableData).length; i++) { // for (let i = 1; i <= Object.keys(cableData).length; i++) {
let gc = "graphComponent" + i; // let gc = "graphComponent" + i;
if (graphProps[gc]) { // if (graphProps[gc]) {
graphProps[gc].data = cableData["data" + i]; // graphProps[gc].data = cableData["data" + i];
graphProps[gc].type = GraphType.MULTILINE; // graphProps[gc].type = GraphType.MULTILINE;
} // }
} // }
} else { // } else {
measurements.forEach((reading: pond.Measurement, readIndex) => { // measurements.forEach((reading: pond.Measurement, readIndex) => {
if (notNull(reading) && notNull(reading.timestamp) && notNull(reading.measurement)) { // if (notNull(reading) && notNull(reading.timestamp) && notNull(reading.measurement)) {
let date = new Date(Date.parse(reading.timestamp)); // let date = new Date(Date.parse(reading.timestamp));
for (let index = 0; index < ext.measurements.length; index++) { // for (let index = 0; index < ext.measurements.length; index++) {
let componentMeasurement: ComponentMeasurement = ext.measurements[index]; // let componentMeasurement: ComponentMeasurement = ext.measurements[index];
if (componentMeasurement.isErrorMeasurement(reading.measurement)) { // if (componentMeasurement.isErrorMeasurement(reading.measurement)) {
break; // break;
} // }
let dataPoint = null; // let dataPoint = null;
switch (componentMeasurement.graphType) { // switch (componentMeasurement.graphType) {
case GraphType.AREA: // case GraphType.AREA:
let avg = componentMeasurement.extract( // let avg = componentMeasurement.extract(
or(reading.measurement, {} as quack.Measurement), // or(reading.measurement, {} as quack.Measurement),
filters // filters
); // );
if (avg && avg.low !== null && avg.high !== null) { // if (avg && avg.low !== null && avg.high !== null) {
dataPoint = { // dataPoint = {
x: date, // x: date,
y: avg.low, // y: avg.low,
y0: avg.high // y0: avg.high
} as GraphPoint; // } as GraphPoint;
} // }
break; // break;
case GraphType.SCATTER: // case GraphType.SCATTER:
let scatter = componentMeasurement.extract( // let scatter = componentMeasurement.extract(
or(reading.measurement, {} as quack.Measurement), // or(reading.measurement, {} as quack.Measurement),
filters // filters
); // );
if (scatter && scatter.value !== undefined && scatter.value !== null) { // if (scatter && scatter.value !== undefined && scatter.value !== null) {
dataPoint = { // dataPoint = {
x: date, // x: date,
y: Number(scatter.value), // y: Number(scatter.value),
bubble: or(scatter.bubble, 0) // bubble: or(scatter.bubble, 0)
} as GraphPoint; // } as GraphPoint;
} // }
break; // break;
case GraphType.RADAR: // case GraphType.RADAR:
if (subtype === quack.AnalogInputSubtype.ANALOG_INPUT_SUBTYPE_WIND_DIRECTION) { // if (subtype === quack.AnalogInputSubtype.ANALOG_INPUT_SUBTYPE_WIND_DIRECTION) {
let value = componentMeasurement.extract( // let value = componentMeasurement.extract(
or(reading.measurement, {} as quack.Measurement), // or(reading.measurement, {} as quack.Measurement),
filters // filters
); // );
dataPoint = { // dataPoint = {
x: getWindDirection(value).dString, // x: getWindDirection(value).dString,
y: getWindDirection(value).direction // y: getWindDirection(value).direction
}; // };
} // }
break; // break;
default: // default:
//linear and bar // //linear and bar
let value = componentMeasurement.extract( // let value = componentMeasurement.extract(
or(reading.measurement, {} as quack.Measurement), // or(reading.measurement, {} as quack.Measurement),
filters // filters
); // );
if (value !== undefined && value !== null) { // if (value !== undefined && value !== null) {
if (componentType === quack.ComponentType.COMPONENT_TYPE_ANALOG_INPUT) { // if (componentType === quack.ComponentType.COMPONENT_TYPE_ANALOG_INPUT) {
if (subtype === quack.AnalogInputSubtype.ANALOG_INPUT_SUBTYPE_FUEL) { // if (subtype === quack.AnalogInputSubtype.ANALOG_INPUT_SUBTYPE_FUEL) {
if (value > 100) { // if (value > 100) {
value = 0; // value = 0;
} // }
} // }
} // }
if (componentType === quack.ComponentType.COMPONENT_TYPE_EDGE_TRIGGERED) { // if (componentType === quack.ComponentType.COMPONENT_TYPE_EDGE_TRIGGERED) {
if (subtype === quack.EdgeTriggeredSubtype.EDGE_TRIGGERED_SUBTYPE_WIND_SPEED) { // if (subtype === quack.EdgeTriggeredSubtype.EDGE_TRIGGERED_SUBTYPE_WIND_SPEED) {
if (readIndex < measurements.length - 1) { // if (readIndex < measurements.length - 1) {
//do math // //do math
//get the time of both the current reading and the reading before it // //get the time of both the current reading and the reading before it
let currentTime = new Date( // let currentTime = new Date(
Date.parse(measurements[readIndex].timestamp) // Date.parse(measurements[readIndex].timestamp)
).valueOf(); // ).valueOf();
let prevTime = new Date( // let prevTime = new Date(
Date.parse(measurements[readIndex + 1].timestamp) // Date.parse(measurements[readIndex + 1].timestamp)
).valueOf(); // ).valueOf();
//get the difference in seconds between the two // //get the difference in seconds between the two
let deltaTime = (currentTime - prevTime) / 1000; // let deltaTime = (currentTime - prevTime) / 1000;
//divide the ticks by the time between intervals for the average ticks per second and multiply // //divide the ticks by the time between intervals for the average ticks per second and multiply
//by 2.4 to convert to km/h // //by 2.4 to convert to km/h
value = (Number(value) / deltaTime) * 2.4; // value = (Number(value) / deltaTime) * 2.4;
} else { // } else {
value = 0; // value = 0;
} // }
} // }
if (subtype === quack.EdgeTriggeredSubtype.EDGE_TRIGGERED_SUBTYPE_RAIN) { // if (subtype === quack.EdgeTriggeredSubtype.EDGE_TRIGGERED_SUBTYPE_RAIN) {
value = Math.round(value * 0.2794); // value = Math.round(value * 0.2794);
} // }
} // }
dataPoint = { // dataPoint = {
x: date, // x: date,
y: Number(value) // y: Number(value)
} as GraphPoint; // } as GraphPoint;
} // }
break; // break;
} // }
if (dataPoint !== null) { // if (dataPoint !== null) {
graphProps["graphComponent" + (index + 1).toString()].data.push(dataPoint); // graphProps["graphComponent" + (index + 1).toString()].data.push(dataPoint);
} // }
} // }
} // }
}); // });
} // }
return graphProps; // return graphProps;
} // }
function getWindDirection(value: number) { function getWindDirection(value: number) {
//determine the general direction based on the value //determine the general direction based on the value

View file

@ -4,8 +4,8 @@ import GrainCableLightIcon from "assets/components/grainCableLight.png";
import TemperatureHumidityDarkIcon from "assets/components/temperatureHumidityDark.png"; import TemperatureHumidityDarkIcon from "assets/components/temperatureHumidityDark.png";
import TemperatureHumidityLightIcon from "assets/components/temperatureHumidityLight.png"; import TemperatureHumidityLightIcon from "assets/components/temperatureHumidityLight.png";
import { ExtractMoisture, grainName } from "grain"; import { ExtractMoisture, grainName } from "grain";
import { GraphPoint } from "common/Graph"; //import { GraphPoint } from "common/Graph";
import moment from "moment"; //import moment from "moment";
import { import {
AreaChartData, AreaChartData,
ComponentMeasurement, ComponentMeasurement,
@ -418,38 +418,38 @@ export function binSplitAt(filters?: GraphFilters): number {
} }
//TODO: deprecated function with new measurements //TODO: deprecated function with new measurements
export function multilineGrainCableData( // export function multilineGrainCableData(
measurements: Array<pond.Measurement>, // measurements: Array<pond.Measurement>,
filters?: GraphFilters // filters?: GraphFilters
) { // ) {
let temperature: Array<Array<GraphPoint>> = []; // let temperature: Array<Array<GraphPoint>> = [];
let humidity: Array<Array<GraphPoint>> = []; //humidity is not being used by the graphs // let humidity: Array<Array<GraphPoint>> = []; //humidity is not being used by the graphs
let moisture: Array<Array<GraphPoint>> = []; // let moisture: Array<Array<GraphPoint>> = [];
measurements.forEach((measurement: pond.Measurement, i) => { // measurements.forEach((measurement: pond.Measurement, i) => {
let nodes = extractNodes(measurement.measurement); // let nodes = extractNodes(measurement.measurement);
let selectedNodes = filters ? or(filters.selectedNodes, []) : []; // let selectedNodes = filters ? or(filters.selectedNodes, []) : [];
for (let j = 0; j < selectedNodes.length; j++) { // for (let j = 0; j < selectedNodes.length; j++) {
if (i === 0) { // if (i === 0) {
temperature[j] = []; // temperature[j] = [];
humidity[j] = []; // humidity[j] = [];
moisture[j] = []; // moisture[j] = [];
} // }
let nodeIndex = selectedNodes[j]; // let nodeIndex = selectedNodes[j];
if (nodeIndex < nodes.length) { // if (nodeIndex < nodes.length) {
let node = nodes[nodeIndex]; // let node = nodes[nodeIndex];
let ts = moment(measurement.timestamp); // let ts = moment(measurement.timestamp);
temperature[j].push({ x: ts, y: node.temperature }); // temperature[j].push({ x: ts, y: node.temperature });
humidity[j].push({ x: ts, y: node.humidity }); // humidity[j].push({ x: ts, y: node.humidity });
let grain = // let grain =
isBinSplit(filters) && nodeIndex >= binSplitAt(filters) // isBinSplit(filters) && nodeIndex >= binSplitAt(filters)
? pond.Grain.GRAIN_NONE // ? pond.Grain.GRAIN_NONE
: or(filters, { grainType: pond.Grain.GRAIN_NONE }).grainType; // : or(filters, { grainType: pond.Grain.GRAIN_NONE }).grainType;
moisture[j].push({ // moisture[j].push({
x: ts, // x: ts,
y: ExtractMoisture(grain, node.temperature, node.humidity) // y: ExtractMoisture(grain, node.temperature, node.humidity)
}); // });
} // }
} // }
}); // });
return { data1: temperature, data2: moisture, data3: humidity }; // return { data1: temperature, data2: moisture, data3: humidity };
} // }

View file

@ -110,6 +110,10 @@ export class MeasurementDescriber {
} }
if (componentType === quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE) { if (componentType === quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE) {
this.details.graph = GraphType.AREA; this.details.graph = GraphType.AREA;
if(componentSubtype === quack.GrainCableSubtype.GRAIN_CABLE_SUBTYPE_OPI_DIAG){
this.details.label = "Cable ID"
this.details.unit = ""
}
} }
if (componentType === quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT) { if (componentType === quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT) {
this.details.path = "vpd.celciusTimes10"; this.details.path = "vpd.celciusTimes10";
@ -194,6 +198,10 @@ export class MeasurementDescriber {
if (componentType === quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE) { if (componentType === quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE) {
this.details.label = "Humidity"; this.details.label = "Humidity";
this.details.graph = GraphType.AREA; this.details.graph = GraphType.AREA;
if(componentSubtype === quack.GrainCableSubtype.GRAIN_CABLE_SUBTYPE_OPI_DIAG){
this.details.label = "Cable Type"
this.details.unit = ""
}
// can be used for testing node splitting on a cable if you dont have a component that uses it // can be used for testing node splitting on a cable if you dont have a component that uses it
// this.details.nodeDetails = { // this.details.nodeDetails = {
// colours: ["white", "black"], // colours: ["white", "black"],