308 lines
10 KiB
TypeScript
308 lines
10 KiB
TypeScript
import { Box, Grid2 as Grid, Typography, Theme, Divider } from "@mui/material";
|
|
import GrainDescriber from "grain/GrainDescriber";
|
|
import { Component } from "models";
|
|
import { convertedUnitMeasurement } from "models/UnitMeasurement";
|
|
import moment from "moment";
|
|
import { pond } from "protobuf-ts/pond";
|
|
import React, { useEffect, useState } from "react";
|
|
import { Plenum } from "models/Plenum";
|
|
import { Pressure } from "models/Pressure";
|
|
import { GrainCable } from "models/GrainCable";
|
|
import { extension, Summary } from "pbHelpers/ComponentType";
|
|
import { makeStyles } from "@mui/styles";
|
|
|
|
interface Props {
|
|
component: Component | Plenum | Pressure | GrainCable;
|
|
reading?: convertedUnitMeasurement | null;
|
|
tableCell?: boolean;
|
|
centered?: boolean;
|
|
dense?: boolean;
|
|
omitTime?: boolean;
|
|
onlyRecent?: boolean;
|
|
largeText?: boolean;
|
|
boldMeasurements?: boolean;
|
|
}
|
|
|
|
const useStyles = makeStyles((theme: Theme) => {
|
|
return ({
|
|
"@keyframes ripple": {
|
|
from: {
|
|
transform: "scale(.8)",
|
|
opacity: 1
|
|
},
|
|
to: {
|
|
transform: "scale(1.4)",
|
|
opacity: 0
|
|
}
|
|
},
|
|
on: {
|
|
boxShadow: `0 0 0 2px ${theme.palette.background.paper}`,
|
|
animationName: "$ripple",
|
|
animationDuration: "1.2s",
|
|
animationTimingFunction: "ease-in-out",
|
|
animationIterationCount: "infinite"
|
|
},
|
|
off: {
|
|
backgroundColor: "transparent"
|
|
},
|
|
divider: {
|
|
margin: theme.spacing(1)
|
|
}
|
|
})
|
|
});
|
|
|
|
export default function UnitMeasurementSummary(props: Props) {
|
|
const {
|
|
component,
|
|
reading,
|
|
tableCell,
|
|
centered,
|
|
dense,
|
|
onlyRecent,
|
|
largeText,
|
|
boldMeasurements
|
|
} = props;
|
|
const [summaries, setSummaries] = useState<Summary[]>([]);
|
|
const [timestamp, setTimestamp] = useState("");
|
|
const [recent, setRecent] = useState(true);
|
|
const classes = useStyles();
|
|
|
|
useEffect(() => {
|
|
if (reading) {
|
|
if (onlyRecent) {
|
|
setRecent(
|
|
moment().diff(moment(reading.timestamp), "milliseconds") <=
|
|
component.settings.reportPeriodMs
|
|
);
|
|
}
|
|
setTimestamp(reading.timestamp);
|
|
setSummaries(
|
|
extension(component.type(), component.subType()).unitMeasurementSummary(
|
|
reading
|
|
)
|
|
);
|
|
}
|
|
}, [component, reading]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
const noSummary = () => {
|
|
return (
|
|
<Typography variant="body1" color="textPrimary">
|
|
No Data
|
|
</Typography>
|
|
);
|
|
};
|
|
|
|
const noRecent = () => {
|
|
return (
|
|
<Typography variant="body1" color="textPrimary">
|
|
No Recent Data
|
|
</Typography>
|
|
);
|
|
};
|
|
|
|
const getSummaryComponent = (summaries: Summary[]): any => {
|
|
if (summaries.length < 1) {
|
|
return noSummary();
|
|
}
|
|
|
|
let overlays: JSX.Element[] = [];
|
|
let grain = GrainDescriber(component.settings.grainType);
|
|
return (
|
|
<Grid container>
|
|
<Grid size={{ xs: 12 }}>
|
|
<Typography variant={largeText ? "body1" : "caption"} color="textPrimary">
|
|
{summaries.map((summary: Summary, index: number) => {
|
|
let sumVal = summary.value;
|
|
if (component.settings.hasOverlays) {
|
|
let overlay: pond.ComponentOverlays = pond.ComponentOverlays.create();
|
|
let found = false;
|
|
component.settings.overlays.forEach(ov => {
|
|
if (ov.measurementType === summary.type) {
|
|
//check if the sum value is within the overlay min-max, currently only checks based off the first node
|
|
//TODO-CS: possibly consider having more overlays by checking all nodes
|
|
let numVal = parseFloat(sumVal);
|
|
if (numVal >= ov.min && numVal <= ov.max) {
|
|
overlay = ov;
|
|
found = true;
|
|
}
|
|
}
|
|
});
|
|
if (found) {
|
|
overlays.push(
|
|
<React.Fragment key={"overlay" + index}>
|
|
<Grid >
|
|
<Box
|
|
width="20px"
|
|
height="20px"
|
|
borderRadius="50%"
|
|
className={classes.on}
|
|
style={{ background: overlay.colour }}
|
|
/>
|
|
</Grid>
|
|
<Grid >
|
|
<Typography
|
|
variant={largeText ? "body1" : "caption"}
|
|
component="span"
|
|
style={{ marginLeft: 5, marginRight: 5 }}>
|
|
{overlay.message}
|
|
</Typography>
|
|
</Grid>
|
|
</React.Fragment>
|
|
);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<span key={index}>
|
|
{summary.label}
|
|
{summary.error && <span style={{ color: "red" }}>{" [error(s)]"}</span>}
|
|
{": "}
|
|
<span style={{ color: summary.colour, fontWeight: boldMeasurements ? 650 : 500 }}>
|
|
{sumVal}
|
|
</span>{" "}
|
|
<br />
|
|
</span>
|
|
);
|
|
})}
|
|
</Typography>
|
|
</Grid>
|
|
{component.settings.grainType !== pond.Grain.GRAIN_NONE &&
|
|
component.settings.grainType !== pond.Grain.GRAIN_INVALID && (
|
|
<Typography variant={largeText ? "body1" : "caption"} color="textPrimary">
|
|
<span key={"grainType"}>
|
|
Grain Type: <span style={{ color: grain.colour }}>{grain.name}</span>
|
|
</span>
|
|
</Typography>
|
|
)}
|
|
{!props.omitTime && (
|
|
<Grid size={{ xs: 12 }}>
|
|
<Typography color="textSecondary" variant={largeText ? "body1" : "caption"}>
|
|
{moment(timestamp).fromNow()}
|
|
</Typography>
|
|
</Grid>
|
|
)}
|
|
{overlays.length > 0 && (
|
|
<React.Fragment>
|
|
<Grid size={{ xs: 12 }}>
|
|
<Divider variant="middle" className={classes.divider} />
|
|
</Grid>
|
|
<Grid container direction="row">
|
|
{overlays}
|
|
</Grid>
|
|
</React.Fragment>
|
|
)}
|
|
</Grid>
|
|
);
|
|
};
|
|
|
|
const getTableCellSummaryComponent = (summaries: Summary[]): any => {
|
|
if (summaries.length < 1 || summaries === undefined) {
|
|
return <span>Unknown</span>;
|
|
}
|
|
const blacklist: Array<any> = [];
|
|
const filteredSummaries = summaries.filter((item: any) => !blacklist.includes(item.label));
|
|
return (
|
|
<Grid container direction="row" justifyContent={centered ? "center" : "flex-start"} spacing={1}>
|
|
{filteredSummaries.map((summary: Summary, i: number) => {
|
|
let sumVal = summary.value;
|
|
return (
|
|
<Grid key={"summary-" + i}>
|
|
{summary.label}
|
|
{summary.error && <span style={{ color: "red" }}>{" [error(s)]"}</span>}
|
|
{": "}
|
|
<span style={{ color: summary.colour, fontWeight: boldMeasurements ? 650 : 500 }}>
|
|
{sumVal}
|
|
</span>{" "}
|
|
{i + 1 !== filteredSummaries.length && ","}
|
|
</Grid>
|
|
);
|
|
})}
|
|
</Grid>
|
|
);
|
|
};
|
|
|
|
const getDenseSummary = (summaries: Summary[]): any => {
|
|
if (summaries.length < 1) {
|
|
return null;
|
|
}
|
|
|
|
const blacklist: Array<any> = ["Temperature (Air)", "Moisture (Air)"];
|
|
const filteredSummaries = summaries.filter(
|
|
(item: any) =>
|
|
!blacklist.includes(item.label) && (item.label !== "Grain Type" || item.value !== "None")
|
|
);
|
|
|
|
let overlays: any = [];
|
|
|
|
return (
|
|
<Box width={"100%"} component="div" key={Math.random()}>
|
|
{filteredSummaries.map((summary: Summary, i: number) => {
|
|
let sumVal = summary.value;
|
|
if (component.settings.hasOverlays) {
|
|
let overlay: pond.ComponentOverlays = pond.ComponentOverlays.create();
|
|
let found = false;
|
|
component.settings.overlays.forEach(ov => {
|
|
if (ov.measurementType === summary.type) {
|
|
//check if the sum value is within the overlay min-max
|
|
let numVal = parseFloat(sumVal);
|
|
if (numVal >= ov.min && numVal <= ov.max) {
|
|
overlay = ov;
|
|
found = true;
|
|
}
|
|
}
|
|
});
|
|
if (found) {
|
|
overlays.push(
|
|
<React.Fragment key={"overlay" + i}>
|
|
<Grid >
|
|
<Box
|
|
width="20px"
|
|
height="20px"
|
|
borderRadius="50%"
|
|
className={classes.on}
|
|
style={{ background: overlay.colour }}
|
|
/>
|
|
</Grid>
|
|
<Grid >
|
|
<Typography
|
|
variant={largeText ? "body1" : "caption"}
|
|
component="span"
|
|
style={{ marginLeft: 5, marginRight: 5 }}>
|
|
{overlay.message}
|
|
</Typography>
|
|
</Grid>
|
|
</React.Fragment>
|
|
);
|
|
}
|
|
}
|
|
return (
|
|
<Typography
|
|
key={"summary-" + i}
|
|
variant={largeText ? "body1" : "caption"}
|
|
component="span">
|
|
<span style={{ color: summary.colour }}>{sumVal}</span>
|
|
{i + 1 !== filteredSummaries.length && ", "}
|
|
</Typography>
|
|
);
|
|
})}
|
|
{!props.omitTime && (
|
|
<Box component="span">
|
|
{" - "}
|
|
<Typography color="textSecondary" variant={largeText ? "body1" : "caption"}>
|
|
{moment(timestamp).fromNow()}
|
|
</Typography>
|
|
</Box>
|
|
)}
|
|
<Grid container direction="row">
|
|
{overlays}
|
|
</Grid>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
if (!recent) return noRecent();
|
|
if (summaries.length <= 0) return noSummary();
|
|
if (dense) return getDenseSummary(summaries);
|
|
if (tableCell) return getTableCellSummaryComponent(summaries);
|
|
return getSummaryComponent(summaries);
|
|
}
|