import { Box, Checkbox, FormControl, MenuItem, List, ListItem, InputLabel, Select, Typography, Stack, FormControlLabel, useTheme } from "@mui/material"; import Bin3dView from "bin/3dView/Scene/Bin3dView"; import { Bin, Component, Device } from "models"; import { Controller } from "models/Controller"; import { pond, quack } from "protobuf-ts/pond"; import React, { useEffect } from "react"; import { useState } from "react"; import NodeControls from "./binSummary/components/nodeControls"; import { NodeData } from "bin/3dView/Data/BuildNodeData"; import { CableData } from "bin/3dView/Data/BuildCableData"; import ModeChangeDialog from "bin/conditioning/modeChangeDialog"; import { cloneDeep } from "lodash"; import { useMobile } from "hooks"; interface Props { bin: Bin // cables?: GrainCable[] devices: Device[] // fans?: Controller[] // heaters?: Controller[] permissions: pond.Permission[] componentDevices: Map componentMap: Map binPrefs?: Map updateBinCallback?: (bin: Bin) => void } export default function bin3dVisualizer(props: Props){ const {bin, permissions, devices, componentDevices, componentMap, binPrefs, updateBinCallback} = props const theme = useTheme() const [showHeatmap, setShowHeatmap] = useState(true) // const [showHotspots, setShowHotspots] = useState(false) // const [showFill, setShowFill] = useState(false) const [showTemp, setShowTemp] = useState(true) const [showMoisture, setShowMoisture] = useState(false) const [showMoistureHeatmap, setShowMoistureHeatmap] = useState(false) const [binDisplay, setBinDisplay] = useState("temp") const [binMode, setBinMode] = useState(pond.BinMode.BIN_MODE_NONE) const [openModeChange, setOpenModeChange] = useState(false) const [selectedCable, setSelectedCable] = useState(undefined) const [selectedNode, setSelectedNode] = useState(undefined) const [selectedCableDevice, setSelectedCableDevice] = useState(Device.create()) //this is the device that the cable that has the node that was clicked on belongs to const [filteredComponents, setFilteredComponents] = useState([]) //components that are filtered to only include ones on the same device const [openNodeControls, setOpenNodeControls] = useState(false) const [devMap, setDevMap] = useState>(new Map()) const [modeChangeInProgress, setModeChangeInProgress] = useState(false) const [showLabels, setShowLabels] = useState(true) const isMobile = useMobile() useEffect(()=>{ let newMap: Map = new Map() devices.forEach(d => { newMap.set(d.id(), d) }) setDevMap(newMap) },[devices]) useEffect(()=>{ setBinMode(bin.settings.mode) },[bin]) const updateBin = () => { if(updateBinCallback){ let clone = cloneDeep(bin) clone.settings.mode = binMode updateBinCallback(clone) } }; const binModeControl = () => { return ( {bin.name()} ) } const heatmapDisplay = () => { return ( Heatmap {/* Checkbox moved below dropdown — no longer affects top alignment */} setShowLabels(checked)} /> } label={Show Values} sx={{ mt: 0.5, ml: '-9px' }} /> ) } // const controllerState = (controller: Controller) => { // let state = false // controller.status.lastGoodMeasurement.forEach(um => { // if(um.type === quack.MeasurementType.MEASUREMENT_TYPE_BOOLEAN){ // if(um.values.length > 0){ // let readings = um.values // if(readings[readings.length-1].values.length > 0){ // let nodes = readings[readings.length-1].values // if (nodes[nodes.length -1] === 1){ // state = true // } // } // } // } // }) // return state // } const controllerDisplay = () => { if ((bin.status.fans && bin.status.fans.length > 0) || (bin.status.heaters && bin.status.heaters.length > 0)){ return ( Controllers {bin.status.fans && bin.status.fans.map(fan => { // let isOn = controllerState(fan) return ( {fan.name} {fan.state ? "ON" : "OFF"} )})} {bin.status.heaters && bin.status.heaters.map(heater => { // let isOn = controllerState(heater) return ( {heater.name} {heater.state ? "ON" : "OFF"} )})} // loop through controllers displaying each one ) }else{ return } } const desktopHUD = () => { return ( {binModeControl()} {heatmapDisplay()} {controllerDisplay()} ) } const mobileHUD = () => { return ( {/* Top-left: Bin Name / Mode */} {binModeControl()} {/* Top-right: Heatmap Display */} {heatmapDisplay()} {/* Bottom-left: Controller Status */} {controllerDisplay()} ) } return ( {(selectedCable && selectedNode) && { setOpenNodeControls(false) }} /> } { setOpenModeChange(false) if(refresh) { updateBin() }else{ setBinMode(bin.settings.mode) }; }} defaultTargetMoisture={bin.settings.inventory?.initialMoisture} // defaultOutdoorTemp={outdoorTemp} // defaultOutdoorHumidity={outdoorHumidity} // changeTargetMoisture={newMoisture => { // setTargetMoisture(newMoisture) // }} // changeOutdoorTemp={newTemp => { // setOutdoorTemp(newTemp) // }} // changeOutdoorHumidity={newHumidity => { // setOutdoorHumidity(newHumidity) // }} startChange={() => { setModeChangeInProgress(true) }} changeComplete={() => { setModeChangeInProgress(false) }} /> { //this will be how to control the dialog to update the top nodes and node exclusion etc (make new component called NodeControls for this) //use the cable data to get the device it belongs to let d = devMap.get(cable.grainCable.device) if(d !== undefined){ setSelectedNode(node) setSelectedCable(cable) setSelectedCableDevice(d) //filter the controls so that only components on THIS device are options for new interactions, a side note of this using the components that are in the bins status //is that it will indirectly also filter by bin as well so only components that are both on the bin and the same device as the cable will appear let filtered: Component[] = [] if(bin.status.fans){ bin.status.fans.forEach((f) => { if(componentDevices.get(f.key) === d.id()){ let comp = componentMap.get(f.key) if(comp){ filtered.push(comp) } } }) } if(bin.status.heaters){ bin.status.heaters.forEach((h) => { if(componentDevices.get(h.key) === d.id()){ let comp = componentMap.get(h.key) if(comp){ filtered.push(comp) } } }) } //also make sure to push the cable into the list of filtered components for the source let c = componentMap.get(cable.grainCable.key) if(c !== undefined){ filtered.push(c) } setFilteredComponents(filtered) setOpenNodeControls(true) } }} scale={100} showTempHeatmap={showHeatmap} showMoistureHeatmap={showMoistureHeatmap} showTemp={showTemp && showLabels} showMoisture={showMoisture && showLabels} xOffset={isMobile ? undefined : -120} /> {isMobile ? mobileHUD() : desktopHUD()} ) }