diff --git a/src/gate/GateDeltaTempGraph.tsx b/src/gate/GateDeltaTempGraph.tsx index 6a883bd..fc5b253 100644 --- a/src/gate/GateDeltaTempGraph.tsx +++ b/src/gate/GateDeltaTempGraph.tsx @@ -1,4 +1,4 @@ -import { Box, Card, CardHeader, Grid2, Typography } from "@mui/material" +import { Card, CardHeader, Grid2, Typography } from "@mui/material" import { blue, orange } from "@mui/material/colors" import SingleSetAreaChart, { SSAreaDataPoint } from "charts/SingleSetAreaChart" import { useComponentAPI } from "hooks" @@ -38,11 +38,68 @@ export default function GateDeltaTempGraph(props: Props){ const warmingColour = orange["500"] const coolingColour = blue["500"] + const getUnitByType = (unitMeasurements: UnitMeasurement[], type: quack.MeasurementType) => { + return unitMeasurements.find(u => u.type === type); + } + + const buildDeltas = (ambientMeasurements: UnitMeasurement[], tempCompMeasurements: UnitMeasurement[], measurementType: quack.MeasurementType) => { + const ambient = getUnitByType(ambientMeasurements, measurementType); + const outlet = getUnitByType(tempCompMeasurements, measurementType); + + if (!ambient || !outlet) return []; + + let i = 0; + let j = 0; + + let lastAmbient: number | null = null; + let lastOutlet: number | null = null; + + const deltas: SSAreaDataPoint[] = []; + + const ambientTimes = ambient.timestamps.map(t => moment(t).valueOf()).reverse(); + const outletTimes = outlet.timestamps.map(t => moment(t).valueOf()).reverse(); + const ambientValues = [...ambient.values].reverse(); + const outletValues = [...outlet.values].reverse(); + + while (i < ambientTimes.length || j < outletTimes.length) { + + const aTime = i < ambientTimes.length ? ambientTimes[i] : Infinity; + const bTime = j < outletTimes.length ? outletTimes[j] : Infinity; + + if (aTime <= bTime) { + const valueArray = ambientValues[i++]; + if (!valueArray.error && valueArray.values.length > 0) { + lastAmbient = valueArray.values[valueArray.values.length -1]; + } + + if (lastAmbient !== null && lastOutlet !== null) { + deltas.push({ + timestamp: aTime, + value: Math.round((lastOutlet - lastAmbient)*100)/100 + }); + } + + } else { + const valueArray = outletValues[j++]; + if (!valueArray.error && valueArray.values.length > 0) { + lastOutlet = valueArray.values[valueArray.values.length -1]; + } + + if (lastAmbient !== null && lastOutlet !== null) { + deltas.push({ + timestamp: bTime, + value: Math.round((lastOutlet - lastAmbient)*100)/100 + }); + } + } + } + return deltas + } + /** * the use effect will use the temp component passed in get the measurements, then use the first node and the last node to determine the difference, * if there is only one node, or only one node is reading due to errors, then it will use that as both the inlet and outlet so the delta will be 0, * - * //NOT IMPLEMENTED YET// * if an ambient is passed in it will use the readings from the ambient as the inlet and the temp component as the outlet */ useEffect(()=>{ @@ -52,7 +109,7 @@ export default function GateDeltaTempGraph(props: Props){ tempComponent.key(), start.toISOString(), end.toISOString(), - 500, + 500, 0, "timestamp", ).then(resp => { @@ -60,7 +117,24 @@ export default function GateDeltaTempGraph(props: Props){ let tempCompMeasurements = resp.data.measurements.map(um => UnitMeasurement.any(um, user)); //if there is an ambient component, then treat the temp component like a single sensor and not a chain, and use the ambient measurements as the inlet if(ambient){ - //TODO: this is just an idea of how to handle a possible path for the overhaul of gates, if that is the route we take then this needs to be coded + //need to then load the measurements for the ambient component + componentAPI.listUnitMeasurements( + deviceID, + ambient.key(), + start.toISOString(), + end.toISOString(), + 500, + 0, + "timestamp", + ).then(resp => { + let ambientMeasurements = resp.data.measurements.map(um => UnitMeasurement.any(um, user)); + //now loop through each of them to 'combine' them into a single array + const deltas = buildDeltas(ambientMeasurements, tempCompMeasurements, quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE) + if(deltas.length > 0){ + setData(deltas) + setRecent(deltas[deltas.length-1]) + } + }) }else{//else no ambient is passed in treat the temp component like a chain let deltas: SSAreaDataPoint[] = [] diff --git a/src/gate/GateDevice.tsx b/src/gate/GateDevice.tsx index bfc1641..f62dd25 100644 --- a/src/gate/GateDevice.tsx +++ b/src/gate/GateDevice.tsx @@ -23,6 +23,7 @@ import GateGraphs from "./GateGraphs"; //import { ToggleButton, ToggleButtonGroup } from "@material-ui/lab"; import { getThemeType } from "theme"; import ButtonGroup from "common/ButtonGroup"; +import { cloneDeep } from "lodash"; interface Props { gate: Gate; @@ -55,8 +56,11 @@ export default function GateDevice(props: Props) { // const [pcaFanOn, setPCAFanOn] = useState(false); const [detail, setDetail] = useState<"sensors" | "analytics">("analytics"); const [lastAmbient, setLastAmbient] = useState(0); - const [lastTemps, setLastTemps] = useState({ t1: 0, t2: 0 }); - const [lastPressures, setLastPressures] = useState({ p1: 0, p2: 0 }); + //const [lastTemps, setLastTemps] = useState({ t1: 0, t2: 0 }); + const [ductTemp, setDuctTemp] = useState() + //const [lastPressures, setLastPressures] = useState({ p1: 0, p2: 0 }); + const [ductPressure, setDuctPressure] = useState() + useEffect(() => { //addresses of controller LEDs on a V1 device @@ -142,24 +146,20 @@ export default function GateDevice(props: Props) { useEffect(() => { let tempComponent = componentOptions.get(tempKey); - let t1 = 0; - let t2 = 0; - if (tempComponent) { + if(tempComponent){ tempComponent.status.measurement.forEach(um => { - let measurement = UnitMeasurement.any(um, user); - if ( - measurement.type === quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE && - measurement.values.length > 0 - ) { - let nodeVals = measurement.values[0].values; - if (nodeVals.length > 1) { - t1 = nodeVals[0]; //uses the first node - t2 = nodeVals[1]; //uses second node node + if(um.type === quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE){ + let clone = cloneDeep(um) + let measurement = UnitMeasurement.any(clone, user) + if(measurement.values.length > 0){ + let readings = measurement.values[measurement.values.length -1] + if(readings.values.length > 0){ + setDuctTemp(readings.values[readings.values.length -1]) + } } } - }); + }) } - setLastTemps({ t1, t2 }); }, [componentOptions, tempKey, user]); useEffect(() => { @@ -180,24 +180,20 @@ export default function GateDevice(props: Props) { useEffect(() => { let pressureComponent = componentOptions.get(pressureKey); - let p1 = 0; - let p2 = 0; - if (pressureComponent) { + if(pressureComponent){ pressureComponent.status.measurement.forEach(um => { - let measurement = UnitMeasurement.any(um, user); - if ( - measurement.type === quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE && - measurement.values.length > 0 - ) { - let nodeVals = measurement.values[0].values; - if (nodeVals.length > 1) { - p1 = nodeVals[0]; - p2 = nodeVals[1]; + if(um.type === quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE){ + let clone = cloneDeep(um) + let measurement = UnitMeasurement.any(clone, user) + if(measurement.values.length > 0){ + let readings = measurement.values[measurement.values.length -1] + if(readings.values.length > 0){ + setDuctPressure(readings.values[readings.values.length -1]) + } } } - }); + }) } - setLastPressures({ p1, p2 }); }, [componentOptions, pressureKey, user]); const ambientSelector = () => { @@ -340,8 +336,10 @@ export default function GateDevice(props: Props) { : 0 } ambientTemp={lastAmbient} - innerTemps={lastTemps} - innerPressures={lastPressures} + //innerTemps={lastTemps} + //innerPressures={lastPressures} + ductTemp={ductTemp} + ductPressure={ductPressure} ambientSelector={ambientSelector} tempChainSelector={tempSelector} pressureChainSelector={pressureSelector} @@ -374,9 +372,6 @@ export default function GateDevice(props: Props) { gate={gate} display={detail} compMap={componentOptions} - // setPCAState={(state: boolean) => { - // setPCAState(state); - // }} ambient={ambientKey} pressure={pressureKey} tempChain={tempKey} diff --git a/src/gate/GateGraphs.tsx b/src/gate/GateGraphs.tsx index 3ce5932..b2ec455 100644 --- a/src/gate/GateGraphs.tsx +++ b/src/gate/GateGraphs.tsx @@ -303,6 +303,7 @@ export default function GateGraphs(props: Props) { */ const sensorGraphs = () => { const tempComp = compMap.get(tempChain) + const ambientComp = compMap.get(ambient) let graphs: JSX.Element[] = [] //pressure @@ -314,7 +315,7 @@ export default function GateGraphs(props: Props) { if(tempComp){ graphs.push( - + ) } //T/H chain @@ -323,7 +324,6 @@ export default function GateGraphs(props: Props) { graphs.push(graphCard(tempComp, tempReadings)) } //ambient - let ambientComp = compMap.get(ambient) let ambientReadings = compMeasurements.get(ambient) if (ambientComp && ambientReadings){ graphs.push(graphCard(ambientComp, ambientReadings)) @@ -345,6 +345,7 @@ export default function GateGraphs(props: Props) { const analyticGraphs = () => { let tempComp = compMap.get(tempChain) + let ambientComp = compMap.get(ambient) return ( {/* add a new chart here for the delta temp over time */} {tempComp && - + } ); diff --git a/src/gate/GateList.tsx b/src/gate/GateList.tsx index bbc5ecd..2cdec52 100644 --- a/src/gate/GateList.tsx +++ b/src/gate/GateList.tsx @@ -164,29 +164,49 @@ export default function GateList(props: Props) { // } const conditionDisplay = (gate: Gate) => { - console.log(gate) let display = "" - let deltaTemp = 0 if(gate.status.pcaState === pond.PCAState.PCA_STATE_OFF){ display = "Inactive" } else if (gate.status.pcaState === pond.PCAState.PCA_STATE_UNKNOWN){ display = "--" - } else { //the pca is currently active calulate the delta temp (T2 - T1) provided there are two temps - //loop to find the temp readings - let clone = cloneDeep(gate.status.lastTempReading) - clone.forEach(unitMeasurement => { - let um = UnitMeasurement.create(unitMeasurement, user) - if(um.type === quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE){ - if(um.values.length > 0){ //as long as there is at least on thing in the measurements - let lastReading = um.values[um.values.length-1] //there should only be one measurement in here but just make sure to get the end of the array - if(lastReading.values.length > 1){ //make sure there are at least two values in the array - console.log(lastReading.values) - deltaTemp = lastReading.values[lastReading.values.length -1] - lastReading.values[0] //subtract the first value from the last value to get the delta + } else { //the pca is currently active calulate the delta temp (outlet - ambient) + let ambient = cloneDeep(gate.status.lastAmbientReading) + let temp = cloneDeep(gate.status.lastTempReading) + + if(ambient.length > 0 && temp.length > 0){ + let ambientTemp: number | undefined + let outletTemp: number | undefined + ambient.forEach(um => { + if(um.type === quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE){ + let clone = cloneDeep(um) + let measurement = UnitMeasurement.any(clone, user) + if(measurement.values.length > 0){ + let readings = measurement.values[measurement.values.length -1] + if(readings.values.length > 0){ + ambientTemp = readings.values[readings.values.length -1] + } } } + }) + temp.forEach(um => { + if(um.type === quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE){ + let clone = cloneDeep(um) + let measurement = UnitMeasurement.any(clone, user) + if(measurement.values.length > 0){ + let readings = measurement.values[measurement.values.length -1] + if(readings.values.length > 0){ + outletTemp = readings.values[readings.values.length -1] + } + } + } + }) + if(ambientTemp && outletTemp){ + let deltaTemp = outletTemp - ambientTemp + display = deltaTemp.toFixed(2) + (getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT ? "°F" : "°C") + }else{ + display = "Sensor Missing" } - }) - display = deltaTemp.toFixed(2) + (getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT ? "°F" : "°C") + } } return ( diff --git a/src/gate/GateSVG.tsx b/src/gate/GateSVG.tsx index d71f857..33e767d 100644 --- a/src/gate/GateSVG.tsx +++ b/src/gate/GateSVG.tsx @@ -43,8 +43,10 @@ const useStyles = makeStyles(() => ({ interface Props { finalTemp: number; ambientTemp: number; - innerTemps: { t1: number; t2: number }; - innerPressures: { p1: number; p2: number }; + //innerTemps: { t1: number; t2: number }; + //innerPressures: { p1: number; p2: number }; + ductTemp?: number; + ductPressure?: number; ambientSelector: () => JSX.Element; tempChainSelector: () => JSX.Element; pressureChainSelector: () => JSX.Element; @@ -57,8 +59,10 @@ export default function GateSVG(props: Props) { const { finalTemp, ambientTemp, - innerTemps, - innerPressures, + //innerTemps, + //innerPressures, + ductTemp, + ductPressure, ambientSelector, tempChainSelector, pressureChainSelector, @@ -277,12 +281,15 @@ export default function GateSVG(props: Props) { values.push( - T1: {innerTemps.t1} + {/* T1: {innerTemps.t1} {user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT ? "°F" : "°C"} - , T2: {innerTemps.t2} + , T1: {innerTemps.t2} {user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT + ? "°F" + : "°C"} */} + Temperature: {ductTemp ?? "N/A"} {user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT ? "°F" : "°C"} @@ -292,12 +299,15 @@ export default function GateSVG(props: Props) { values.push( - P1: {innerPressures.p1}{" "} + {/* P1: {innerPressures.p1}{" "} {user.settings.pressureUnit === pond.PressureUnit.PRESSURE_UNIT_INCHES_OF_WATER ? "iwg" : "kPa"} , P2: {innerPressures.p2}{" "} {user.settings.pressureUnit === pond.PressureUnit.PRESSURE_UNIT_INCHES_OF_WATER + ? "iwg" + : "kPa"} */} + Pressure: {ductPressure ?? "N/A"} {user.settings.pressureUnit === pond.PressureUnit.PRESSURE_UNIT_INCHES_OF_WATER ? "iwg" : "kPa"} diff --git a/src/providers/pond/gateAPI.tsx b/src/providers/pond/gateAPI.tsx index ac63bb0..5b07606 100644 --- a/src/providers/pond/gateAPI.tsx +++ b/src/providers/pond/gateAPI.tsx @@ -138,7 +138,6 @@ export default function GateProvider(props: PropsWithChildren) { return new Promise>((resolve, reject) => { get(url).then(resp => { resp.data = pond.ListGatesResponse.fromObject(resp.data) - console.log(resp) return resolve(resp) }).catch(err => { return reject(err)