added and all nodes option for the cards and graphs

This commit is contained in:
csawatzky 2026-05-29 12:03:56 -06:00
parent 99f37f7bda
commit c3ee4a518c
3 changed files with 141 additions and 39 deletions

View file

@ -1,5 +1,5 @@
import { AccessTime, MoreVert } from "@mui/icons-material";
import { Avatar, Box, Button, Card, Grid2, IconButton, Menu, MenuItem, Typography, useTheme } from "@mui/material";
import { Avatar, Box, Button, Card, Checkbox, FormControlLabel, Grid2, IconButton, Menu, MenuItem, Typography, useTheme } from "@mui/material";
import BinSensorCard from "bin/BinSensorCard";
import { GetDefaultDateRange } from "common/time/DateRange";
import { ExtractMoisture } from "grain";
@ -71,6 +71,7 @@ export default function BinSensorsDisplay(props: Props){
const defaultDateRange = GetDefaultDateRange();
const [startDate, setStartDate] = useState<Moment>(defaultDateRange.start);
const [endDate, setEndDate] = useState<Moment>(defaultDateRange.end);
const [filterNodes, setFilterNodes] = useState(true)
//organize the components into the correct 'positions' using the bins preferences
useEffect(()=>{
@ -303,21 +304,21 @@ export default function BinSensorsDisplay(props: Props){
const rows: { label: string; min: string; avg: string; max: string }[] = [
{
label: `Temp ${unit}`,
min: cable.minTemp(user.tempUnit()).toFixed(2),
avg: cable.aveTemp(user.tempUnit()).toFixed(2),
max: cable.maxTemp(user.tempUnit()).toFixed(2),
min: cable.minTemp(user.tempUnit(), filterNodes).toFixed(2),
avg: cable.aveTemp(user.tempUnit(), filterNodes).toFixed(2),
max: cable.maxTemp(user.tempUnit(), filterNodes).toFixed(2),
},
...(hasHumidity ? [{
label: "RH %",
min: cable.minHumidity().toFixed(2),
avg: cable.aveHumidity().toFixed(2),
max: cable.maxHumidity().toFixed(2),
min: cable.minHumidity(filterNodes).toFixed(2),
avg: cable.aveHumidity(filterNodes).toFixed(2),
max: cable.maxHumidity(filterNodes).toFixed(2),
}] : []),
...(hasMoisture ? [{
label: "EMC %",
min: cable.minMoisture().toFixed(2),
avg: cable.aveMoisture().toFixed(2),
max: cable.maxMoisture().toFixed(2),
min: cable.minMoisture(filterNodes).toFixed(2),
avg: cable.aveMoisture(filterNodes).toFixed(2),
max: cable.maxMoisture(filterNodes).toFixed(2),
}] : []),
]
@ -695,6 +696,20 @@ export default function BinSensorsDisplay(props: Props){
{showGraphs &&
<TimeBar updateDateRange={updateDateRange} startDate={startDate} endDate={endDate} />
}
{cables.length > 0 &&
<FormControlLabel
control={
<Checkbox
checked={!filterNodes}
onChange={(e, checked) => {
setFilterNodes(!filterNodes)
}}
/>
}
label={<Typography>Show All Nodes
</Typography>}
/>
}
</Box>
)
}
@ -718,11 +733,12 @@ export default function BinSensorsDisplay(props: Props){
<BinSensorGraph
icon={icon}
title={cable.name()}
tag={"Cable"}
tag={"Cable Averages"}
device={device}
component={cable.key()}
component={components.get(cable.key())!}
startDate={startDate}
endDate={endDate}
endDate={endDate}
allNodes={!filterNodes}
onMenuClick={(event) => {
setComponentUnnassigned(false);
setSelectedComponentType(cable.type());
@ -752,7 +768,7 @@ export default function BinSensorsDisplay(props: Props){
title={plenum.name()}
tag={"Plenum"}
device={device}
component={plenum.key()}
component={components.get(plenum.key())!}
startDate={startDate}
endDate={endDate}
onMenuClick={(event) => {
@ -783,7 +799,7 @@ export default function BinSensorsDisplay(props: Props){
title={pressure.name()}
tag={"Pressure"}
device={device}
component={pressure.key()}
component={components.get(pressure.key())!}
startDate={startDate}
endDate={endDate}
onMenuClick={(event) => {
@ -813,7 +829,7 @@ export default function BinSensorsDisplay(props: Props){
title={ambient.name()}
tag={"Ambient"}
device={device}
component={ambient.key()}
component={components.get(ambient.key())!}
startDate={startDate}
endDate={endDate}
onMenuClick={(event) => {
@ -844,7 +860,7 @@ export default function BinSensorsDisplay(props: Props){
title={h.name()}
tag={"Headspace"}
device={device}
component={h.key()}
component={components.get(h.key())!}
startDate={startDate}
endDate={endDate}
onMenuClick={(event) => {
@ -870,7 +886,7 @@ export default function BinSensorsDisplay(props: Props){
let icon = GetComponentIcon(co2.type(), co2.subType(), themeType)
return(
<Grid2 size={{xs: 6, sm: 6, md: 4, lg: 3}}>
<BinSensorGraph device={device} component={co2.key()} startDate={startDate} endDate={endDate} />
<BinSensorGraph device={device} component={components.get(co2.key())!} startDate={startDate} endDate={endDate} />
</Grid2>
)
}
@ -885,7 +901,7 @@ export default function BinSensorsDisplay(props: Props){
if(showGraphs && device){
return(
<Grid2 size={{xs: 6, sm: 6, md: 4, lg: 3}}>
<BinSensorGraph device={device} component={lidar.key()} startDate={startDate} endDate={endDate} />
<BinSensorGraph device={device} component={components.get(lidar.key())!} startDate={startDate} endDate={endDate} />
</Grid2>
)
}
@ -908,7 +924,7 @@ export default function BinSensorsDisplay(props: Props){
if(showGraphs && device){
return(
<Grid2 size={{xs: 6, sm: 6, md: 4, lg: 3}}>
<BinSensorGraph device={device} component={heater.key()} startDate={startDate} endDate={endDate} />
<BinSensorGraph device={device} component={components.get(heater.key())!} startDate={startDate} endDate={endDate} />
</Grid2>
)
}
@ -923,7 +939,7 @@ export default function BinSensorsDisplay(props: Props){
if(showGraphs && device){
return(
<Grid2 size={{xs: 6, sm: 6, md: 4, lg: 3}}>
<BinSensorGraph device={device} component={fan.key()} startDate={startDate} endDate={endDate} />
<BinSensorGraph device={device} component={components.get(fan.key())!} startDate={startDate} endDate={endDate} />
</Grid2>
)
}

View file

@ -1,8 +1,10 @@
import { Avatar, Box, Card, CardHeader, CircularProgress, IconButton, Paper, Typography, useTheme } from "@mui/material";
import { Avatar, Box, Card, CircularProgress, IconButton, Paper, Typography, useTheme } from "@mui/material";
import moment, { Moment } from "moment";
import { Component } from "models";
import { useComponentAPI, useGlobalState } from "providers";
import { useEffect, useMemo, useState } from "react";
import { UnitMeasurement } from "models/UnitMeasurement";
import { quack } from "protobuf-ts/quack";
import {
Legend,
Line,
@ -20,12 +22,47 @@ interface Props {
device: number;
icon?: string;
title?: string;
tag?: string;
tag?: string;
onMenuClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
component: string;
component: Component;
startDate: Moment;
endDate: Moment;
customHeight?: string | number;
/**
* this will cause all of the readings for a cable to be used in the average,
* by default it will filter any excluded nodes and only use nodes below the top node
*/
allNodes?: boolean
}
/** Grain-cable nodes to include when averaging (matches GrainCable.filter indexing). */
function valuesForAveraging(
nodeValues: number[],
component: Component,
allNodes?: boolean
): number[] {
if (allNodes) {
return nodeValues.filter(v => !isNaN(v));
}
const grainFilledTo = component.settings.grainFilledTo ?? 0;
const excludedNodes = component.settings.excludedNodes ?? [];
const included: number[] = [];
nodeValues.forEach((v, index) => {
if (isNaN(v)) {
return;
}
if (excludedNodes.includes(index)) {
return;
}
if (grainFilledTo > 0 && index >= grainFilledTo) {
return;
}
included.push(v);
});
return included;
}
interface MeasurementMeta {
@ -54,7 +91,11 @@ function normalizeValue(value: number, min: number, max: number): number {
return ((value - min) / (max - min)) * 100;
}
function buildChartData(measurements: UnitMeasurement[]): {
function buildChartData(
measurements: UnitMeasurement[],
component: Component,
allNodes?: boolean
): {
data: ChartPoint[];
series: SeriesInfo[];
} {
@ -66,7 +107,7 @@ function buildChartData(measurements: UnitMeasurement[]): {
if (valArray.error || !um.timestamps[i]) {
return;
}
const nodeValues = valArray.values.filter(v => !isNaN(v));
const nodeValues = valuesForAveraging(valArray.values, component, allNodes);
if (nodeValues.length === 0) {
return;
}
@ -137,7 +178,7 @@ function SensorGraphTooltip(props: TooltipProps<number, string>) {
}
export default function BinSensorGraph(props: Props) {
const { device, icon, title, tag, component, startDate, endDate, customHeight = 350, onMenuClick } = props;
const { device, icon, title, tag, component, startDate, endDate, customHeight = 350, onMenuClick, allNodes } = props;
const [measurements, setMeasurements] = useState<UnitMeasurement[]>([]);
const [loading, setLoading] = useState(false);
const componentAPI = useComponentAPI();
@ -148,7 +189,7 @@ export default function BinSensorGraph(props: Props) {
useEffect(() => {
setLoading(true);
componentAPI
.sampleUnitMeasurements(device, component, startDate, endDate, 1000)
.sampleUnitMeasurements(device, component.key(), startDate, endDate, 1000, undefined, undefined, undefined, undefined, true)
.then(res => {
setMeasurements(res.data.measurements.map(um => UnitMeasurement.any(um, user)));
})
@ -160,7 +201,10 @@ export default function BinSensorGraph(props: Props) {
});
}, [device, component, startDate, endDate, componentAPI, user]);
const { data, series } = useMemo(() => buildChartData(measurements), [measurements]);
const { data, series } = useMemo(
() => buildChartData(measurements, component, allNodes),
[measurements, component, allNodes]
);
return (
<Card raised sx={{ height: "100%", padding: 1.5 }}>
@ -170,7 +214,7 @@ export default function BinSensorGraph(props: Props) {
<Avatar
variant="square"
src={icon}
alt={name + " icon"}
alt={(title ?? "sensor") + " icon"}
style={{ width: theme.spacing(3), height: theme.spacing(3) }}
/>
}