From a65dbe88201d40c7a063b24267fdf424fc0627e2 Mon Sep 17 00:00:00 2001 From: csawatzky Date: Fri, 15 May 2026 15:49:53 -0600 Subject: [PATCH] finished work on the table, made the legend, and hooked up the edits for the temp/moisture targets --- src/bin/3dView/Systems/Cables/GrainCable.tsx | 2 +- .../3dView/Systems/Heatmap/NodePointCloud.tsx | 2 +- .../3dView/Systems/Heatmap/TempHeatMap.tsx | 2 +- src/bin/binSummary/BinSummary.tsx | 7 +- src/bin/binSummary/components/binDetails.tsx | 56 +++- .../binSummary/components/binTableView.tsx | 243 ++++++++++++++++-- src/models/Bin.ts | 5 +- 7 files changed, 282 insertions(+), 35 deletions(-) diff --git a/src/bin/3dView/Systems/Cables/GrainCable.tsx b/src/bin/3dView/Systems/Cables/GrainCable.tsx index 62ca26f..71ba6bd 100644 --- a/src/bin/3dView/Systems/Cables/GrainCable.tsx +++ b/src/bin/3dView/Systems/Cables/GrainCable.tsx @@ -43,7 +43,7 @@ export default function GrainCable(props: Props) { {nodes.map((node, i) => ( - + ))} ) diff --git a/src/bin/3dView/Systems/Heatmap/NodePointCloud.tsx b/src/bin/3dView/Systems/Heatmap/NodePointCloud.tsx index 1af8ba8..3d096b4 100644 --- a/src/bin/3dView/Systems/Heatmap/NodePointCloud.tsx +++ b/src/bin/3dView/Systems/Heatmap/NodePointCloud.tsx @@ -73,7 +73,7 @@ export default function NodePointCloud(props: Props) { .filter(n => n.inGrain && !n.excluded) .map(n => { const lower = bin.lowerTempThreshold(); - const upper = bin.upperTempThreshold(); + const upper = bin.targetTemp(); if (n.celcius >= lower && n.celcius <= upper) return null; diff --git a/src/bin/3dView/Systems/Heatmap/TempHeatMap.tsx b/src/bin/3dView/Systems/Heatmap/TempHeatMap.tsx index 8e70a79..dfacfce 100644 --- a/src/bin/3dView/Systems/Heatmap/TempHeatMap.tsx +++ b/src/bin/3dView/Systems/Heatmap/TempHeatMap.tsx @@ -129,7 +129,7 @@ export default function TempHeatMap({ bin, nodes, flatMaxY }: Props) { const binRadius = bin.diameter() / 2; const sidewallHeight = bin.sidewallHeight(); const hopperHeight = bin.hopperHeight() ?? 0; - const upperThreshold = bin.upperTempThreshold(); + const upperThreshold = bin.targetTemp(); const sidewallBaseY = -sidewallHeight / 2; const hopperTipY = sidewallBaseY - hopperHeight; diff --git a/src/bin/binSummary/BinSummary.tsx b/src/bin/binSummary/BinSummary.tsx index 6327cba..51d3c1d 100644 --- a/src/bin/binSummary/BinSummary.tsx +++ b/src/bin/binSummary/BinSummary.tsx @@ -11,6 +11,7 @@ import { useEffect, useState } from "react"; import { Controller } from "models/Controller"; import { GrainCable } from "models/GrainCable"; import { pond } from "protobuf-ts/pond"; +import BinDetails from "./components/binDetails"; interface Props { bin: Bin @@ -156,7 +157,7 @@ export default function BinSummary(props: Props){ - + - - + + diff --git a/src/bin/binSummary/components/binDetails.tsx b/src/bin/binSummary/components/binDetails.tsx index f7a9c2c..1ef4952 100644 --- a/src/bin/binSummary/components/binDetails.tsx +++ b/src/bin/binSummary/components/binDetails.tsx @@ -1,17 +1,65 @@ -import { Box } from "@mui/material"; +import { Box, Tab, Tabs } from "@mui/material"; +import React from "react"; +import { useState } from "react"; +import BinTableView from "./binTableView"; +import { Bin } from "models"; interface Props { - + bin: Bin + updateBinCallback?: (bin: Bin) => void } -export default function BinDetails (props: Props) { +interface TabPanelProps { + children?: React.ReactNode; + index: any; + value: any; +} +function TabPanelMine(props: TabPanelProps) { + const { children, value, index, ...other } = props; + + return ( + + ); + } + +export default function BinDetails (props: Props) { + const {bin, updateBinCallback} = props + const [currentTab, setCurrentTab] = useState(0) //this is where we will have the tabs abd tab panels for the table view, alerts, interactions, and analysis return ( - + setCurrentTab(value)} + indicatorColor="primary" + textColor="primary" + variant="fullWidth"> + + + + + + + + + + + + + + + + + ) } \ No newline at end of file diff --git a/src/bin/binSummary/components/binTableView.tsx b/src/bin/binSummary/components/binTableView.tsx index 94721dd..72d49c3 100644 --- a/src/bin/binSummary/components/binTableView.tsx +++ b/src/bin/binSummary/components/binTableView.tsx @@ -1,14 +1,47 @@ -import { Box, Table, TableBody, TableCell, TableHead, TableRow } from "@mui/material"; -import { green, grey, orange, red } from "@mui/material/colors"; +import { Edit, GppGood, Save } from "@mui/icons-material"; +import { Box, darken, IconButton, Table, TableBody, TableCell, TableHead, TableRow, TextField, Theme, Typography } from "@mui/material"; +import { green, grey, orange, red, yellow } from "@mui/material/colors"; +import { makeStyles } from "@mui/styles"; +import HumidityIcon from "component/HumidityIcon"; +import TemperatureIcon from "component/TemperatureIcon"; +import { cloneDeep } from "lodash"; import { Bin } from "models"; import { pond } from "protobuf-ts/pond"; import { useGlobalState } from "providers"; -import React from "react"; +import React, { useEffect, useState } from "react"; import { useMemo } from "react"; import { avg, celsiusToFahrenheit } from "utils"; +const useStyles = makeStyles((theme: Theme) => ({ + tableStyle: { + borderCollapse: "collapse", + }, + headerCellStyle: { + fontWeight: 650, + backgroundColor: darken(theme.palette.background.paper, 0.2), + fontSize: 15, + textAlign: "center", + border: "1px solid black" + }, + cellStyle: { + padding: 2, + fontWeight: 650, + fontSize: 17, + textAlign: "center", + border: "1px solid black", + + }, + thresholdCell: { + fontWeight: 650, + fontSize: 15, + textAlign: "center", + } + }) +); + interface Props { bin: Bin + updateBinCallback?: (bin: Bin) => void } interface BinLevel { @@ -20,8 +53,29 @@ interface BinLevel { export default function BinTableView(props: Props) { - const {bin} = props + const classes = useStyles() + const {bin, updateBinCallback} = props const [{user}] = useGlobalState() + const [enterTemp, setEnterTemp] = useState(false) + const [enterMoisture, setEnterMoisture] = useState(false) + const [tempTarget, setTempTarget] = useState(0) + const [tempEntry, setTempEntry] = useState("") + const [moistureEntry, setMoistureEntry] = useState("") + const [moistureTarget, setMoistureTarget] = useState(0) + const inBounds = green[600] + const caution = yellow[800] + const warning = red[700] + + useEffect(()=>{ + let val = bin.targetTemp() + if(user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT){ + val = bin.targetTemp() * (9/5) + 32 + } + setTempTarget(val) + setTempEntry(val.toFixed(2)) + setMoistureTarget(bin.targetMoisture()) + setMoistureEntry(bin.targetMoisture().toFixed(2)) + },[bin]) const binLevels = useMemo(() => { const cables = bin.status.grainCables @@ -89,21 +143,20 @@ export default function BinTableView(props: Props) { let moistureDisplay = "--" let lvlTemp let lvlMoisture - let tempColour: string = grey[600] - let moistureColour: string = grey[600] + let tempColour: string = "" + let moistureColour: string = "" //determine the temp if(level.grainTemps.length > 0){ - console.log(level.grainTemps) lvlTemp = avg(level.grainTemps) //if the average temp is 5 degrees above the bins threshold - if(lvlTemp > (bin.upperTempThreshold() + 5)){ - tempColour = red[700] - }else if(lvlTemp > bin.upperTempThreshold()){ + if(lvlTemp > (bin.targetTemp() + 5)){ + tempColour = warning + }else if(lvlTemp > bin.targetTemp()){ //if the average temp is greater than 5 degrees over - tempColour = orange[600] + tempColour = caution }else { - tempColour = green[300] + tempColour = inBounds } }else if (level.airTemps.length > 0){ lvlTemp = avg(level.airTemps) @@ -112,19 +165,19 @@ export default function BinTableView(props: Props) { if(user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT){ lvlTemp = celsiusToFahrenheit(lvlTemp) } - tempDisplay = lvlTemp.toFixed(2) + (user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT ? " F" : " C") + tempDisplay = lvlTemp.toFixed(2) + (user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT ? " °F" : " °C") } //determine the moisture to show if(level.grainMoistures.length > 0){ lvlMoisture = avg(level.grainMoistures) if(lvlMoisture > (bin.targetMoisture() + 1.5)){ - moistureColour = red[700] + moistureColour = warning }else if(lvlMoisture > bin.targetMoisture()){ //if the average temp is greater than 5 degrees over - moistureColour = orange[600] + moistureColour = caution }else { - moistureColour = green[300] + moistureColour = inBounds } }else if (level.airMoistures.length > 0){ lvlMoisture = avg(level.airMoistures) @@ -134,33 +187,177 @@ export default function BinTableView(props: Props) { } return ( - {tempDisplay} - {moistureDisplay} + {tempDisplay} + {moistureDisplay} ) } + const update = () => { + let temp = tempTarget + if(user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT){ + //convert to c + temp = (temp - 32) * (5 / 9); + + } + let clone = cloneDeep(bin) + if(clone.settings.inventory && updateBinCallback){ + clone.settings.inventory.targetTemperature = temp + clone.settings.inventory.targetMoisture = moistureTarget + updateBinCallback(clone) + } + } + + const tempThreshDisplay = () => { + if(user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT){ + return tempTarget.toFixed(2) + " °F" + } + return tempTarget.toFixed(2) + " °C" + } + + const LegendSwatch = ({ color }: { color: string }) => ( + + ) + + + const updateTempEntry = () => { + return ( + { + let val = parseFloat(e.target.value) + if(!isNaN(val)){ + setTempTarget(val) + } + setTempEntry(e.target.value) + }} + /> + ) + } + + const updateMoistureEntry = () => { + return ( + { + let val = parseFloat(e.target.value) + if(!isNaN(val)){ + setMoistureTarget(val) + } + setMoistureEntry(e.target.value) + }} + /> + ) + } + return ( - + {/* Level Table */} +
- Level - Temperature - Moisture + + + + + Targets + + + + + + + {enterTemp ? + updateTempEntry() + : + + {tempThreshDisplay()} + + } + {enterTemp ? + { + setEnterTemp(false) + //and update the bin with the new temp target + update() + }} + > + + + : + { + setEnterTemp(true) + }}> + + + } + + + + + + {enterMoisture ? + updateMoistureEntry() + : + + {bin.targetMoisture()}% + + } + {enterMoisture ? + { + setEnterMoisture(false) + //and update the bin with the new temp target + update() + }} + > + + + : + { + setEnterMoisture(true) + }}> + + + } + + + + + Level + Temperature + Moisture {binLevels.map((level, i) => ( - {binLevels.length - i} + {binLevels.length - i} {levelDisplay(level)} ))}
+ {/* Table Legend */} + + + + On or below target + + + + {user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT ? "+10 °F" : "+5 °C"} above target + + + + {user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT ? "+20 °F" : "+10 °C"} above target + + +
) } \ No newline at end of file diff --git a/src/models/Bin.ts b/src/models/Bin.ts index 1041d86..a3e46f4 100644 --- a/src/models/Bin.ts +++ b/src/models/Bin.ts @@ -302,8 +302,9 @@ export class Bin { return c; } - public upperTempThreshold(): number { - return this.settings.highTemp + //changed this to just use the target temp rather than the upper limit as we are deprecating the limits + public targetTemp(): number { + return this.settings.inventory?.targetTemperature ?? 0 } public lowerTempThreshold(): number {