added tons of files, got the bin page to render
This commit is contained in:
parent
a027b8a96f
commit
9bbbf0940e
34 changed files with 10492 additions and 19 deletions
303
src/bin/graphs/BinGraphsVPD.tsx
Normal file
303
src/bin/graphs/BinGraphsVPD.tsx
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
import { Box, CircularProgress, MenuItem, TextField } from "@mui/material";
|
||||
import GrainDryingChart, { GrainDryingPoint } from "charts/GrainDryingChart";
|
||||
import { GrainCable } from "models/GrainCable";
|
||||
import { Plenum } from "models/Plenum";
|
||||
import { UnitMeasurement } from "models/UnitMeasurement";
|
||||
import moment from "moment";
|
||||
import { quack } from "protobuf-ts/quack";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { avg } from "utils";
|
||||
|
||||
interface Props {
|
||||
plenums: Plenum[];
|
||||
cables: GrainCable[];
|
||||
measurementMap: Map<string, UnitMeasurement[]>;
|
||||
returnLastVPD: (lastVPD: GrainDryingPoint | undefined) => void;
|
||||
newXDomain?: number[] | string[];
|
||||
multiGraphZoom?: (domain: number[] | string[]) => void;
|
||||
multiGraphZoomOut?: boolean;
|
||||
//perBushelCFM?: number;
|
||||
}
|
||||
|
||||
interface UnitMeasurementHelper {
|
||||
timestamp: string;
|
||||
compKey: string;
|
||||
measurementType: quack.MeasurementType;
|
||||
value: number;
|
||||
}
|
||||
|
||||
interface NodeOption {
|
||||
name: string;
|
||||
number: number;
|
||||
}
|
||||
|
||||
export default function BinGraphsVPD(props: Props) {
|
||||
const {
|
||||
plenums,
|
||||
cables,
|
||||
measurementMap,
|
||||
newXDomain,
|
||||
multiGraphZoom,
|
||||
multiGraphZoomOut,
|
||||
returnLastVPD
|
||||
//perBushelCFM
|
||||
} = props;
|
||||
const [vpdData, setVpdData] = useState<GrainDryingPoint[]>([]);
|
||||
const [plenum, setPlenum] = useState<string>("");
|
||||
const [cable, setCable] = useState<string>("");
|
||||
const [cableOptions, setCableOptions] = useState<GrainCable[]>([]);
|
||||
const [calculating, setCalculating] = useState(false);
|
||||
const [selectedNode, setSelectedNode] = useState(-1);
|
||||
const [nodeOps, setNodeOps] = useState<NodeOption[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
let plenum: string = "";
|
||||
if (plenums[0]) {
|
||||
plenum = plenums[0].key();
|
||||
}
|
||||
let ops: GrainCable[] = [];
|
||||
let cable: string = "";
|
||||
cables.forEach(c => {
|
||||
if (c.humidities.length > 0) {
|
||||
ops.push(c);
|
||||
if (ops.length === 1) {
|
||||
cable = c.key();
|
||||
let nodes: NodeOption[] = [];
|
||||
c.humidities.forEach((hum, i) => {
|
||||
nodes.push({
|
||||
name: "Node" + (i + 1),
|
||||
number: i
|
||||
});
|
||||
});
|
||||
setNodeOps(nodes);
|
||||
}
|
||||
}
|
||||
});
|
||||
setPlenum(plenum);
|
||||
setCable(cable);
|
||||
setCableOptions(ops);
|
||||
}, [plenums, cables]);
|
||||
|
||||
const buildHelperArray = (
|
||||
plenumUM: UnitMeasurement[],
|
||||
cableUM: UnitMeasurement[],
|
||||
node?: number
|
||||
) => {
|
||||
let helper: UnitMeasurementHelper[] = [];
|
||||
//doing the plenum and the cable seperately because the plenum will always be done the same way however the cable will be done differently if there is a node selected
|
||||
plenumUM.forEach(unitM => {
|
||||
if (
|
||||
unitM.type === quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE ||
|
||||
unitM.type === quack.MeasurementType.MEASUREMENT_TYPE_PERCENT
|
||||
) {
|
||||
unitM.values.forEach((valSet, i) => {
|
||||
helper.push({
|
||||
timestamp: unitM.timestamps[i],
|
||||
measurementType: unitM.type,
|
||||
compKey: unitM.componentId,
|
||||
value: avg(valSet.values)
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
cableUM.forEach(unitM => {
|
||||
if (
|
||||
unitM.type === quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE ||
|
||||
unitM.type === quack.MeasurementType.MEASUREMENT_TYPE_PERCENT
|
||||
) {
|
||||
unitM.values.forEach((valSet, i) => {
|
||||
let newHelper = {
|
||||
timestamp: unitM.timestamps[i],
|
||||
measurementType: unitM.type,
|
||||
compKey: unitM.componentId,
|
||||
value: node !== undefined ? valSet.values[node] : avg(valSet.values)
|
||||
};
|
||||
helper.push(newHelper);
|
||||
});
|
||||
}
|
||||
});
|
||||
helper.sort((a, b) => {
|
||||
return moment(a.timestamp).valueOf() - moment(b.timestamp).valueOf();
|
||||
});
|
||||
return helper;
|
||||
};
|
||||
|
||||
// T = temperature im degrees Celsius
|
||||
// RH = relative humidity (%)
|
||||
const calculateVPD = (T: number, RH: number) => {
|
||||
const es = 0.6108 * Math.exp((17.27 * T) / (T + 237.3));
|
||||
const ea = (RH / 100) * es;
|
||||
return ea - es;
|
||||
};
|
||||
|
||||
const buildData = (measurements: UnitMeasurementHelper[]) => {
|
||||
let data: GrainDryingPoint[] = [];
|
||||
|
||||
let recent: GrainDryingPoint | undefined;
|
||||
|
||||
let currentPlenumTemp: number;
|
||||
let currentPlenumHum: number;
|
||||
let currentCableTemp: number;
|
||||
let currentCableHum: number;
|
||||
if (measurements[0]) {
|
||||
let lastTime = moment(measurements[0].timestamp);
|
||||
measurements.forEach(m => {
|
||||
let thisTime = moment(m.timestamp);
|
||||
if (m.compKey === plenum) {
|
||||
if (m.measurementType === quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE) {
|
||||
currentPlenumTemp = m.value;
|
||||
} else if (m.measurementType === quack.MeasurementType.MEASUREMENT_TYPE_PERCENT) {
|
||||
currentPlenumHum = m.value;
|
||||
}
|
||||
}
|
||||
if (m.compKey === cable) {
|
||||
if (m.measurementType === quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE) {
|
||||
currentCableTemp = m.value;
|
||||
} else if (m.measurementType === quack.MeasurementType.MEASUREMENT_TYPE_PERCENT) {
|
||||
currentCableHum = m.value;
|
||||
}
|
||||
}
|
||||
if (currentPlenumTemp && currentPlenumHum && currentCableTemp && currentCableHum) {
|
||||
if (thisTime.diff(lastTime, "seconds") >= 2) {
|
||||
lastTime = thisTime;
|
||||
let plenumVPD = calculateVPD(currentPlenumTemp, currentPlenumHum);
|
||||
let cableVPD = calculateVPD(currentCableTemp, currentCableHum);
|
||||
let dryScore = cableVPD - plenumVPD;
|
||||
// if (perBushelCFM) {
|
||||
// dryScore = dryScore * perBushelCFM;
|
||||
// }
|
||||
let newPoint: GrainDryingPoint = {
|
||||
dryScore: dryScore,
|
||||
timestamp: moment(m.timestamp).valueOf()
|
||||
};
|
||||
data.push(newPoint);
|
||||
if (!recent || recent.timestamp < newPoint.timestamp) {
|
||||
recent = newPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setVpdData(data);
|
||||
returnLastVPD(recent);
|
||||
};
|
||||
|
||||
//calculate the vpd using the average values of the nodes
|
||||
useEffect(() => {
|
||||
setSelectedNode(-1);
|
||||
setVpdData([]);
|
||||
if (plenum && cable) {
|
||||
let plenumUM = measurementMap.get(plenum);
|
||||
let cableUM = measurementMap.get(cable);
|
||||
if (plenumUM && cableUM) {
|
||||
setCalculating(true);
|
||||
let measurements = buildHelperArray(plenumUM, cableUM);
|
||||
buildData(measurements);
|
||||
setCalculating(false);
|
||||
}
|
||||
}
|
||||
}, [plenum, cable, measurementMap]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
//calculate the vpd using the specified node value
|
||||
const nodeVPD = (node: number) => {
|
||||
if (plenum && cable) {
|
||||
setVpdData([]);
|
||||
let plenumUM = measurementMap.get(plenum);
|
||||
let cableUM = measurementMap.get(cable);
|
||||
if (plenumUM && cableUM) {
|
||||
setCalculating(true);
|
||||
let measurements: UnitMeasurementHelper[] = [];
|
||||
if (node === -1) {
|
||||
measurements = buildHelperArray(plenumUM, cableUM);
|
||||
} else {
|
||||
measurements = buildHelperArray(plenumUM, cableUM, node);
|
||||
}
|
||||
buildData(measurements);
|
||||
setCalculating(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{plenums.length > 1 && (
|
||||
<TextField
|
||||
style={{ marginRight: 15 }}
|
||||
label="Plenum"
|
||||
variant="outlined"
|
||||
select
|
||||
id="plenumSelector"
|
||||
value={plenum}
|
||||
onChange={e => {
|
||||
setPlenum(e.target.value as string);
|
||||
}}>
|
||||
{plenums.map(k => {
|
||||
return (
|
||||
<MenuItem key={k.key()} value={k.key()}>
|
||||
{k.name()}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</TextField>
|
||||
)}
|
||||
{cableOptions.length > 1 && (
|
||||
<TextField
|
||||
style={{ marginRight: 15 }}
|
||||
label="Cable"
|
||||
variant="outlined"
|
||||
select
|
||||
id="cableSelector"
|
||||
value={cable}
|
||||
onChange={e => {
|
||||
setCable(e.target.value as string);
|
||||
}}>
|
||||
{cableOptions.map(k => {
|
||||
return (
|
||||
<MenuItem key={k.key()} value={k.key()}>
|
||||
{k.name()}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</TextField>
|
||||
)}
|
||||
<TextField
|
||||
label="Node"
|
||||
variant="outlined"
|
||||
select
|
||||
id="nodeSelector"
|
||||
value={selectedNode}
|
||||
onChange={e => {
|
||||
setSelectedNode(+e.target.value);
|
||||
nodeVPD(+e.target.value);
|
||||
}}>
|
||||
<MenuItem key={"noNode"} value={-1}>
|
||||
Average Score
|
||||
</MenuItem>
|
||||
{nodeOps.map(n => {
|
||||
return (
|
||||
<MenuItem key={n.name} value={n.number}>
|
||||
{n.name}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</TextField>
|
||||
{calculating ? (
|
||||
<Box minHeight={100} display="flex" justifyContent="center" alignItems="center">
|
||||
<CircularProgress />
|
||||
</Box>
|
||||
) : (
|
||||
vpdData.length > 0 && (
|
||||
<GrainDryingChart
|
||||
data={vpdData}
|
||||
showStroke
|
||||
displayY
|
||||
newXDomain={newXDomain}
|
||||
multiGraphZoom={multiGraphZoom}
|
||||
multiGraphZoomOut={multiGraphZoomOut}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue