diff --git a/index.html b/index.html index 31b4ca1..8209efe 100644 --- a/index.html +++ b/index.html @@ -6,7 +6,40 @@ Adaptive Dashboard + + +
diff --git a/indexV2.html b/indexV2.html index 31b4ca1..8209efe 100644 --- a/indexV2.html +++ b/indexV2.html @@ -6,7 +6,40 @@ Adaptive Dashboard + + +
diff --git a/package-lock.json b/package-lock.json index ddc2cac..7028616 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9727,9 +9727,9 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { @@ -10953,7 +10953,11 @@ }, "node_modules/protobuf-ts": { "version": "1.0.0", +<<<<<<< HEAD "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#80ad2f8b81afe9e04f10f9b52a8250e6824d2b41", +======= + "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#c08965a296f2cd0799472fd8b163186cf6885d4c", +>>>>>>> tidio "dependencies": { "protobufjs": "^6.8.8" } @@ -13757,9 +13761,9 @@ } }, "node_modules/vite": { - "version": "6.3.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.6.tgz", - "integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dev": true, "license": "MIT", "dependencies": { diff --git a/src/app/App.css b/src/app/App.css index 89db113..93468b3 100644 --- a/src/app/App.css +++ b/src/app/App.css @@ -44,4 +44,16 @@ html, body, #root { .MuiPopover-root { z-index: 1501 !important; +} + +#tidio-chat-iframe { + bottom: 100px !important; /* Adjust this value – higher number = moved further up from the bottom */ + right: 20px !important; /* Optional: tweak horizontal position if needed */ +} + +/* Optional: Different position on mobile (to avoid overlaps there) */ +@media only screen and (max-width: 980px) { + #tidio-chat-iframe { + bottom: 80px !important; /* Example for mobile – test and adjust */ + } } \ No newline at end of file diff --git a/src/bin/BinSettings.tsx b/src/bin/BinSettings.tsx index 92ced48..61389f9 100644 --- a/src/bin/BinSettings.tsx +++ b/src/bin/BinSettings.tsx @@ -494,7 +494,6 @@ export default function BinSettings(props: Props) { //if the users preferences are in feet convert the distance to cm otherwise it was entered as cm so use that form.inventory.lidarDropCm = getDistanceUnit() === pond.DistanceUnit.DISTANCE_UNIT_FEET ? lidarDropDistance * 30.48 : lidarDropDistance } - console.log(form) binAPI .updateBin(bin.key(), form, as) .then(response => { diff --git a/src/bin/BinVisualizerV2.tsx b/src/bin/BinVisualizerV2.tsx index 8cd41e0..5b3dc62 100644 --- a/src/bin/BinVisualizerV2.tsx +++ b/src/bin/BinVisualizerV2.tsx @@ -3,14 +3,13 @@ import { Box, Button, Card, + CircularProgress, darken, DialogActions, DialogContent, - DialogContentText, DialogTitle, Grid2 as Grid, IconButton, - InputAdornment, lighten, Link, Skeleton, @@ -18,8 +17,6 @@ import { Switch, TextField, Theme, - ToggleButton, - ToggleButtonGroup, Typography, useTheme, } from "@mui/material"; @@ -30,8 +27,8 @@ import HumidityIcon from "component/HumidityIcon"; import TemperatureIcon from "component/TemperatureIcon"; import GrainDescriber, { GrainOptions, ToGrainOption } from "grain/GrainDescriber"; import useViewport from "hooks/useViewport"; -import { cloneDeep, round } from "lodash"; -import { Bin, Component, Device, Interaction } from "models"; +import { round } from "lodash"; +import { Bin, Component, Device } from "models"; import { GetComponentIcon } from "pbHelpers/ComponentType"; import { describeMeasurement } from "pbHelpers/MeasurementDescriber"; import { useMobile } from "hooks"; @@ -48,6 +45,7 @@ import { useBinAPI } from "providers/pond/binAPI"; import BindaptIcon from "products/Bindapt/BindaptIcon"; import { AccessTime, + ArrowForwardIos, CheckCircleOutline, Error, InfoOutlined, @@ -56,7 +54,6 @@ import { TrendingUp, Warning } from "@mui/icons-material"; -import DevicePresetsFromPicker from "device/DevicePresetsFromPicker"; import { Plenum } from "models/Plenum"; import { Pressure } from "models/Pressure"; import { GrainCable } from "models/GrainCable"; @@ -70,8 +67,9 @@ import { Ambient } from "models/Ambient"; import { ExtractMoisture } from "grain"; import SearchSelect, { Option } from "common/SearchSelect"; import Edit from "@mui/icons-material/Edit"; -import { makeStyles, styled } from "@mui/styles"; +import { makeStyles } from "@mui/styles"; import ButtonGroup from "common/ButtonGroup"; +import ModeChangeDialog from "./conditioning/modeChangeDialog"; const useStyles = makeStyles((theme: Theme) => { return ({ @@ -172,18 +170,21 @@ interface GrainConditions { emcTrend: number; } +interface CombinedPlenum { + tempHumidity?: Plenum, + pressure?: Pressure +} + interface Props { bin: Bin; loading: boolean; components: Map; componentDevices?: Map; devices: Device[]; - plenum?: Plenum; + plenums?: Plenum[]; ambient?: Ambient; cables?: GrainCable[]; - pressure?: Pressure; - interactions?: Interaction[]; - //changeBinMode: (binMode: pond.BinMode) => boolean; + pressures?: Pressure[]; refresh: (showSnack?: boolean) => void; afterUpdate?(): void; preferences?: Map; @@ -195,11 +196,9 @@ interface Props { export default function BinVisualizer(props: Props) { const { bin, - //changeBinMode, - //changeGrainAmount, - plenum, + plenums, ambient, - pressure, + pressures, loading, cables, components, @@ -207,7 +206,6 @@ export default function BinVisualizer(props: Props) { preferences, componentDevices, permissions, - //interactions, refresh, updateComponentCallback, binPresets @@ -233,10 +231,10 @@ export default function BinVisualizer(props: Props) { ).colour(); const pressColour = describeMeasurement(quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE).colour(); const emcColour = describeMeasurement(quack.MeasurementType.MEASUREMENT_TYPE_GRAIN_EMC).colour(); - const [showInputMoisture, setShowInputMoisture] = useState(false); - const [moistureInput, setMoistureInput] = useState(""); - const [tempInput, setTempInput] = useState(""); - const [outdoorHumidityInput, setOutdoorHumidityInput] = useState(""); + // const [showInputMoisture, setShowInputMoisture] = useState(false); + const [targetMoisture, setTargetMoisture] = useState(0); + const [outdoorTemp, setOutdoorTemp] = useState(0); + const [outdoorHumidity, setOutdoorHumidity] = useState(0); const [modeTime, setModeTime] = useState(moment()); const [grainUpdate, setGrainUpdate] = useState(false); const [openStorageTime, setOpenStorageTime] = useState(false); @@ -249,7 +247,7 @@ export default function BinVisualizer(props: Props) { const [cfmLowOpen, setCFMLowOpen] = useState(false); const [cfmHighOpen, setCFMHighOpen] = useState(false); const [{ user }] = useGlobalState(); - const [newPreset, setNewPreset] = useState(pond.BinMode.BIN_MODE_NONE); + // const [newPreset, setNewPreset] = useState(pond.BinMode.BIN_MODE_NONE); const [newBinMode, setNewBinMode] = useState(pond.BinMode.BIN_MODE_NONE); const [selectedCable, setSelectedCable] = useState(); const [openNodeDialog, setOpenNodeDialog] = useState(false); @@ -260,7 +258,7 @@ export default function BinVisualizer(props: Props) { //const [highTempCable, setHighTempCable] = useState(); //const [lowTempCable, setLowTempCable] = useState(); //the switch value to determine what to display in the grain condition box - const [valueDisplay, setValueDisplay] = useState<"low" | "avg" | "high">("avg"); + const [valueDisplay, setValueDisplay] = useState<"low" | "avg" | "high">("high"); //the variables to be able to change the grain type through a unique dialog outside of the settings const [grainChangeDialog, setGrainChangeDialog] = useState(false); const grainOptions = GrainOptions(); @@ -282,65 +280,47 @@ export default function BinVisualizer(props: Props) { const [storageDate, setStorageDate] = useState(moment().format("YYYY-MM-DD")); const [storageTime, setStorageTime] = useState(moment().format("HH:mm")); - // const StyledToggle = styled(ToggleButton)(({ theme }: { theme: Theme }) => ({ - // root: { - // backgroundColor: "transparent", - // overflow: "visible", - // content: "content-box", - // "&$selected": { - // backgroundColor: "gold", - // color: "black", - // borderRadius: 24, - // fontWeight: "bold" - // }, - // "&$selected:hover": { - // backgroundColor: "rgb(255, 255, 0)", - // color: "black", - // borderRadius: 24 - // } - // }, - // selected: {} - // })); + const [activePlenum, setActivePlenum] = useState() + const [activePlenumIndex, setActivePlenumIndex] = useState(0) + const [combinedPlenums, setCombinedPlenums] = useState([]) - // const StyledToggleButtonGroup = styled(ToggleButtonGroup)(({ theme }: { theme: Theme }) => ({ - // grouped: { - // margin: theme.spacing(-0.5), - // border: "none", - // padding: theme.spacing(1), - // "&:not(:first-child):not(:last-child)": { - // borderRadius: 24, - // marginRight: theme.spacing(0.5), - // marginLeft: theme.spacing(0.5) - // }, - // "&:first-child": { - // borderRadius: 24, - // marginLeft: theme.spacing(0.25) - // }, - // "&:last-child": { - // borderRadius: 24, - // marginRight: theme.spacing(0.25) - // } - // }, - // root: { - // backgroundColor: darken( - // theme.palette.background.paper, - // getThemeType() === "light" ? 0.05 : 0.25 - // ), - // borderRadius: 24, - // content: "border-box" - // } - // })); + const [openModeChange, setOpenModeChange] = useState(false) + const [modeChangeInProgress, setModeChangeInProgress] = useState(false) useEffect(() => { setModeTime(moment(bin.status.lastModeChange)); }, [bin.status.lastModeChange]); + useEffect(()=>{ + //match the plenums and pressures based on their location (address) + let combinedPlenums: CombinedPlenum[] = [] + if (plenums) { + plenums.forEach(plenum => { + let newCombinedPlenum: CombinedPlenum = { + tempHumidity: plenum + } + if (pressures) { + pressures.forEach(pressure => { + if(plenum.location().address === pressure.location().address){ + newCombinedPlenum.pressure = pressure + } + }) + } + combinedPlenums.push(newCombinedPlenum) + }) + } + if (combinedPlenums.length > 0){ + setActivePlenum(combinedPlenums[0]) + } + setCombinedPlenums(combinedPlenums) + },[plenums, pressures]) + useEffect(() => { setIsCustomInventory(bin.storage() !== pond.BinStorage.BIN_STORAGE_SUPPORTED_GRAIN); setStorageType(bin.storage()); - setNewPreset(pond.BinMode.BIN_MODE_NONE); + // setNewPreset(pond.BinMode.BIN_MODE_NONE); if (bin.settings.inventory) { - setMoistureInput(bin.settings.inventory.initialMoisture.toString()); + setTargetMoisture(bin.settings.inventory.initialMoisture); setCustomTypeName(bin.settings.inventory.customTypeName); setGrainType(bin.settings.inventory.grainType); setGrainOption(ToGrainOption(bin.settings.inventory.grainType)); @@ -353,9 +333,9 @@ export default function BinVisualizer(props: Props) { if (user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) { t = CtoF(t); } - setTempInput(t.toString()); + setOutdoorTemp(t); } - if (bin.settings) setOutdoorHumidityInput(bin.settings.outdoorHumidity.toString()); + if (bin.settings) setOutdoorHumidity(bin.settings.outdoorHumidity); }, [bin, user]); useEffect(() => { @@ -391,20 +371,6 @@ export default function BinVisualizer(props: Props) { humids.push(...filteredNodes.humids) emcs.push(...filteredNodes.moistures) } - // let tempClone = cloneDeep(cable.temperatures).reverse(); - // let humClone = cloneDeep(cable.humidities).reverse(); - // let emcClone = cloneDeep(cable.grainMoistures).reverse(); - //add the cable data to the proper arrays - // if (cable.topNode > 0) { - // temps.push(...tempClone.splice(0, cable.topNode)); - // humids.push(...humClone.splice(0, cable.topNode)); - // emcs.push(...emcClone.splice(0, cable.topNode)); - // } else { - // //if the cable has no fill set (top node) then use all of the nodes - // temps = tempClone; - // humids = humClone; - // emcs = emcClone; - // } //add the trend data to the proper arrays if the top node is set let cableTrendData = binTrend.cableTrend[cable.key()]; @@ -418,7 +384,6 @@ export default function BinVisualizer(props: Props) { //determine which node is the coldest so that the data displayed is for the same node let lowTempIndex = filteredNodes.temps.indexOf(Math.min(...filteredNodes.temps)) === -1 ? 0 : filteredNodes.temps.indexOf(Math.min(...filteredNodes.temps)); - console.log(lowTempIndex) lowNodeConditions = { tempC: filteredNodes.temps[lowTempIndex], humidity: filteredNodes.humids[lowTempIndex], @@ -798,7 +763,7 @@ export default function BinVisualizer(props: Props) { setIsCustomInventory(bin.storage() !== pond.BinStorage.BIN_STORAGE_SUPPORTED_GRAIN); setStorageType(bin.storage()); if (bin.settings.inventory) { - setMoistureInput(bin.settings.inventory.initialMoisture.toString()); + setTargetMoisture(bin.settings.inventory.initialMoisture); setCustomTypeName(bin.settings.inventory.customTypeName); setGrainType(bin.settings.inventory.grainType); setGrainOption(ToGrainOption(bin.settings.inventory.grainType)); @@ -1094,14 +1059,28 @@ export default function BinVisualizer(props: Props) { const plenumOverview = () => { return ( + - Plenum + Plenum {plenums && plenums?.length > 1 ? activePlenumIndex+1 : ""} + {plenums && plenums.length > 1 && + { + let newIndex = activePlenumIndex + 1 + if(newIndex > combinedPlenums.length - 1){ + newIndex = 0 + } + setActivePlenum(combinedPlenums[newIndex]) + setActivePlenumIndex(newIndex) + }}> + + + } + - {plenum ? ( + {activePlenum?.tempHumidity ? ( - {plenum.getTempString(user.settings.temperatureUnit)} + {activePlenum.tempHumidity.getTempString(user.settings.temperatureUnit)} @@ -1141,7 +1120,7 @@ export default function BinVisualizer(props: Props) { fontWeight: 650, color: humidColour }}> - {plenum.getHumidityString()} + {activePlenum.tempHumidity.getHumidityString()} @@ -1262,7 +1241,7 @@ export default function BinVisualizer(props: Props) { style={{ height: isMobile ? 20 : 25, width: isMobile ? 20 : 25 }} /> - {pressure ? ( + {activePlenum?.pressure ? ( - {pressure.getPressureString(user.settings.pressureUnit)} + {activePlenum.pressure.getPressureString(user.settings.pressureUnit)} {/* @@ -1600,7 +1579,7 @@ export default function BinVisualizer(props: Props) { Fan Performance - {pressure && pressure.fanId === 0 && !bin.settings.fan?.type && bin.fanID() === 0 && ( + {activePlenum?.pressure && activePlenum.pressure.fanId === 0 && !bin.settings.fan?.type && bin.fanID() === 0 && ( No fans found to calculate CFM @@ -1642,8 +1621,8 @@ export default function BinVisualizer(props: Props) { {ExtractMoisture( bin.grain(), - plenum?.temperature ?? 0, - plenum?.humidity ?? 0 + activePlenum?.tempHumidity?.temperature ?? 0, + activePlenum?.tempHumidity?.humidity ?? 0 ).toFixed(2)} % @@ -1745,7 +1724,11 @@ export default function BinVisualizer(props: Props) { Bin Mode + {modeChangeInProgress && + + } { - setShowInputMoisture(true) + // setShowInputMoisture(true) + setModeConditioning() //will set it to either drying or hydrating based on the initial and target moisture } } ]} toggledButtons={determineToggle(mode)} toggle /> - {/* - - Storage - - - Cooldown - - {bin.settings.inventory && - bin.settings.inventory?.initialMoisture < bin.settings.inventory?.targetMoisture ? ( - setShowInputMoisture(true)} - value={pond.BinMode.BIN_MODE_HYDRATING} - aria-label="Hydrating Mode"> - Hydrating - - ) : ( - setShowInputMoisture(true)} - value={pond.BinMode.BIN_MODE_DRYING} - aria-label="Drying Mode"> - Drying - - )} - */} ); }; @@ -1972,106 +1921,32 @@ export default function BinVisualizer(props: Props) { }; const setModeStorage = () => { - setNewPreset(pond.BinMode.BIN_MODE_STORAGE); + // setNewPreset(pond.BinMode.BIN_MODE_STORAGE); setNewBinMode(pond.BinMode.BIN_MODE_STORAGE); + setOpenModeChange(true) }; const setModeCooldown = () => { if (bin.settings.mode === pond.BinMode.BIN_MODE_COOLDOWN) { return; } - setNewPreset(pond.BinMode.BIN_MODE_COOLDOWN); + // setNewPreset(pond.BinMode.BIN_MODE_COOLDOWN); setNewBinMode(pond.BinMode.BIN_MODE_COOLDOWN); + setOpenModeChange(true) }; - const setModeDrying = () => { + const setModeConditioning = () => { if ( bin.settings.inventory && bin.settings.inventory?.initialMoisture < bin.settings.inventory?.targetMoisture ) { - setNewPreset(pond.BinMode.BIN_MODE_HYDRATING); + // setNewPreset(pond.BinMode.BIN_MODE_HYDRATING); setNewBinMode(pond.BinMode.BIN_MODE_HYDRATING); } else { - setNewPreset(pond.BinMode.BIN_MODE_DRYING); + // setNewPreset(pond.BinMode.BIN_MODE_DRYING); setNewBinMode(pond.BinMode.BIN_MODE_DRYING); } - }; - - const closeMoistureDialog = () => { - setNewPreset(0); - setNewBinMode(bin.settings.mode); - setShowInputMoisture(false); - }; - - const moistureDialog = () => { - return ( - - Input new grain moisture - - - Updating the current grain moisture will calibrate the estimate for more accurate - results. The outdoor temperature will have some effect on the estimate as well; consider - updating with drastic changes in the weather or using a predicted average. - - setMoistureInput(event.target.value)} - InputProps={{ - endAdornment: % - }} - style={{ marginTop: theme.spacing(2) }} - fullWidth - variant="outlined" - /> - - Weather - - setTempInput(event.target.value)} - InputProps={{ - endAdornment: ( - - {getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT - ? "°F" - : "℃"} - - ) - }} - style={{ marginTop: theme.spacing(2) }} - fullWidth - variant="outlined" - /> - setOutdoorHumidityInput(event.target.value)} - InputProps={{ - endAdornment: % - }} - style={{ marginTop: theme.spacing(2) }} - fullWidth - variant="outlined" - /> - - - - - - - ); + setOpenModeChange(true) }; if (loading) { @@ -2083,7 +1958,7 @@ export default function BinVisualizer(props: Props) { b.settings.storage = storageType; if (b.settings.inventory) { if (b.settings.inventory.initialMoisture) - b.settings.inventory.initialMoisture = Number(moistureInput); + b.settings.inventory.initialMoisture = targetMoisture; //update all the grain stuff b.settings.inventory.grainType = grainType; b.settings.inventory.customTypeName = customTypeName; @@ -2094,14 +1969,14 @@ export default function BinVisualizer(props: Props) { } if (b.settings.outdoorTemp !== undefined) { - let t = Number(tempInput); + let t = outdoorTemp; if (user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) { - t = FtoC(Number(tempInput)); + t = FtoC(outdoorTemp); } b.settings.outdoorTemp = t; } if (b.settings.outdoorHumidity !== undefined) - b.settings.outdoorHumidity = Number(outdoorHumidityInput); + b.settings.outdoorHumidity = outdoorHumidity; if (b.settings.mode != newBinMode){ if(b.settings.inventory){ @@ -2117,25 +1992,43 @@ export default function BinVisualizer(props: Props) { return ( - {moistureDialog()} {cfmLowDialog()} {cfmHighDialog()} {changeGrain()} {storageTimeDialog()} - { - setShowInputMoisture(false); - setNewPreset(0) - if (success) updateBin(); - }} - parentPreset={newPreset} + binMode={newBinMode} grain={bin.settings.inventory?.grainType} + devices={devices} + open={openModeChange} binCables={cables} - compDevMap={componentDevices} presets={binPresets} + compDevMap={componentDevices} + preferences={preferences} + onClose={(refresh) => { + setOpenModeChange(false) + if(refresh) updateBin(); + }} + defaultTargetMoisture={bin.settings.inventory?.initialMoisture} + // defaultOutdoorTemp={get this from any ambient sensors} + // defaultOutdoorHumidity={get this from any ambient sensors} + changeTargetMoisture={newMoisture => { + setTargetMoisture(newMoisture) + }} + changeOutdoorTemp={newTemp => { + setOutdoorTemp(newTemp) + }} + changeOutdoorHumidity={newHumidity => { + setOutdoorHumidity(newHumidity) + }} + startChange={() => { + setModeChangeInProgress(true) + }} + changeComplete={() => { + setModeChangeInProgress(false) + }} + /> {binMode()} @@ -2148,7 +2041,7 @@ export default function BinVisualizer(props: Props) { {controls()} - {plenum && pressure && fanPerformance()} + {plenums && pressures && fanPerformance()} {ambient && ambientDisplay()} {(bin.status.fans.length > 0 || bin.status.heaters.length > 0) && controllerDisplay()} {dryingEstimate()} diff --git a/src/bin/conditioning/conditioningSelector.tsx b/src/bin/conditioning/conditioningSelector.tsx new file mode 100644 index 0000000..79fdc25 --- /dev/null +++ b/src/bin/conditioning/conditioningSelector.tsx @@ -0,0 +1,90 @@ +import { Typography } from "@mui/material" +import { Ambient } from "models/Ambient" +import { Controller } from "models/Controller" +import { Plenum } from "models/Plenum" +import React, { useState } from "react" +import PickComponentSet, { ComponentSet } from "./pickComponentSet" +import { cloneDeep } from "lodash" +import { DevicePreset } from "models/DevicePreset" +import { pond } from "protobuf-ts/pond" + + +interface Props { + plenums: Plenum[] + ambients: Ambient[] + heaters: Controller[] + fans: Controller[] + binMode: pond.BinMode + presets?: DevicePreset[] + updateSets: (sets: ComponentSet[]) => void +} + + +export default function ConditioningSelector(props: Props){ + const {plenums, ambients, heaters, fans, updateSets, presets, binMode} = props + const [currentSets, setCurrentSets] = useState([]) + + const sensorIndex = (key: string) => { + let index = -1 + currentSets.forEach((set, i) => { + if(set.sensor.key() === key){ + index = i + } + }) + return index + } + + const setUpdate = (key: string, set?: ComponentSet) => { + let sets = cloneDeep(currentSets) + let index = sensorIndex(key) + //if the set is defined, add it to the existing sets as long as it doesn't already exist + if(set){ + //check if there is a set that has a sensor with the key + if(index !== -1){ + sets[index] = set //replace it with the new set + }else{//if the set was not found ie is new + sets.push(set)//add the new set + } + }else{ //if the set is undefined + if (index !== -1){ + sets.splice(index, 1)//remove it from the sets + } + //no else is needed because if it doesn't exist nothing need be done + } + //once the sets are finished being changed pas that up to the parent via the prop function + setCurrentSets(sets) + updateSets(sets) + } + + return ( + + {(plenums.length > 0 || ambients.length > 0) && + Select the sensors you would like to add an interaction to + } + {plenums.map(plenum => { + return ( + + )})} + {ambients.map(ambient => { + return ( + + )})} + + ) +} \ No newline at end of file diff --git a/src/bin/conditioning/modeChangeDialog.tsx b/src/bin/conditioning/modeChangeDialog.tsx new file mode 100644 index 0000000..2913e4a --- /dev/null +++ b/src/bin/conditioning/modeChangeDialog.tsx @@ -0,0 +1,846 @@ +import { Autocomplete, Box, Button, DialogActions, DialogContent, DialogContentText, DialogTitle, TextField, Stepper, Step, StepLabel, InputAdornment, Typography, useTheme } from "@mui/material"; +import ResponsiveDialog from "common/ResponsiveDialog"; +import { useComponentAPI, useInteractionsAPI } from "hooks"; +import { Component, Device, Interaction } from "models"; +import { Ambient } from "models/Ambient"; +import { Controller } from "models/Controller"; +import { DevicePreset } from "models/DevicePreset"; +import { GrainCable } from "models/GrainCable"; +import { Plenum } from "models/Plenum"; +import { pond } from "protobuf-ts/pond"; +import { quack } from "protobuf-ts/quack"; +import { useGlobalState } from "providers"; +import React from "react"; +import { useEffect, useState } from "react"; +import ConditioningSelector from "./conditioningSelector"; +import CableTopNodeSummary from "bin/CableTopNodeSummary"; +import { ComponentSet } from "./pickComponentSet"; +import { sameComponentID } from "pbHelpers/Component"; +import moment from "moment"; +import { lowerCase } from "lodash"; +import { describeMeasurement } from "pbHelpers/MeasurementDescriber"; +import { fahrenheitToCelsius, getTemperatureUnit } from "utils"; +import { GetGrainExtensionMap } from "grain"; +import { PromiseProgress, Stage, Step as PromiseStep } from "common/PromiseProgress"; + +interface Props { + open: boolean + onClose: (refresh: boolean) => void + binKey: string; + devices: Device[]; + binMode: pond.BinMode; + grain?: pond.Grain; + preferences?: Map; + binCables?: GrainCable[]; + compDevMap?: Map; + presets?: DevicePreset[]; + startChange: () => void; + changeComplete: () => void; + defaultTargetMoisture?: number; + changeTargetMoisture?: (newTarget: number) => void + defaultOutdoorTemp?: number; + changeOutdoorTemp?: (newTemp: number) => void + defaultOutdoorHumidity?: number; + changeOutdoorHumidity?: (newHumidity: number) => void +} + +interface Option { + label: string; + device: Device; + icon?: string; +} + +interface DialogStep { + label: string; + completed?: boolean; +} + +export default function ModeChangeDialog(props: Props){ + const { + open, + onClose, + binMode, + devices, + binKey, + preferences, + binCables, + compDevMap, + grain, + startChange, + changeComplete, + defaultTargetMoisture, + defaultOutdoorTemp, + defaultOutdoorHumidity, + changeTargetMoisture, + changeOutdoorTemp, + changeOutdoorHumidity, + presets + } = props + const [{as}] = useGlobalState() + const [componentSets, setComponentSets] = useState([]) + const interactionAPI = useInteractionsAPI() + const componentAPI = useComponentAPI(); + const [selectedDevice, setSelectedDevice] = useState() + const [existingInteractions, setExistingInteractions] = useState([]) + const [loading, setLoading] = useState(false) + const [options, setOptions] = useState([]) + const [deviceComponents, setDeviceComponents] = useState>( + new Map() + ); + + const [plenums, setPlenums] = useState([]) + const [ambients, setAmbients] = useState([]) + const [heaters, setHeaters] = useState([]) + const [fans, setFans] = useState([]) + const grainExtensionMap = GetGrainExtensionMap(); + const [conflictingSetInteractions, setConflictingSetInteractions] = useState([]) + const [interactionsToAdd, setInteractionsToAdd] = useState() + const [promiseStages, setPromiseStages] = useState([]) + const [dialogSteps, setDialogSteps] = useState([]) + const [currentStep, setCurrentStep] = useState(0) + const theme = useTheme() + + //the state variables for the moisture + const [moistureInput, setMoistureInput] = useState("") + const [outdoorTempInput, setOutdoorTempInput] = useState("") + const [outdoorHumidityInput, setOutdoorHumidityInput] = useState("") + + const [deviceOption, setDeviceOption] = useState