refactor the bin level over time
the auto inventory stuff pulls from the measurements and the manual/hybrid stuff pulls from the history, the auto will also create a new chart underneath for the bin modes
This commit is contained in:
parent
54a826bc2d
commit
67cafbe2ed
2 changed files with 133 additions and 96 deletions
|
|
@ -13,7 +13,7 @@ import { Bin } from "models";
|
|||
import moment, { Moment } from "moment";
|
||||
import { pond, quack } from "protobuf-ts/pond";
|
||||
import { useBinAPI, useGlobalState } from "providers";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { Legend } from "recharts";
|
||||
import { getGrainUnit } from "utils";
|
||||
|
||||
|
|
@ -31,24 +31,13 @@ export default function BinLevelOverTime(props: Props) {
|
|||
const { bin, fertilizerBin, colour, startDate, endDate, customHeight } = props;
|
||||
const binAPI = useBinAPI();
|
||||
const [{as}] = useGlobalState();
|
||||
const [showCable, setShowCable] = useState(false);
|
||||
const [manualData, setManualData] = useState<BarData[]>([]);
|
||||
const [cableData, setCableData] = useState<BarData[]>([]);
|
||||
const [data, setData] = useState<BarData[]>([]);
|
||||
const [modeAreas, setModeAreas] = useState<RefArea[]>([]);
|
||||
// const defaultDateRange = GetDefaultDateRange();
|
||||
// const [startDate, setStartDate] = useState<Moment>(defaultDateRange.start);
|
||||
// const [endDate, setEndDate] = useState<Moment>(defaultDateRange.end);
|
||||
const [dataLoading, setDataLoading] = useState(false);
|
||||
const [cableDataLoading, setCableDataLoading] = useState(false);
|
||||
const [capacity, setCapacity] = useState<number | undefined>();
|
||||
|
||||
// const updateDateRange = (newStartDate: any, newEndDate: any) => {
|
||||
// let range = GetDefaultDateRange();
|
||||
// range.start = newStartDate;
|
||||
// range.end = newEndDate;
|
||||
// setStartDate(newStartDate);
|
||||
// setEndDate(newEndDate);
|
||||
// };
|
||||
|
||||
const [modeData, setModeData] = useState<BarData[]>([]);
|
||||
const [histDataLoading, setHistDataLoading] = useState(false);
|
||||
|
||||
const getFill = (mode: pond.BinMode) => {
|
||||
let fill = "";
|
||||
|
|
@ -72,8 +61,8 @@ export default function BinLevelOverTime(props: Props) {
|
|||
return fill;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (dataLoading || bin.key() === "") return;
|
||||
//loads the data from the bin history table
|
||||
const loadHistoryData = useCallback(() => {
|
||||
setDataLoading(true);
|
||||
let cap = bin.settings.specs?.bushelCapacity;
|
||||
if (fertilizerBin && cap) {
|
||||
|
|
@ -146,48 +135,125 @@ export default function BinLevelOverTime(props: Props) {
|
|||
fill: getFill(bin.settings.mode)
|
||||
});
|
||||
}
|
||||
setManualData(data);
|
||||
setData(data);
|
||||
setModeAreas(modeAreas);
|
||||
})
|
||||
.finally(() => {
|
||||
setDataLoading(false);
|
||||
});
|
||||
}, [binAPI, bin, startDate, endDate, fertilizerBin]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
},[startDate, endDate, bin, fertilizerBin])
|
||||
|
||||
useEffect(() => {
|
||||
if (cableDataLoading || bin.key() === "") return;
|
||||
setCableDataLoading(true);
|
||||
//loads the data from the measuremnts table as a unit measurement
|
||||
const loadMeasurementData = useCallback(() => {
|
||||
setDataLoading(true);
|
||||
let cap = bin.settings.specs?.bushelCapacity;
|
||||
if (fertilizerBin && cap) {
|
||||
cap = cap * 35.239;
|
||||
}
|
||||
if (getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_WEIGHT && cap) {
|
||||
cap = cap / bin.bushelsPerTonne();
|
||||
}
|
||||
setCapacity(cap);
|
||||
let autoBarData: BarData[] = [];
|
||||
|
||||
//load the measurment data to build the bushel bars
|
||||
binAPI
|
||||
.listBinMeasurements(bin.key(), startDate, endDate, 500, 0, "asc", undefined, undefined, undefined, undefined, as)
|
||||
.listBinMeasurements(bin.key(), startDate, endDate, 500, 0, "asc")
|
||||
.then(resp => {
|
||||
let lastBushels = -1;
|
||||
|
||||
resp.data.measurements.forEach(objMeasurement => {
|
||||
//set the bushel cable measurements
|
||||
let m = pond.UnitMeasurementsForObject.fromObject(objMeasurement);
|
||||
if (m.measurementType === quack.MeasurementType.MEASUREMENT_TYPE_BUSHEL_CABLE) {
|
||||
let cableBarData: BarData[] = [];
|
||||
let lastBushels = -1;
|
||||
|
||||
//val will be an array of only a single number since this is not a measurement that involves nodes
|
||||
m.values.forEach((val, i) => {
|
||||
if (val.values[0] && m.timestamps[i]) {
|
||||
if (lastBushels !== val.values[0]) {
|
||||
cableBarData.push({
|
||||
value: val.values[0],
|
||||
timestamp: moment(m.timestamps[i]).valueOf()
|
||||
});
|
||||
lastBushels = val.values[0];
|
||||
}
|
||||
//val will be an array of only a single number since this is not a measurement that involves nodes
|
||||
m.values.forEach((val, i) => {
|
||||
if (val.values[0] && m.timestamps[i]) {
|
||||
if (lastBushels !== val.values[0]) {
|
||||
autoBarData.push({
|
||||
value: val.values[0],
|
||||
timestamp: moment(m.timestamps[i]).valueOf(),
|
||||
});
|
||||
lastBushels = val.values[0];
|
||||
}
|
||||
});
|
||||
setCableData(cableBarData);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
if (autoBarData.length === 0) {
|
||||
let bushels = bin.settings.inventory?.grainBushels ?? 0;
|
||||
let currentTime = moment().valueOf();
|
||||
autoBarData.push({
|
||||
value: fertilizerBin
|
||||
? Math.round(bushels * 35.239)
|
||||
: getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_WEIGHT
|
||||
? Math.round((bushels / bin.bushelsPerTonne()) * 100) / 100
|
||||
: bushels,
|
||||
timestamp: currentTime
|
||||
});
|
||||
}
|
||||
setData(autoBarData);
|
||||
})
|
||||
.catch(err => {})
|
||||
.finally(() => {
|
||||
setCableDataLoading(false);
|
||||
setDataLoading(false);
|
||||
});
|
||||
}, [binAPI, bin, startDate, endDate, fertilizerBin, as]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
},[startDate, endDate, bin, fertilizerBin])
|
||||
|
||||
const modeDataFromHistory = useCallback(()=>{
|
||||
if (histDataLoading) return;
|
||||
setHistDataLoading(true)
|
||||
binAPI
|
||||
.listHistoryBetween(bin.key(), 500, startDate.toISOString(), endDate.toISOString())
|
||||
.then(resp => {
|
||||
//loop through the history finding all the times the bin mode changed
|
||||
let currentMode: pond.BinMode | undefined = undefined
|
||||
let modeData: BarData[] = []
|
||||
resp.data.history.forEach(hist => {
|
||||
let histBin = Bin.create()
|
||||
histBin.settings = hist.settings ?? pond.BinSettings.create()
|
||||
if(hist.settings && currentMode !== hist.settings.mode){
|
||||
currentMode = hist.settings.mode
|
||||
modeData.push({
|
||||
timestamp: moment(hist.timestamp).valueOf(),
|
||||
value: 1,
|
||||
fill: getFill(currentMode)
|
||||
})
|
||||
}
|
||||
})
|
||||
setModeData(modeData)
|
||||
})
|
||||
.catch(err => {
|
||||
|
||||
})
|
||||
.finally(()=>{
|
||||
setHistDataLoading(false)
|
||||
})
|
||||
},[bin, startDate, endDate])
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
//dont call a load function if loading is already in progress
|
||||
if (dataLoading || bin.key() === "") return;
|
||||
//check what is controlling the bin inventory
|
||||
let control = bin.inventoryControl()
|
||||
//for automatic lidar and cables get the data from measurements as an object measurement
|
||||
if(control === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC_LIDAR ||
|
||||
control === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC
|
||||
){
|
||||
//load the measurement data for the bar graph
|
||||
loadMeasurementData()
|
||||
//load the data for the storage mode changes
|
||||
modeDataFromHistory()
|
||||
|
||||
//for manual and hybrid (lidar or in the future, cables) get the data from bin_history
|
||||
}else if(control === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_MANUAL ||
|
||||
control === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_HYBRID_LIDAR ||
|
||||
control === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_UNKNOWN
|
||||
){
|
||||
loadHistoryData()
|
||||
}
|
||||
|
||||
},[bin, loadMeasurementData, loadHistoryData])
|
||||
|
||||
const legend = () => {
|
||||
return (
|
||||
|
|
@ -203,15 +269,16 @@ export default function BinLevelOverTime(props: Props) {
|
|||
);
|
||||
};
|
||||
|
||||
const manualChart = () => {
|
||||
const inventoryChart = () => {
|
||||
return (
|
||||
<BarGraph
|
||||
customHeight={customHeight}
|
||||
data={manualData}
|
||||
data={data}
|
||||
refAreas={modeAreas}
|
||||
barColour={colour}
|
||||
yMax={capacity}
|
||||
graphLegend={legend()}
|
||||
yMin={0}
|
||||
graphLegend={modeAreas.length > 0 ? legend() : undefined}
|
||||
labelColour="white"
|
||||
labels
|
||||
useGradient
|
||||
|
|
@ -219,62 +286,28 @@ export default function BinLevelOverTime(props: Props) {
|
|||
);
|
||||
};
|
||||
|
||||
const cableChart = () => {
|
||||
if (cableData.length === 0) {
|
||||
return (
|
||||
<Box height={customHeight} paddingTop={"25%"}>
|
||||
<Typography style={{ textAlign: "center", fontSize: 25, fontWeight: 650 }}>
|
||||
No cable estimates for selected time frame
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
const autoBinModeChart = () => {
|
||||
return (
|
||||
<BarGraph
|
||||
customHeight={customHeight}
|
||||
data={cableData}
|
||||
//refAreas={modeAreas}
|
||||
barColour={colour}
|
||||
yMax={capacity}
|
||||
yMin={0}
|
||||
<BarGraph
|
||||
data={modeData}
|
||||
customHeight={100}
|
||||
hideYAxis
|
||||
graphLegend={legend()}
|
||||
labelColour="white"
|
||||
labels
|
||||
useGradient
|
||||
yMax={1}
|
||||
yMin={0}
|
||||
/>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Card raised style={{ padding: 5 }}>
|
||||
<Grid
|
||||
container
|
||||
direction="row"
|
||||
alignContent="center"
|
||||
alignItems="center"
|
||||
justifyContent="space-between">
|
||||
<Grid>
|
||||
<Typography style={{ fontSize: 25, fontWeight: 650, marginLeft: 20 }}>
|
||||
Grain Levels Over Time
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={showCable}
|
||||
onChange={(_, checked) => {
|
||||
setShowCable(checked);
|
||||
}}
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
label="Cable Estimates"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{dataLoading ? <CircularProgress /> : showCable ? cableChart() : manualChart()}
|
||||
<Typography style={{ fontSize: 25, fontWeight: 650, marginLeft: 20 }}>
|
||||
Grain Levels Over Time
|
||||
</Typography>
|
||||
{dataLoading ? <CircularProgress /> : inventoryChart()}
|
||||
{/* when using auto will need to make a new chart just for the bin modes since the auto inventory comes from another place */}
|
||||
{(bin.inventoryControl() === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC_LIDAR ||
|
||||
bin.inventoryControl() === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC) && autoBinModeChart()}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {
|
|||
export interface BarData {
|
||||
timestamp: number;
|
||||
value: number;
|
||||
fill?: string;
|
||||
}
|
||||
|
||||
export interface RefArea {
|
||||
|
|
@ -36,8 +37,9 @@ export interface AxisLabel {
|
|||
|
||||
interface Props {
|
||||
data: BarData[];
|
||||
barColour: string;
|
||||
barColour?: string;
|
||||
refAreas?: RefArea[];
|
||||
hideYAxis?: boolean;
|
||||
yMin?: number;
|
||||
yMax?: number;
|
||||
graphLegend?: JSX.Element;
|
||||
|
|
@ -60,6 +62,7 @@ export default function BarGraph(props: Props) {
|
|||
data,
|
||||
barColour,
|
||||
refAreas,
|
||||
hideYAxis,
|
||||
yMin,
|
||||
yMax,
|
||||
graphLegend,
|
||||
|
|
@ -103,6 +106,7 @@ export default function BarGraph(props: Props) {
|
|||
{graphLegend}
|
||||
<YAxis
|
||||
domain={[yMin ?? "dataMin", yMax ?? "auto"]}
|
||||
hide={hideYAxis}
|
||||
interval="preserveStartEnd"
|
||||
scale={"linear"}
|
||||
label={yLabel}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue