401 lines
12 KiB
TypeScript
401 lines
12 KiB
TypeScript
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";
|
|
import GateDeltaTempGraph from "./GateDeltaTempGraph";
|
|
|
|
interface Props {
|
|
gate: Gate;
|
|
display: "analytics" | "sensors";
|
|
compMap: Map<string, Component>;
|
|
device: 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 } = props;
|
|
const [{ as }] = useGlobalState();
|
|
const [xDomain, setXDomain] = useState<string[] | number[]>(["dataMin", "dataMax"]);
|
|
const [zoomed, setZoomed] = useState(false);
|
|
const defaultDateRange = GetDefaultDateRange();
|
|
const [startDate, setStartDate] = useState<Moment>(defaultDateRange.start);
|
|
const [endDate, setEndDate] = useState<Moment>(defaultDateRange.end);
|
|
const themeType = useThemeType();
|
|
const classes = useStyles();
|
|
const [{ user }] = useGlobalState();
|
|
const [compMeasurements, setCompMeasurements] = useState<Map<string, UnitMeasurement[]>>(
|
|
new Map<string, UnitMeasurement[]>()
|
|
);
|
|
const gateAPI = useGateAPI();
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
useEffect(() => {
|
|
if (loading) return;
|
|
setLoading(true);
|
|
let measurementMap: Map<string, UnitMeasurement[]> = new Map<string, UnitMeasurement[]>();
|
|
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<number, number[]> = new Map<number, number[]>();
|
|
|
|
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<number, number[]>();
|
|
}
|
|
});
|
|
return (
|
|
<MultiLineGraph
|
|
customHeight={300}
|
|
key={unitMeasurement.type}
|
|
lineData={lineData}
|
|
//averagedData={showAveraged ? averagedData : []}
|
|
numLines={unitMeasurement.values.length > 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 (
|
|
<AreaGraph
|
|
key={unitMeasurement.type}
|
|
customHeight={350}
|
|
data={areaData}
|
|
//averagedData={showAveraged ? avgData : []}
|
|
describer={describer}
|
|
tooltip
|
|
newXDomain={xDomain}
|
|
multiGraphZoom={domain => {
|
|
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 (
|
|
<CardHeader
|
|
avatar={
|
|
<Avatar
|
|
variant="square"
|
|
src={componentIcon}
|
|
className={classes.avatarIcon}
|
|
alt={comp.name()}
|
|
/>
|
|
}
|
|
title={<Typography style={{ fontSize: 25, fontWeight: 650 }}>{comp.name()}</Typography>}
|
|
subheader={
|
|
<UnitMeasurementSummary
|
|
component={comp}
|
|
reading={UnitMeasurement.convertLastMeasurement(umArray)}
|
|
/>
|
|
}
|
|
/>
|
|
);
|
|
};
|
|
|
|
const graphCard = (component: Component, unitMeasurements: UnitMeasurement[]) => {
|
|
return (
|
|
<Card raised key={component.key()} style={{ padding: 10, marginBottom: 15 }}>
|
|
{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
|
|
);
|
|
}
|
|
})}
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* This function creates the charts for the gate page using the MAIN components on the gate, and delta time, it DOES NOT show any other components that could be on the device
|
|
* @returns list of cards for the charts
|
|
*/
|
|
const sensorGraphs = () => {
|
|
const tempComp = compMap.get(tempChain)
|
|
const ambientComp = compMap.get(ambient)
|
|
let graphs: JSX.Element[] = []
|
|
|
|
//pressure
|
|
let pressureComp = compMap.get(pressure)
|
|
let pressureReadings = compMeasurements.get(pressure)
|
|
if(pressureComp && pressureReadings){
|
|
graphs.push(graphCard(pressureComp, pressureReadings))
|
|
}
|
|
|
|
if(tempComp){
|
|
graphs.push(
|
|
<GateDeltaTempGraph deviceID={device} start={startDate} end={endDate} tempComponent={tempComp} ambient={ambientComp}/>
|
|
)
|
|
}
|
|
//T/H chain
|
|
let tempReadings = compMeasurements.get(tempChain)
|
|
if(tempComp && tempReadings){
|
|
graphs.push(graphCard(tempComp, tempReadings))
|
|
}
|
|
//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 = () => {
|
|
let tempComp = compMap.get(tempChain)
|
|
let ambientComp = compMap.get(ambient)
|
|
return (
|
|
<Box>
|
|
<GateFlowGraph
|
|
newXDomain={xDomain}
|
|
device={device}
|
|
start={startDate}
|
|
end={endDate}
|
|
gate={gate}
|
|
ambient={ambient}
|
|
pressureComponent={pressure}
|
|
// setPCAState={(state: boolean) => {
|
|
// setPCAState(state);
|
|
// }}
|
|
multiGraphZoom={domain => {
|
|
setXDomain(domain);
|
|
setZoomed(true);
|
|
}}
|
|
/>
|
|
{/* add a new chart here for the delta temp over time */}
|
|
{tempComp &&
|
|
<GateDeltaTempGraph deviceID={device} start={startDate} end={endDate} tempComponent={tempComp} ambient={ambientComp}/>
|
|
}
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
const showGraphs = () => {
|
|
switch (display) {
|
|
case "sensors":
|
|
return sensorGraphs();
|
|
case "analytics":
|
|
return analyticGraphs();
|
|
default:
|
|
return <Box>Unknown Graph Type Selected</Box>;
|
|
}
|
|
};
|
|
|
|
return (
|
|
<React.Fragment>
|
|
<Box display="flex" justifyContent="space-between" alignItems="center" marginBottom={2}>
|
|
<TimeBar updateDateRange={updateDateRange} startDate={startDate} endDate={endDate} />
|
|
{zoomed && (
|
|
<Tooltip title="Zoom Out Graphs">
|
|
<Button variant="outlined" onClick={zoomOut}>
|
|
<ZoomOut />
|
|
</Button>
|
|
</Tooltip>
|
|
)}
|
|
</Box>
|
|
{loading ? <LinearProgress /> : showGraphs()}
|
|
</React.Fragment>
|
|
);
|
|
}
|