From f02aa3a6e05b40b95f8d59158424046bb7f775de Mon Sep 17 00:00:00 2001 From: csawatzky Date: Wed, 28 Jan 2026 13:04:34 -0600 Subject: [PATCH] added more of the status for the gate to the gates dashboard, also changed the sensor graphs to have them in a specific order --- package-lock.json | 4 +- package.json | 2 +- src/gate/GateDevice.tsx | 20 ++++++ src/gate/GateGraphs.tsx | 91 +++++++++++++++++--------- src/gate/GateList.tsx | 139 +++++++++++++++++++++++++++------------- src/pages/Gate.tsx | 14 ++-- 6 files changed, 183 insertions(+), 87 deletions(-) diff --git a/package-lock.json b/package-lock.json index c150062..3d7a074 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,7 +42,7 @@ "mui-tel-input": "^7.0.0", "notistack": "^3.0.1", "openweathermap-ts": "^1.2.10", - "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#staging", + "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#gate_dashboard", "query-string": "^9.2.1", "react": "^18.3.1", "react-beautiful-dnd": "^13.1.1", @@ -10953,7 +10953,7 @@ }, "node_modules/protobuf-ts": { "version": "1.0.0", - "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#c9ef7906fd97cda8ef4bd149ec4a796159a7c067", + "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#7b1d4cc9b83dbd50ce5fd2bc751f4f8f68471861", "dependencies": { "protobufjs": "^6.8.8" } diff --git a/package.json b/package.json index ee0e9b7..03913bd 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "mui-tel-input": "^7.0.0", "notistack": "^3.0.1", "openweathermap-ts": "^1.2.10", - "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#master", + "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#gate_dashboard", "query-string": "^9.2.1", "react": "^18.3.1", "react-beautiful-dnd": "^13.1.1", diff --git a/src/gate/GateDevice.tsx b/src/gate/GateDevice.tsx index f7a8746..63ec92c 100644 --- a/src/gate/GateDevice.tsx +++ b/src/gate/GateDevice.tsx @@ -44,6 +44,8 @@ export default function GateDevice(props: Props) { const [tempKey, setTempKey] = useState(""); const [pressureKey, setPressureKey] = useState(""); const [ambientKey, setAmbientKey] = useState(""); + const [greenLightKey, setGreenLightKey] = useState(""); + const [redLightKey, setRedLightKey] = useState(""); const { openSnack } = useSnackbar(); const [{ user }] = useGlobalState(); const [interactionDialog, setInteractionDialog] = useState(false); @@ -57,8 +59,18 @@ export default function GateDevice(props: Props) { const [lastPressures, setLastPressures] = useState({ p1: 0, p2: 0 }); useEffect(() => { + //addresses of controller LEDs on a V1 device + let redAddr = "3-1-512"; + let greenAddr = "3-1-1024"; if (comprehensiveDevice.device) { setDevice(Device.any(comprehensiveDevice.device)); + + //check if the device is a MiPCA V2 device since the lEDs will have different addresses + let dev = Device.create(comprehensiveDevice.device); + if (dev.settings.product === pond.DeviceProduct.DEVICE_PRODUCT_MIPCA_V2) { + redAddr = "3-1-256"; + greenAddr = "3-1-512"; + } } if (comprehensiveDevice.components) { let components: Map = new Map(); @@ -100,6 +112,11 @@ export default function GateDevice(props: Props) { break; } } + if (c.locationString() === redAddr && c.subType() === 1) { + setRedLightKey(c.key()); + } else if (c.locationString() === greenAddr && c.subType() === 1) { + setGreenLightKey(c.key()); + } }); setComponentOptions(components); } @@ -362,6 +379,9 @@ export default function GateDevice(props: Props) { }} ambient={ambientKey} pressure={pressureKey} + tempChain={tempKey} + redKey={redLightKey} + greenKey={greenLightKey} device={device.id()} /> diff --git a/src/gate/GateGraphs.tsx b/src/gate/GateGraphs.tsx index 40c47d8..286abee 100644 --- a/src/gate/GateGraphs.tsx +++ b/src/gate/GateGraphs.tsx @@ -36,6 +36,9 @@ interface Props { device: string | number; pressure: string; ambient: string; + tempChain: string; + redKey: string; + greenKey: string; setPCAState: (state: boolean) => void; } @@ -60,7 +63,7 @@ const useStyles = makeStyles((theme: Theme) => ({ ); export default function GateGraphs(props: Props) { - const { gate, display, compMap, device, pressure, ambient, setPCAState } = props; + const { gate, display, compMap, device, pressure, ambient, tempChain, greenKey, redKey, setPCAState } = props; const [{ as }] = useGlobalState(); const [xDomain, setXDomain] = useState(["dataMin", "dataMax"]); const [zoomed, setZoomed] = useState(false); @@ -270,42 +273,68 @@ export default function GateGraphs(props: Props) { ); }; - const sensorGraphs = () => { + const graphCard = (component: Component, unitMeasurements: UnitMeasurement[]) => { return ( - - {Array.from(compMeasurements.values()).map((compMeasurement, i) => { - let c = Component.create(); - if (compMeasurement[0]) { - c = compMap.get(compMeasurement[0].componentId) ?? Component.create(); - } - if (compMap.get(c.key())) { - return ( - - {graphHeader(c)} - {compMeasurement.map(um => { - if (um.values[0] && um.values[0].values.length > 1) { - return areaGraph( - um, - describeMeasurement(um.type, c.type(), c.subType()), - c.settings.smoothingAverages - ); - } else { - return lineGraph( - um, - describeMeasurement(um.type, c.type(), c.subType()), - c.settings.smoothingAverages - ); - } - })} - + + {graphHeader(component)} + {unitMeasurements.map(um => { + if (um.values[0] && um.values[0].values.length > 1) { + return areaGraph( + um, + describeMeasurement(um.type, component.type(), component.subType()), + component.settings.smoothingAverages ); } else { - return ; + return lineGraph( + um, + describeMeasurement(um.type, component.type(), component.subType()), + component.settings.smoothingAverages + ); } })} - + ); - }; + } + + /** + * This function creates the charts for the gate page using the MAIN components on the gate, it DOES NOT show any other components that could be on the device + * @returns list of cards for the charts + */ + const sensorGraphs = () => { + let graphs: JSX.Element[] = [] + + //pressure + let pressureComp = compMap.get(pressure) + let pressureReadings = compMeasurements.get(pressure) + if(pressureComp && pressureReadings){ + graphs.push(graphCard(pressureComp, pressureReadings)) + } + //T/H chain + let tempComp = compMap.get(tempChain) + let tempReadings = compMeasurements.get(tempChain) + if(tempComp && tempReadings){ + graphs.push(graphCard(tempComp, tempReadings)) + } + //ambient + let ambientComp = compMap.get(ambient) + let ambientReadings = compMeasurements.get(ambient) + if (ambientComp && ambientReadings){ + graphs.push(graphCard(ambientComp, ambientReadings)) + } + //green + let greenComp = compMap.get(greenKey) + let greenReadings = compMeasurements.get(greenKey) + if (greenComp && greenReadings){ + graphs.push(graphCard(greenComp, greenReadings)) + } + //red + let redComp = compMap.get(redKey) + let redReadings = compMeasurements.get(redKey) + if (redComp && redReadings){ + graphs.push(graphCard(redComp, redReadings)) + } + return graphs + } const analyticGraphs = () => { return ( diff --git a/src/gate/GateList.tsx b/src/gate/GateList.tsx index 3e3b330..cd316c4 100644 --- a/src/gate/GateList.tsx +++ b/src/gate/GateList.tsx @@ -15,6 +15,10 @@ import { CheckCircleOutline, DoNotDisturb, ErrorOutline, HelpOutlineOutlined, Se import { cloneDeep } from "lodash"; import moment from "moment"; import { pond } from "protobuf-ts/pond"; +import { UnitMeasurement } from "models/UnitMeasurement"; +import { quack } from "protobuf-ts/quack"; +import { getTemperatureUnit } from "utils"; +import { teal } from "@mui/material/colors"; interface Props { //gates: Gate[]; @@ -47,6 +51,7 @@ export default function GateList(props: Props) { const navigate = useNavigate(); const isMobile = useMobile(); const classes = useStyles(); + const [{user}] = useGlobalState() const [gateDialog, setGateDialog] = useState(false); const [tablePage, setTablePage] = useState(0) const [pageSize, setPageSize] = useState(10) @@ -104,6 +109,38 @@ export default function GateList(props: Props) { } } + const lastOutletReading = (reading: pond.UnitMeasurementsForComponent[]) => { + let outletDisplay = "--" + let timeSince = "No Reading Yet" + let color = "" + reading.forEach(um => { + let clone = cloneDeep(um) + let measurement = UnitMeasurement.create(clone, user) + if(measurement.type === quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE || measurement.type === quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE){ + let nodes = measurement.values[measurement.values.length-1].values + outletDisplay = nodes[nodes.length-1].toFixed(2) + " " + measurement.unit + timeSince = moment(measurement.timestamps[measurement.timestamps.length-1]).fromNow() + color = measurement.colour + } + }) + return ( + + + Value: + {outletDisplay} + + + Time: + {timeSince} + + + ) + } + + const CtoF = (celsius: number) => { + return Math.round((celsius * (9 / 5) + 32) * 100) / 100; + }; + const desktopCols = (): Column[] => { return [ { @@ -126,68 +163,78 @@ export default function GateList(props: Props) { ) }, - { - title: "Duct Type", - render: gate => ( - - - {gate.settings.ductName} - - - ) - }, - { - title: "Duct Size(mm)", - render: gate => ( - - - {gate.ductDiameter()} - - - ) - }, - { - title: "Duct Length(m)", - render: gate => ( - - - {gate.settings.ductLength} - - - ) - }, - { - title: "PCA Unit", - render: gate => ( - - - {gate.settings.pcaType} - - - ) - }, { title: "PCA Status", - render: gate => ( - + render: gate => { + return ( {displayPCAStatus(gate.status.pcaState)} + ) + } + }, + { + title: "Conditioning", + render: gate => { + let display = "" + if(gate.status.pcaState === pond.PCAState.PCA_STATE_OFF){ + display = "Inactive" + } else if (gate.status.pcaState === pond.PCAState.PCA_STATE_UNKNOWN){ + display = "--" + } else { + display = gate.status.heating ? "Heating" : "Cooling" + } + return ( + + + {display} + - ) + )} + }, + { + title: "Estimated Temp", + render: gate => { + let display = "--" + if(gate.status.pcaState !== pond.PCAState.PCA_STATE_OFF){ + //only display it if the final temp is between -4 and 60 C, is a valid number, and is not exactly 0 + //0 is technically a valid number but the likely hood of the calculation being exactly 0 is negligible + if (gate.status.finalTemp < 60 && gate.status.finalTemp > -40 && !isNaN(gate.status.finalTemp) && gate.status.finalTemp !== 0){ + display = getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT ? CtoF(gate.status.finalTemp).toFixed(2) + "°F" : gate.status.finalTemp.toFixed(2) + "°C" + } + } + return ( + + + {display} + + + )} }, { title: "Last FLow", render: gate => ( - Last Flow: - {gate.status.lastMassAirflow.toFixed(2)} + Flow: + {gate.status.lastMassAirflow.toFixed(2)} kg/s - Last Reading: + Read: {gate.status.lastUpdate !== "" ? moment(gate.status.lastUpdate).fromNow() : "No Reading Yet"} ) + }, + { + title: "Outlet Temperature", + render: gate => ( + {lastOutletReading(gate.status.lastTempReading)} + ) + }, + { + title: "Outlet Pressure", + render: gate => ( + {lastOutletReading(gate.status.lastPressureReading)} + ) } ] } diff --git a/src/pages/Gate.tsx b/src/pages/Gate.tsx index 767bb22..780600a 100644 --- a/src/pages/Gate.tsx +++ b/src/pages/Gate.tsx @@ -192,7 +192,9 @@ export default function Gate(props: Props) { const deviceDrawer = () => { return ( - + + + Connect Device + - - - Connect Device - - Click here to add a device to the gate. To add an additional device to a gate use the "Link Device" icon on the top right side of this page