some tweaks to the 3d bin and nodes, finished up the table view
This commit is contained in:
parent
325d3ea7a0
commit
619f9b7bf4
9 changed files with 291 additions and 146 deletions
|
|
@ -21,7 +21,7 @@ export interface NodeData {
|
|||
}
|
||||
|
||||
export function BuildNodeData(cables: CableData[]): NodeData[] {
|
||||
const nodeRadius = 20
|
||||
const nodeRadius = 15
|
||||
let nodeData: NodeData[] = []
|
||||
|
||||
cables.forEach((cable, cableIndex) => {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import { pond } from "protobuf-ts/pond";
|
|||
import GrainCableFill from "../Systems/Inventory/GrainCableFill";
|
||||
import TempHeatMap from "../Systems/Heatmap/TempHeatMap";
|
||||
import NodePointCloud from "../Systems/Heatmap/NodePointCloud";
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
interface Props {
|
||||
bin: Bin
|
||||
|
|
@ -35,8 +36,6 @@ interface Props {
|
|||
export default function Bin3dView(props: Props) {
|
||||
const { bin, scale = 100, fillPercent, nodeClick, showTempHeatmap, showGrain, showHotspots, showResetButton, showMoistureHeatmap, showTemp, showMoisture } = props
|
||||
|
||||
const binCenter = useMemo(() => new Vector3(0, 0, 0), []);
|
||||
|
||||
// Compute the initial camera radius so the full bin fits in frame.
|
||||
// Uses the perspective camera FOV (default 75°) with a padding factor.
|
||||
// Both the diameter and total height are considered so neither is clipped.
|
||||
|
|
@ -129,7 +128,6 @@ export default function Bin3dView(props: Props) {
|
|||
cableData={cableData}
|
||||
nodeData={nodeData}
|
||||
bin={bin}
|
||||
binCenter={binCenter}
|
||||
onNodeClick={(node, cable) => {
|
||||
if (nodeClick) nodeClick(node, cable)
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
import GrainCable from "./GrainCable";
|
||||
import { Vector3 } from "three";
|
||||
import { Bin } from "models";
|
||||
import React from "react";
|
||||
import { CableData } from "../../Data/BuildCableData";
|
||||
import { NodeData } from "../../Data/BuildNodeData";
|
||||
|
||||
interface Props {
|
||||
cableData: CableData[]
|
||||
nodeData: NodeData[]
|
||||
bin: Bin
|
||||
binCenter: Vector3,
|
||||
cableData: CableData[],
|
||||
nodeData: NodeData[],
|
||||
bin: Bin,
|
||||
renderOrder?: number,
|
||||
displayTemp?: boolean,
|
||||
displayMoisture?: boolean,
|
||||
|
|
@ -17,7 +15,7 @@ interface Props {
|
|||
}
|
||||
|
||||
export default function BinCables(props: Props){
|
||||
const {bin, binCenter, cableData, nodeData, onNodeClick, renderOrder, displayTemp, displayMoisture} = props
|
||||
const {bin, cableData, nodeData, onNodeClick, renderOrder, displayTemp, displayMoisture} = props
|
||||
return (
|
||||
<React.Fragment>
|
||||
{cableData.map((cable, i) => {
|
||||
|
|
@ -29,7 +27,6 @@ export default function BinCables(props: Props){
|
|||
cable={cable}
|
||||
nodes={cableNodes}
|
||||
bin={bin}
|
||||
binCenter={binCenter}
|
||||
displayTemp={displayTemp}
|
||||
displayMoisture={displayMoisture}
|
||||
onNodeClick={(node) => {
|
||||
|
|
|
|||
|
|
@ -1,21 +1,18 @@
|
|||
import Sphere, { SphereGeometry } from "3dModels/Shapes/3D/Sphere"
|
||||
import { useFrame, useThree } from "@react-three/fiber"
|
||||
import { Billboard, Text } from "@react-three/drei"
|
||||
import React, { useMemo, useState } from "react"
|
||||
import { Text } from "@react-three/drei"
|
||||
import React, { useMemo } from "react"
|
||||
import { Euler, Group, Vector3 } from "three"
|
||||
import { describeMeasurement } from "pbHelpers/MeasurementDescriber"
|
||||
import { quack } from "protobuf-ts/quack"
|
||||
import { green, grey, orange, red } from "@mui/material/colors";
|
||||
import { useGlobalState } from "providers"
|
||||
import { pond } from "protobuf-ts/pond"
|
||||
import Ring, { RingGeometry } from "3dModels/Shapes/3D/Ring"
|
||||
import { NodeData } from "../../Data/BuildNodeData"
|
||||
import { TempToColour } from "bin/3dView/utils/tempToColour"
|
||||
|
||||
interface Props {
|
||||
node: NodeData
|
||||
binCenter: Vector3
|
||||
lowerThreshold: number
|
||||
upperThreshold: number,
|
||||
targetMoisture: number,
|
||||
renderOrder?: number,
|
||||
displayTemp?: boolean,
|
||||
displayMoisture?: boolean,
|
||||
|
|
@ -23,33 +20,31 @@ interface Props {
|
|||
}
|
||||
|
||||
export default function CableNode(props: Props) {
|
||||
const { node, binCenter, lowerThreshold, upperThreshold, onNodeClick, renderOrder, displayTemp, displayMoisture } = props
|
||||
const { node, upperThreshold, targetMoisture, onNodeClick, renderOrder, displayTemp, displayMoisture } = props
|
||||
const { camera } = useThree();
|
||||
const [{ user }] = useGlobalState();
|
||||
// const [showLabel, setShowLabel] = useState(false);
|
||||
const labelGroupRef = React.useRef<Group>(null);
|
||||
const ROW_HEIGHT = 35;
|
||||
const PADDING = 20;
|
||||
const ROW_HEIGHT = 45;
|
||||
const PADDING = 15;
|
||||
const LABEL_WIDTH = 220;
|
||||
const flagRef = React.useRef<Group>(null);
|
||||
|
||||
//this can be used to control label visibility through the cameras zoom level
|
||||
// useFrame(() => {
|
||||
// const distance = camera.position.distanceTo(binCenter)
|
||||
|
||||
// const SHOW_DISTANCE = 15;
|
||||
// const HIDE_DISTANCE = 16;
|
||||
|
||||
// if (!showLabel && distance < SHOW_DISTANCE) {
|
||||
// setShowLabel(true);
|
||||
// } else if (showLabel && distance > HIDE_DISTANCE) {
|
||||
// setShowLabel(false);
|
||||
// }
|
||||
|
||||
// const scale = distance * 0.07;
|
||||
|
||||
// if (labelGroupRef.current) {
|
||||
// labelGroupRef.current.scale.set(scale, scale, 1);
|
||||
// }
|
||||
// });
|
||||
useFrame(() => {
|
||||
if (!flagRef.current || !showLabel) return;
|
||||
|
||||
// Get camera's right direction in world space
|
||||
const cameraRight = new Vector3();
|
||||
camera.getWorldDirection(cameraRight); // forward
|
||||
const up = new Vector3(0, 1, 0);
|
||||
cameraRight.crossVectors(cameraRight, up).normalize(); // right = forward × up
|
||||
|
||||
const pos = node.position.clone();
|
||||
pos.addScaledVector(cameraRight, LABEL_WIDTH / 2); // offset right in screen space
|
||||
pos.addScaledVector(camera.position.clone().sub(node.position).normalize(), 1); // nudge toward camera
|
||||
|
||||
flagRef.current.position.copy(pos);
|
||||
flagRef.current.quaternion.copy(camera.quaternion);
|
||||
});
|
||||
|
||||
const tempDisplay = useMemo(() => {
|
||||
if (user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
|
||||
|
|
@ -63,7 +58,7 @@ export default function CableNode(props: Props) {
|
|||
|
||||
if (node.excluded) {
|
||||
if (!anyToggleOn) return [];
|
||||
return [{ text: "Excluded", color: "red" }];
|
||||
return [{ text: "Excluded", color: grey[700] }];
|
||||
}
|
||||
|
||||
const r: { text: string; color: string }[] = [];
|
||||
|
|
@ -72,7 +67,9 @@ export default function CableNode(props: Props) {
|
|||
if (displayTemp && node.celcius !== 0) {
|
||||
r.push({
|
||||
text: tempDisplay,
|
||||
color: describeMeasurement(quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE).colour()
|
||||
// color: describeMeasurement(quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE).colour()
|
||||
//will be green when below threshold, orange when it goeas above but by less than 5 degrees, and red if more than 5 over
|
||||
color: node.celcius > upperThreshold ? node.celcius > upperThreshold + 5 ? red[700] : orange[800]: green[800]
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -81,21 +78,25 @@ export default function CableNode(props: Props) {
|
|||
if (node.moisture && node.moisture > 0) {
|
||||
r.push({
|
||||
text: `${node.moisture.toFixed(2)}% EMC`,
|
||||
color: describeMeasurement(quack.MeasurementType.MEASUREMENT_TYPE_GRAIN_EMC).colour()
|
||||
// color: describeMeasurement(quack.MeasurementType.MEASUREMENT_TYPE_GRAIN_EMC).colour()
|
||||
color: node.moisture > targetMoisture ? node.moisture > targetMoisture + 0.5 ? red[700] : orange[800]: green[800]
|
||||
});
|
||||
} else if (node.humidity && node.humidity > 0) {
|
||||
r.push({
|
||||
text: `${node.humidity.toFixed(2)}%`,
|
||||
color: describeMeasurement(quack.MeasurementType.MEASUREMENT_TYPE_PERCENT).colour()
|
||||
});
|
||||
}
|
||||
}
|
||||
//apparently we dont want to display humidity at all
|
||||
// else if (node.humidity && node.humidity > 0) {
|
||||
// r.push({
|
||||
// text: `${node.humidity.toFixed(2)}%`,
|
||||
// // color: describeMeasurement(quack.MeasurementType.MEASUREMENT_TYPE_PERCENT).colour()
|
||||
// color: "black"
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
||||
const hasData = r.length > 0;
|
||||
|
||||
// Only show top node if there's actual data
|
||||
if (node.topNode && hasData) {
|
||||
r.unshift({ text: "Top Node", color: "yellow" });
|
||||
r.unshift({ text: "Top Node", color: grey[700] });
|
||||
}
|
||||
|
||||
return r;
|
||||
|
|
@ -117,12 +118,8 @@ export default function CableNode(props: Props) {
|
|||
|
||||
const topNodeRotation = React.useMemo(() => new Euler(-Math.PI / 2, 0, 0), []);
|
||||
|
||||
const labelPosition = node.position.clone();
|
||||
const labelHeight = rows.length * ROW_HEIGHT + PADDING;
|
||||
|
||||
const dir = camera.position.clone().sub(node.position).normalize();
|
||||
labelPosition.add(dir.multiplyScalar(1));
|
||||
|
||||
const getRowY = (index: number) => {
|
||||
const totalHeight = rows.length * ROW_HEIGHT;
|
||||
return totalHeight / 2 - (index * ROW_HEIGHT) - ROW_HEIGHT / 2;
|
||||
|
|
@ -130,9 +127,7 @@ export default function CableNode(props: Props) {
|
|||
|
||||
const nodeColour = () => {
|
||||
if (node.excluded) return "grey";
|
||||
if (!node.inGrain) return "white";
|
||||
const c = TempToColour(node, lowerThreshold, upperThreshold, "node");
|
||||
return c !== null ? c.getStyle() : "white";
|
||||
return "white";
|
||||
};
|
||||
|
||||
const handleClick = (e: any) => {
|
||||
|
|
@ -144,49 +139,46 @@ export default function CableNode(props: Props) {
|
|||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{(!node.inGrain || !showLabel) &&
|
||||
<React.Fragment>
|
||||
<Sphere
|
||||
geometry={geometry}
|
||||
position={node.position}
|
||||
colour={nodeColour()}
|
||||
onClick={handleClick}
|
||||
renderOrder={renderOrder}
|
||||
|
||||
/>
|
||||
{node.topNode && (
|
||||
<Ring geometry={topNodeGeo} position={topNodePosition} rotation={topNodeRotation} renderOrder={renderOrder}/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
}
|
||||
<React.Fragment>
|
||||
<Sphere
|
||||
geometry={geometry}
|
||||
position={node.position}
|
||||
colour={nodeColour()}
|
||||
onClick={handleClick}
|
||||
renderOrder={renderOrder}
|
||||
|
||||
/>
|
||||
{node.topNode && (
|
||||
<Ring geometry={topNodeGeo} position={topNodePosition} rotation={topNodeRotation} renderOrder={renderOrder}/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
{node.inGrain && showLabel &&
|
||||
<Billboard position={labelPosition}>
|
||||
<group ref={labelGroupRef} renderOrder={999} onClick={handleClick}>
|
||||
<mesh position={[0, 0, -1]}>
|
||||
<planeGeometry args={[200, labelHeight]} />
|
||||
<meshBasicMaterial
|
||||
color="white"
|
||||
transparent
|
||||
opacity={0.9}
|
||||
depthWrite={false}
|
||||
depthTest={false}
|
||||
/>
|
||||
</mesh>
|
||||
{rows.map((row, i) => (
|
||||
<Text
|
||||
key={i}
|
||||
position={[0, getRowY(i), 0]}
|
||||
fontSize={30}
|
||||
color={row.color}
|
||||
anchorX="center"
|
||||
anchorY="middle"
|
||||
>
|
||||
{row.text}
|
||||
</Text>
|
||||
))}
|
||||
</group>
|
||||
</Billboard>
|
||||
}
|
||||
<group ref={flagRef} renderOrder={999} onClick={handleClick}>
|
||||
<mesh position={[0, 0, -1]}>
|
||||
<planeGeometry args={[LABEL_WIDTH, labelHeight]} />
|
||||
<meshBasicMaterial
|
||||
color="white"
|
||||
transparent
|
||||
opacity={0.9}
|
||||
depthWrite={false}
|
||||
depthTest={false}
|
||||
/>
|
||||
</mesh>
|
||||
{rows.map((row, i) => (
|
||||
<Text
|
||||
key={i}
|
||||
position={[0, getRowY(i), 0]}
|
||||
fontSize={35}
|
||||
fontWeight={650}
|
||||
color={row.color}
|
||||
anchorX="center"
|
||||
anchorY="middle"
|
||||
>
|
||||
{row.text}
|
||||
</Text>
|
||||
))}
|
||||
</group>
|
||||
}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
|
@ -7,17 +7,16 @@ import { CableData } from "../../Data/BuildCableData"
|
|||
import { NodeData } from "../../Data/BuildNodeData"
|
||||
|
||||
interface Props {
|
||||
cable: CableData
|
||||
nodes: NodeData[]
|
||||
bin: Bin
|
||||
binCenter: Vector3,
|
||||
cable: CableData,
|
||||
nodes: NodeData[],
|
||||
bin: Bin,
|
||||
renderOrder?: number,
|
||||
displayTemp?: boolean,
|
||||
displayMoisture?: boolean
|
||||
displayMoisture?: boolean,
|
||||
onNodeClick?: (node: NodeData) => void
|
||||
}
|
||||
export default function GrainCable(props: Props) {
|
||||
const {cable, nodes, bin, binCenter, onNodeClick, renderOrder, displayTemp, displayMoisture} = props
|
||||
const {cable, nodes, bin, onNodeClick, renderOrder, displayTemp, displayMoisture} = props
|
||||
|
||||
const calculateHeight = () => {
|
||||
return cable.topPosition.distanceTo(cable.bottomPosition)
|
||||
|
|
@ -44,7 +43,7 @@ export default function GrainCable(props: Props) {
|
|||
<React.Fragment>
|
||||
<Cylinder geometry={geometry} position={calculateCenter()} opacity={0.5} renderOrder={renderOrder} depthWrite={false} depthTest={false}/>
|
||||
{nodes.map((node, i) => (
|
||||
<CableNode displayTemp={displayTemp} displayMoisture={displayMoisture} onNodeClick={onNodeClick} key={"node " + i} renderOrder={renderOrder} node={node} binCenter={binCenter} lowerThreshold={bin.lowerTempThreshold()} upperThreshold={bin.upperTempThreshold()}/>
|
||||
<CableNode displayTemp={displayTemp} displayMoisture={displayMoisture} onNodeClick={onNodeClick} key={"node " + i} renderOrder={renderOrder} node={node} targetMoisture={bin.targetMoisture()} upperThreshold={bin.upperTempThreshold()}/>
|
||||
))}
|
||||
</React.Fragment>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -47,12 +47,12 @@ export default function BinSummary(props: Props){
|
|||
<Box padding={2}>
|
||||
<Grid2 container spacing={2}>
|
||||
<Grid2 size={isMobile ? 12 : 8}>
|
||||
<Card>
|
||||
<Card raised>
|
||||
{bin3dView()}
|
||||
</Card>
|
||||
</Grid2>
|
||||
<Grid2 size={isMobile ? 12 : 4}>
|
||||
<Card>
|
||||
<Card raised sx={{height: "100%"}}>
|
||||
{tableView()}
|
||||
</Card>
|
||||
</Grid2>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { Box, Checkbox, Grid2, FormControlLabel } from "@mui/material";
|
||||
import { Box, Checkbox, FormControlLabel, List, ListItem } from "@mui/material";
|
||||
import Bin3dView from "bin/3dView/Scene/Bin3dView";
|
||||
import { Bin } from "models";
|
||||
import React from "react";
|
||||
import { useState } from "react";
|
||||
|
||||
interface Props {
|
||||
|
|
@ -19,13 +20,13 @@ export default function bin3dVisualizer(props: Props){
|
|||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Grid2 container spacing={1}>
|
||||
<Grid2 size={3}>
|
||||
<FormControlLabel
|
||||
const controlsOverlay = () => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<List>
|
||||
<ListItem>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
value={showHeatmap}
|
||||
|
|
@ -37,7 +38,9 @@ export default function bin3dVisualizer(props: Props){
|
|||
}
|
||||
label={"Toggle Heatmap"}
|
||||
/>
|
||||
<FormControlLabel
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
value={showHotspots}
|
||||
|
|
@ -49,7 +52,9 @@ export default function bin3dVisualizer(props: Props){
|
|||
}
|
||||
label={"Toggle Hotspots"}
|
||||
/>
|
||||
<FormControlLabel
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
value={showFill}
|
||||
|
|
@ -61,7 +66,9 @@ export default function bin3dVisualizer(props: Props){
|
|||
}
|
||||
label={"Toggle Hotspots"}
|
||||
/>
|
||||
<FormControlLabel
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
value={showTemp}
|
||||
|
|
@ -73,7 +80,10 @@ export default function bin3dVisualizer(props: Props){
|
|||
}
|
||||
label={"Toggle Temp"}
|
||||
/>
|
||||
<FormControlLabel
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
value={showMoisture}
|
||||
|
|
@ -85,21 +95,38 @@ export default function bin3dVisualizer(props: Props){
|
|||
}
|
||||
label={"Toggle Moisture"}
|
||||
/>
|
||||
</Grid2>
|
||||
<Grid2 size={9}>
|
||||
<Box height={750}>
|
||||
<Bin3dView
|
||||
bin={bin}
|
||||
scale={100}
|
||||
showTempHeatmap={showHeatmap}
|
||||
showTemp={showTemp}
|
||||
showMoisture={showMoisture}
|
||||
showGrain={showFill}
|
||||
showHotspots={showHotspots}
|
||||
/>
|
||||
</Box>
|
||||
</Grid2>
|
||||
</Grid2>
|
||||
</ListItem>
|
||||
</List>
|
||||
|
||||
|
||||
|
||||
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
const binViewer = () => {
|
||||
return (
|
||||
<Bin3dView
|
||||
bin={bin}
|
||||
scale={100}
|
||||
showTempHeatmap={showHeatmap}
|
||||
showTemp={showTemp}
|
||||
showMoisture={showMoisture}
|
||||
showGrain={showFill}
|
||||
showHotspots={showHotspots}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Box position={"relative"} height={750}>
|
||||
{binViewer()}
|
||||
<Box position={"absolute"} top={10} left={10}>
|
||||
{controlsOverlay()}
|
||||
</Box>
|
||||
</Box>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,37 +1,165 @@
|
|||
import { Box } from "@mui/material";
|
||||
import { Box, Table, TableBody, TableCell, TableHead, TableRow } from "@mui/material";
|
||||
import { green, grey, orange, red } from "@mui/material/colors";
|
||||
import { Bin } from "models";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import { useGlobalState } from "providers";
|
||||
import React from "react";
|
||||
import { useMemo } from "react";
|
||||
import { avg, celsiusToFahrenheit } from "utils";
|
||||
|
||||
interface Props {
|
||||
bin: Bin
|
||||
}
|
||||
|
||||
interface LevelAvg {
|
||||
temp: number
|
||||
moisture: number
|
||||
inGrain: boolean
|
||||
|
||||
interface BinLevel {
|
||||
grainTemps: number[]
|
||||
airTemps: number[]
|
||||
grainMoistures: number[]
|
||||
airMoistures: number[]
|
||||
}
|
||||
|
||||
|
||||
export default function BinTableView(props: Props) {
|
||||
const {bin} = props
|
||||
const [{user}] = useGlobalState()
|
||||
|
||||
const cableLevelAverages = useMemo(() => {
|
||||
const binLevels = useMemo(() => {
|
||||
const cables = bin.status.grainCables
|
||||
let levels: LevelAvg[] = []
|
||||
console.log(cables)
|
||||
let levels: BinLevel[] = []
|
||||
//note for future self, the first item in the measurement arrays is the lowest node on the cable
|
||||
//the zones we are creating here will match with the node number because most cables will either have the same number of nodes or only have a difference of 1 or 2
|
||||
//example: cable 1 has 5 nodes cable 2 has 7 nodes, the first five nodes of the cables will match up and average together for each zone
|
||||
//and then zones 6 and 7 will be made using only cable 2 because cable 1 doesn't have them
|
||||
cables.forEach(cable => {
|
||||
|
||||
cable.celcius.forEach((c, index) => {
|
||||
//remember index will be 0 based but the top node starts at 1 for the lowest cable
|
||||
let inGrain = index < cable.topNode
|
||||
if (levels[index]){
|
||||
if(cable.excludedNodes.includes(index+1)) return
|
||||
if(inGrain){
|
||||
levels[index].grainTemps.push(c)
|
||||
//need to check that the cable not only has the moisture mutation on it, but also that it reads humidity
|
||||
//cables that dont read humidity return an array of 0 values so if the average of all of the values is 0 then it is a temp only cable
|
||||
if(cable.moisture.length > 0 && avg(cable.moisture) > 0){
|
||||
//based on all the other checks and the fact that the indexes between the array should match this should never be 0 but put a guard here just in case
|
||||
//also note that we are only doing moisture and not humidity, it seems like users dont care about humidity
|
||||
levels[index].grainMoistures.push(cable.moisture[index] ?? 0)
|
||||
}
|
||||
}else{
|
||||
levels[index].airTemps.push(c)
|
||||
//need to check that the cable not only has the moisture mutation on it, but also that it reads humidity
|
||||
//cables that dont read humidity return an array of 0 values so if the average of all of the values is 0 then it is a temp only cable
|
||||
if(cable.moisture.length > 0 && avg(cable.moisture) > 0){
|
||||
//based on all the other checks and the fact that the indexes between the array should match this should never be 0 but put a guard here just in case
|
||||
levels[index].airMoistures.push(cable.moisture[index] ?? 0)
|
||||
}
|
||||
}
|
||||
}else{
|
||||
let newlevel: BinLevel = {
|
||||
grainTemps: [],
|
||||
grainMoistures: [],
|
||||
airTemps: [],
|
||||
airMoistures: []
|
||||
}
|
||||
if(!cable.excludedNodes.includes(index)){
|
||||
if(inGrain){
|
||||
newlevel.grainTemps.push(c)
|
||||
if(cable.moisture.length > 0 && avg(cable.moisture) > 0){
|
||||
newlevel.grainMoistures.push(cable.moisture[index] ?? 0)
|
||||
}
|
||||
}else{
|
||||
newlevel.airTemps.push(c)
|
||||
if(cable.moisture.length > 0 && avg(cable.moisture) > 0){
|
||||
newlevel.airMoistures.push(cable.moisture[index] ?? 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
levels.push(newlevel)
|
||||
}
|
||||
|
||||
})
|
||||
})
|
||||
//flip the levels so that the highest nodes are on the top
|
||||
levels.reverse()
|
||||
return levels
|
||||
},[bin])
|
||||
|
||||
const levelDisplay = (level: BinLevel) => {
|
||||
let tempDisplay = "--"
|
||||
let moistureDisplay = "--"
|
||||
let lvlTemp
|
||||
let lvlMoisture
|
||||
let tempColour: string = grey[600]
|
||||
let moistureColour: string = grey[600]
|
||||
|
||||
//determine the temp
|
||||
if(level.grainTemps.length > 0){
|
||||
lvlTemp = avg(level.grainTemps)
|
||||
//if the average temp is 5 degrees above the bins threshold
|
||||
if(lvlTemp > (bin.upperTempThreshold() + 5)){
|
||||
tempColour = red[700]
|
||||
}else if(lvlTemp > bin.upperTempThreshold()){
|
||||
//if the average temp is greater than 5 degrees over
|
||||
tempColour = orange[600]
|
||||
}else {
|
||||
tempColour = green[300]
|
||||
}
|
||||
}else if (level.airTemps.length > 0){
|
||||
lvlTemp = avg(level.airTemps)
|
||||
}
|
||||
if (lvlTemp){
|
||||
if(user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT){
|
||||
lvlTemp = celsiusToFahrenheit(lvlTemp)
|
||||
}
|
||||
tempDisplay = lvlTemp.toFixed(2) + (user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT ? " F" : " C")
|
||||
}
|
||||
|
||||
//determine the moisture to show
|
||||
if(level.grainMoistures.length > 0){
|
||||
lvlMoisture = avg(level.grainMoistures)
|
||||
if(lvlMoisture > (bin.targetMoisture() + 1.5)){
|
||||
moistureColour = red[700]
|
||||
}else if(lvlMoisture > bin.targetMoisture()){
|
||||
//if the average temp is greater than 5 degrees over
|
||||
moistureColour = orange[600]
|
||||
}else {
|
||||
moistureColour = green[300]
|
||||
}
|
||||
}else if (level.airMoistures.length > 0){
|
||||
lvlMoisture = avg(level.airMoistures)
|
||||
}
|
||||
if(lvlMoisture){
|
||||
moistureDisplay = lvlMoisture.toFixed(2) + "%"
|
||||
}
|
||||
return (
|
||||
<React.Fragment>
|
||||
<TableCell sx={{color: "black", fontWeight: 650, fontSize: 17, textAlign: "center", backgroundColor: tempColour}}>{tempDisplay}</TableCell>
|
||||
<TableCell sx={{color: "black", fontWeight: 650, fontSize: 17, textAlign: "center", backgroundColor: moistureColour}}>{moistureDisplay}</TableCell>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<Box>Table View</Box>
|
||||
<Box padding={2}>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell sx={{ fontWeight: 650, fontSize: 20, textAlign: "center"}}>Level</TableCell>
|
||||
<TableCell sx={{ fontWeight: 650, fontSize: 20, textAlign: "center"}}>Temperature</TableCell>
|
||||
<TableCell sx={{ fontWeight: 650, fontSize: 20, textAlign: "center"}}>Moisture</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{binLevels.map((level, i) => (
|
||||
<TableRow>
|
||||
<TableCell sx={{color: "gray", fontWeight: 650, fontSize: 17, textAlign: "center"}}>{binLevels.length - i}</TableCell>
|
||||
{levelDisplay(level)}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue