import { Avatar, Box, Button, Card, CardHeader, Checkbox, FormControlLabel, //FormControlLabel, Grid2 as Grid, //Switch, Theme, Tooltip, Typography } from "@mui/material"; import { ZoomOut } from "@mui/icons-material"; import { GetDefaultDateRange } from "common/time/DateRange"; import TimeBar from "common/time/TimeBar"; import GrainDescriber from "grain/GrainDescriber"; import { useMobile, useThemeType } from "hooks"; import { Bin, Component } from "models"; import { GrainCable } from "models/GrainCable"; import { Plenum } from "models/Plenum"; import { Pressure } from "models/Pressure"; import { UnitMeasurement } from "models/UnitMeasurement"; import moment, { Moment } from "moment"; import { pond } from "protobuf-ts/pond"; import { useBinAPI, useGlobalState } from "providers"; import React, { useEffect, useState } from "react"; import { stringToMaterialColour } from "utils"; import BinGraphsTrending from "./BinGraphsTrending"; import BinGraphsVPD from "./BinGraphsVPD"; import BinLevelOverTime from "./BinLevelOverTime"; import BinWaterLevel from "./BinWaterLevel"; import VPDLight from "assets/products/Ag/dryingLight.png"; import VPDDark from "assets/products/Ag/dryingDark.png"; import WaterLight from "assets/products/Ag/waterContentLight.png"; import WaterDark from "assets/products/Ag/waterContentDark.png"; import TrendLight from "assets/products/Ag/trendingLight.png"; import TrendDark from "assets/products/Ag/trendingDark.png"; import { GrainDryingPoint } from "charts/GrainDryingChart"; import { blue, orange, teal } from "@mui/material/colors"; import { DataPoint, TrendPoint } from "charts/TrendingChart"; import { DataPoint as WaterPoint } from "charts/WaterLevelChart"; import { cloneDeep } from "lodash"; import { Controller } from "models/Controller"; import BinComponentGraph from "./BinComponentGraph"; import BinCompositionGraph from "./BinCompositionGraph"; import { makeStyles } from "@mui/styles"; interface Props { bin: Bin; plenums: Plenum[]; cables: GrainCable[]; pressures: Pressure[]; fans: Controller[]; componentDevices: Map; compositionNameMap: Map; compMap: Map; binLoading: boolean; display: "inventory" | "sensors" | "analytics"; } const useStyles = makeStyles((theme: Theme) =>{ return ({ card: { position: "relative", display: "flex", height: "100%", flexDirection: "column", overflow: "visible" }, cardHeader: { padding: theme.spacing(1), paddingLeft: theme.spacing(2), marginRight: 10 }, avatarIcon: { width: 33, height: 33 } }) }); export default function BinGraphs(props: Props) { const { bin, plenums, cables, pressures, binLoading, display, componentDevices, compositionNameMap, compMap, fans } = props; // map using the component key and the unitmeasurements for that component const [compMeasurements, setCompMeasurements] = useState>( new Map() ); const defaultDateRange = GetDefaultDateRange(); const [startDate, setStartDate] = useState(defaultDateRange.start); const [endDate, setEndDate] = useState(defaultDateRange.end); const binAPI = useBinAPI(); const [xDomain, setXDomain] = useState(["dataMin", "dataMax"]); const [zoomed, setZoomed] = useState(false); const themeType = useThemeType(); const classes = useStyles(); const [recentVPD, setRecentVPD] = useState(); const [recentPlenumTrend, setRecentPlenumTrend] = useState(); const [recentCableTrend, setRecentCableTrend] = useState(); const [recentWaterContent, setRecentWaterContent] = useState(); const [{ user, as }] = useGlobalState(); const [measurementsLoading, setMeasurementsLoading] = useState(false); const isMobile = useMobile(); const [{ showErrors }, dispatch] = useGlobalState(); //const [includeCFM, setIncludeCFM] = useState(false); const zoomOut = () => { setXDomain(["dataMin", "dataMax"]); setZoomed(false); }; const zoomIn = (domain: string[] | number[]) => { if (!isMobile) { setXDomain(domain); setZoomed(true); } }; useEffect(() => { let measurementMap: Map = new Map(); if (display !== "inventory" && !measurementsLoading) { setMeasurementsLoading(true); //check if the bin has a fan to use the cfm in the drying graph // if (bin.settings.fan?.type !== pond.FanType.FAN_TYPE_UNKNOWN) { // setIncludeCFM(true); // } binAPI .listBinComponentsMeasurements( bin.key(), startDate.toISOString(), endDate.toISOString(), showErrors, showErrors, as ) .then(resp => { resp.data.measurements.forEach((um: any) => { let unitMeasurement = UnitMeasurement.any(um, user); let entry = measurementMap.get(unitMeasurement.componentId); if (entry) { entry.push(unitMeasurement); } else { measurementMap.set(unitMeasurement.componentId, [unitMeasurement]); } }); setCompMeasurements(measurementMap); setMeasurementsLoading(false); }); } }, [bin, binAPI, startDate, endDate, display, user]); // eslint-disable-line react-hooks/exhaustive-deps const updateDateRange = (newStartDate: any, newEndDate: any) => { let range = GetDefaultDateRange(); range.start = newStartDate; range.end = newEndDate; setStartDate(newStartDate); setEndDate(newEndDate); }; // const determineGrainColour = () => { // let col = "yellow"; // let binInv = bin.settings.inventory; // if (binInv) { // if (binInv.grainType === pond.Grain.GRAIN_CUSTOM) { // col = stringToMaterialColour(binInv.customTypeName); // } else if (binInv.grainType !== pond.Grain.GRAIN_NONE) { // col = GrainDescriber(binInv.grainType).colour; // } // } // return col; // }; const inventoryGraph = () => { return ( {user.hasFeature("grain-composition") && ( )} ); }; //will use the compMeasurments to show all the graphs for all connected sensors, as it goes through the plenums/pressures/cables it will remove them from //the temp measurments map so that it will show the remaining components at the bottom const sensorGraphs = () => { let toShow = cloneDeep(compMeasurements); return ( {plenums.map(p => { let measurements = toShow.get(p.key()); toShow.delete(p.key()); return ( ); })} {pressures.map(p => { let measurements = toShow.get(p.key()); toShow.delete(p.key()); return ( ); })} {cables.map(c => { let measurements = toShow.get(c.key()); toShow.delete(c.key()); return ( ); })} {Array.from(toShow.values()).map((compMeasurement) => { let c = Component.create(); if (compMeasurement[0]) { c = compMap.get(compMeasurement[0].componentId) ?? Component.create(); } return ( ); })} ); }; const waterGraph = () => { let moistureCables: GrainCable[] = []; cables.forEach(cable => { if (cable.humidities.length > 0) { moistureCables.push(cable); } }); if (plenums[0] && moistureCables[0] && pressures[0] && bin.supportedGrain()) { return ( 0 ? componentDevices.get(fans[0].key()) + ":" + fans[0].key() : undefined } newXDomain={xDomain} multiGraphZoom={zoomIn} multiGraphZoomOut returnLast={lastWater => { setRecentWaterContent(lastWater); }} /> ); } else { return ( Plenum with pressure and moisture cable as well as an initial moisture and supported grain type set in the bin settings required to calculate Water Content ); } }; const analyticGraphs = () => { return ( } title={ Drying vs. Hydrating {/* {bin.settings.fan?.type !== pond.FanType.FAN_TYPE_UNKNOWN && ( { setIncludeCFM(checked); }} name="include CFM" /> } label="Include CFM" labelPlacement="start" /> )} */} } subheader={ recentVPD ? ( 0 ? orange[500] : blue[500] }}> {recentVPD.dryScore > 0 ? "drying" : "hydrating"} {" : "} 0 ? orange[500] : blue[500], fontWeight: 500 }}> {recentVPD.dryScore.toFixed(2)}
{moment(recentVPD.timestamp).fromNow()}
) : ( No Data ) } /> {cables.length > 0 && plenums.length > 0 ? ( { setRecentVPD(lastVPD); }} /> ) : ( Plenum and moisture cable required to calculate VPD )}
} title={ Moisture Trending } subheader={ recentPlenumTrend && recentCableTrend ? ( {"Plenum Moisture: "} {recentPlenumTrend.trend.toFixed(2)}
{"Grain Moisture: "} {recentCableTrend.moisture.toString()}
{recentPlenumTrend.timestamp > recentCableTrend.timestamp ? moment(recentPlenumTrend.timestamp).fromNow() : moment(recentCableTrend.timestamp).fromNow()}
) : ( No Data ) } /> {cables.length > 0 && plenums.length > 0 ? ( { setRecentCableTrend(lastData); setRecentPlenumTrend(lastTrend); }} /> ) : ( Plenum and moisture cable required to calculate trending data )}
} title={ Grain Water Content } subheader={ recentWaterContent && bin.settings.inventory && bin.settings.inventory.initialMoisture > 0 ? ( {"Water Content: "} {recentWaterContent.liters && !isNaN(recentWaterContent.liters) ? recentWaterContent.liters.toFixed(2) : ""}
{moment(recentWaterContent.timestamp).fromNow()}
) : ( No Data ) } /> {waterGraph()}
); }; const showGraphs = () => { switch (display) { case "inventory": return inventoryGraph(); case "sensors": return sensorGraphs(); case "analytics": return analyticGraphs(); default: return Unknown Graph Type Selected; } }; return ( {display !== "inventory" && zoomed && ( )} { //setShowErrors(!showErrors); dispatch({ key: "showErrors", value: !showErrors }); }} /> } /> {showGraphs()} ); }