import { Avatar, Box, Button, Card, CardHeader, LinearProgress, Theme, Tooltip, Typography } from "@mui/material"; import { ZoomOut } from "@mui/icons-material"; import AreaGraph, { AreaData } from "charts/measurementCharts/AreaGraph"; import MultiLineGraph, { LineData, Point } from "charts/measurementCharts/MultiLineGraph"; import { GetDefaultDateRange } from "common/time/DateRange"; import TimeBar from "common/time/TimeBar"; import UnitMeasurementSummary from "component/UnitMeasurementSummary"; import { useThemeType } from "hooks"; import { Component } from "models"; import { Gate } from "models/Gate"; import { UnitMeasurement } from "models/UnitMeasurement"; import moment, { Moment } from "moment"; import { GetComponentIcon } from "pbHelpers/ComponentType"; import { describeMeasurement, MeasurementDescriber } from "pbHelpers/MeasurementDescriber"; import { useGateAPI, useGlobalState } from "providers"; import React, { useEffect, useState } from "react"; import { avg } from "utils"; import GateFlowGraph from "./GateFlowGraph"; import { makeStyles } from "@mui/styles"; import { cloneDeep } from "lodash"; interface Props { gate: Gate; display: "analytics" | "sensors"; compMap: Map; device: string | number; pressure: string; ambient: string; tempChain: string; redKey: string; greenKey: string; setPCAState: (state: boolean) => void; } const useStyles = makeStyles((theme: Theme) => ({ 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 GateGraphs(props: 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); const defaultDateRange = GetDefaultDateRange(); const [startDate, setStartDate] = useState(defaultDateRange.start); const [endDate, setEndDate] = useState(defaultDateRange.end); const themeType = useThemeType(); const classes = useStyles(); const [{ user }] = useGlobalState(); const [compMeasurements, setCompMeasurements] = useState>( new Map() ); const gateAPI = useGateAPI(); const [loading, setLoading] = useState(false); useEffect(() => { if (loading) return; setLoading(true); let measurementMap: Map = new Map(); gateAPI .listGateMeasurements(gate.key, startDate.toISOString(), endDate.toISOString(), as) .then(resp => { resp.data.measurements.forEach(um => { let unitMeasurement = UnitMeasurement.any(um, user); let entry = measurementMap.get(unitMeasurement.componentId); if (entry) { entry.push(unitMeasurement); } else { measurementMap.set(unitMeasurement.componentId, [unitMeasurement]); } }); setLoading(false); setCompMeasurements(measurementMap); }); }, [gate, endDate, gateAPI, startDate, as]); // 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 zoomOut = () => { setXDomain(["dataMin", "dataMax"]); setZoomed(false); }; const lineGraph = ( unitMeasurement: UnitMeasurement, describer: MeasurementDescriber, averages?: number ) => { if (unitMeasurement.values.length > 0) { let firstTime: Moment | undefined; let lastTime: Moment | undefined; //build line data to pass to graph let lineData: LineData[] = []; let averagedData: LineData[] = []; let valMap: Map = new Map(); unitMeasurement.values.forEach((vals, i) => { let avgVals: Point[] = []; let newLineData: LineData = { timestamp: moment(unitMeasurement.timestamps[i]).valueOf(), points: vals.values.map((val, i) => ({ value: val, node: i })) }; lineData.push(newLineData); if (averages) { vals.values.forEach((val, k) => { let entry = valMap.get(k); if (entry) { entry.push(val); if (entry.length >= averages) { lastTime = moment(unitMeasurement.timestamps[i]); avgVals.push({ node: k, value: avg(entry) }); } } else { valMap.set(k, [val]); firstTime = moment(unitMeasurement.timestamps[i]); } }); } if (firstTime && lastTime) { averagedData.push({ points: avgVals, timestamp: Math.abs(firstTime.diff(lastTime)) / 2 + Math.min(firstTime.valueOf(), lastTime.valueOf()) }); firstTime = undefined; lastTime = undefined; valMap = new Map(); } }); return ( 1 ? unitMeasurement.values[0].values.length : 0} describer={describer} tooltip newXDomain={xDomain} multiGraphZoom={domain => { setXDomain(domain); setZoomed(true); }} multiGraphZoomOut /> ); } }; const areaGraph = ( unitMeasurement: UnitMeasurement, describer: MeasurementDescriber, averages?: number ) => { if (unitMeasurement.timestamps.length > 0) { let firstTime: Moment | undefined; let lastTime: Moment | undefined; //build line data to pass to graph let areaData: AreaData[] = []; let avgData: AreaData[] = []; let maxVals: number[] = []; let minVals: number[] = []; unitMeasurement.values.forEach((val, j) => { let currentMax = Math.min(...val.values); let currentMin = Math.max(...val.values); let dataPoint: AreaData = { timestamp: moment(unitMeasurement.timestamps[j]).valueOf(), value: [currentMax, currentMin] }; if (averages) { if (minVals.length === 0) { firstTime = moment(unitMeasurement.timestamps[j]); } maxVals.push(currentMax); minVals.push(currentMin); if (minVals.length >= averages) { lastTime = moment(unitMeasurement.timestamps[j]); } } if (firstTime && lastTime) { avgData.push({ timestamp: Math.abs(firstTime.diff(lastTime)) / 2 + Math.min(firstTime.valueOf(), lastTime.valueOf()), value: [avg(maxVals), avg(minVals)] }); firstTime = undefined; lastTime = undefined; maxVals = []; minVals = []; } areaData.push(dataPoint); }); return ( { setXDomain(domain); setZoomed(true); }} multiGraphZoomOut /> ); } }; const graphHeader = (comp: Component) => { const componentIcon = GetComponentIcon(comp.settings.type, comp.settings.subtype, themeType); console.log(comp) let measurement = cloneDeep(comp.status.measurement) let umArray = measurement.map(um => UnitMeasurement.create(um, user)) return ( } title={{comp.name()}} subheader={ } /> ); }; const graphCard = (component: Component, unitMeasurements: UnitMeasurement[]) => { return ( {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 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 ( { setPCAState(state); }} multiGraphZoom={domain => { setXDomain(domain); setZoomed(true); }} /> ); }; const showGraphs = () => { switch (display) { case "sensors": return sensorGraphs(); case "analytics": return analyticGraphs(); default: return Unknown Graph Type Selected; } }; return ( {zoomed && ( )} {loading ? : showGraphs()} ); }