From 3df6db8a0128ce44c2ea23ab99877d9656002463 Mon Sep 17 00:00:00 2001 From: csawatzky Date: Thu, 20 Nov 2025 15:39:29 -0600 Subject: [PATCH] finished the refactoring of the bin mode change to allow for more components to be selected --- src/bin/BinVisualizerV2.tsx | 146 +++--------- src/bin/conditioning/conditioningSelector.tsx | 14 +- src/bin/conditioning/modeChangeDialog.tsx | 222 ++++++++++-------- src/bin/conditioning/pickComponentSet.tsx | 198 ++++++++++++---- src/common/PromiseProgress.tsx | 40 ++-- src/pages/Bin.tsx | 2 - 6 files changed, 350 insertions(+), 272 deletions(-) diff --git a/src/bin/BinVisualizerV2.tsx b/src/bin/BinVisualizerV2.tsx index 4f9ae41..7d99671 100644 --- a/src/bin/BinVisualizerV2.tsx +++ b/src/bin/BinVisualizerV2.tsx @@ -7,11 +7,9 @@ import { darken, DialogActions, DialogContent, - DialogContentText, DialogTitle, Grid2 as Grid, IconButton, - InputAdornment, lighten, Link, Skeleton, @@ -19,8 +17,6 @@ import { Switch, TextField, Theme, - ToggleButton, - ToggleButtonGroup, Typography, useTheme, } from "@mui/material"; @@ -31,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"; @@ -49,7 +45,6 @@ import { useBinAPI } from "providers/pond/binAPI"; import BindaptIcon from "products/Bindapt/BindaptIcon"; import { AccessTime, - ArrowForward, ArrowForwardIos, CheckCircleOutline, Error, @@ -59,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"; @@ -73,7 +67,7 @@ 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"; @@ -191,8 +185,6 @@ interface Props { ambient?: Ambient; cables?: GrainCable[]; pressures?: Pressure[]; - interactions?: Interaction[]; - //changeBinMode: (binMode: pond.BinMode) => boolean; refresh: (showSnack?: boolean) => void; afterUpdate?(): void; preferences?: Map; @@ -204,8 +196,6 @@ interface Props { export default function BinVisualizer(props: Props) { const { bin, - //changeBinMode, - //changeGrainAmount, plenums, ambient, pressures, @@ -216,7 +206,6 @@ export default function BinVisualizer(props: Props) { preferences, componentDevices, permissions, - //interactions, refresh, updateComponentCallback, binPresets @@ -243,9 +232,9 @@ export default function BinVisualizer(props: Props) { 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 [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); @@ -331,7 +320,7 @@ export default function BinVisualizer(props: Props) { setStorageType(bin.storage()); // 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)); @@ -344,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(() => { @@ -774,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)); @@ -1960,83 +1949,6 @@ export default function BinVisualizer(props: Props) { setOpenModeChange(true) }; - // 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" - // /> - // - // - // - // - // - // - // ); - // }; - if (loading) { return ; } @@ -2046,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; @@ -2057,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){ @@ -2080,26 +1992,10 @@ export default function BinVisualizer(props: Props) { return ( - {/* {moistureDialog()} */} {cfmLowDialog()} {cfmHighDialog()} {changeGrain()} {storageTimeDialog()} - {/* { - setShowInputMoisture(false); - setNewPreset(0) - if (success) updateBin(); - }} - parentPreset={newPreset} - grain={bin.settings.inventory?.grainType} - binCables={cables} - compDevMap={componentDevices} - presets={binPresets} - /> */} { 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()} diff --git a/src/bin/conditioning/conditioningSelector.tsx b/src/bin/conditioning/conditioningSelector.tsx index 9b98e20..79fdc25 100644 --- a/src/bin/conditioning/conditioningSelector.tsx +++ b/src/bin/conditioning/conditioningSelector.tsx @@ -1,10 +1,12 @@ -import { Box, Checkbox, DialogActions, DialogContent, FormControlLabel, Typography } from "@mui/material" +import { Typography } from "@mui/material" import { Ambient } from "models/Ambient" import { Controller } from "models/Controller" import { Plenum } from "models/Plenum" -import React, { useEffect, useState } from "react" +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 { @@ -12,12 +14,14 @@ interface Props { 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} = props + const {plenums, ambients, heaters, fans, updateSets, presets, binMode} = props const [currentSets, setCurrentSets] = useState([]) const sensorIndex = (key: string) => { @@ -64,6 +68,8 @@ export default function ConditioningSelector(props: Props){ sensor={plenum} heaters={heaters} fans={fans} + binMode={binMode} + presets={presets} updateSet={setUpdate} /> )})} @@ -74,6 +80,8 @@ export default function ConditioningSelector(props: Props){ sensor={ambient} heaters={heaters} fans={fans} + binMode={binMode} + presets={presets} updateSet={setUpdate} /> )})} diff --git a/src/bin/conditioning/modeChangeDialog.tsx b/src/bin/conditioning/modeChangeDialog.tsx index 4d420e2..630b158 100644 --- a/src/bin/conditioning/modeChangeDialog.tsx +++ b/src/bin/conditioning/modeChangeDialog.tsx @@ -1,4 +1,4 @@ -import { Autocomplete, Box, Button, DialogActions, DialogContent, DialogContentText, DialogTitle, TextField, Stepper, Step, StepLabel, InputAdornment, Typography } from "@mui/material"; +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"; @@ -19,7 +19,7 @@ import { sameComponentID } from "pbHelpers/Component"; import moment from "moment"; import { lowerCase } from "lodash"; import { describeMeasurement } from "pbHelpers/MeasurementDescriber"; -import { getTemperatureUnit } from "utils"; +import { fahrenheitToCelsius, getTemperatureUnit } from "utils"; import { GetGrainExtensionMap } from "grain"; import { PromiseProgress, Stage, Step as PromiseStep } from "common/PromiseProgress"; @@ -37,10 +37,11 @@ interface Props { startChange: () => void; changeComplete: () => void; defaultTargetMoisture?: number; - changeTargetMoisture?: (newTarget: number) => {} + changeTargetMoisture?: (newTarget: number) => void defaultOutdoorTemp?: number; - changeOutdoorTemp?: (newTemp: number) => {} + changeOutdoorTemp?: (newTemp: number) => void defaultOutdoorHumidity?: number; + changeOutdoorHumidity?: (newHumidity: number) => void } interface Option { @@ -67,7 +68,13 @@ export default function ModeChangeDialog(props: Props){ grain, startChange, changeComplete, - defaultTargetMoisture + defaultTargetMoisture, + defaultOutdoorTemp, + defaultOutdoorHumidity, + changeTargetMoisture, + changeOutdoorTemp, + changeOutdoorHumidity, + presets } = props const [{as}] = useGlobalState() const [componentSets, setComponentSets] = useState([]) @@ -85,20 +92,24 @@ export default function ModeChangeDialog(props: Props){ const [ambients, setAmbients] = useState([]) const [heaters, setHeaters] = useState([]) const [fans, setFans] = useState([]) - const [selectedPreset, setSelectedPreset] = useState(undefined); 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("") useEffect(()=>{ setMoistureInput(defaultTargetMoisture?.toFixed(2) ?? "0") - },[defaultTargetMoisture]) + setOutdoorTempInput(defaultOutdoorTemp?.toFixed(2) ?? "0") + setOutdoorHumidityInput(defaultOutdoorHumidity?.toFixed(2) ?? "0") + },[defaultTargetMoisture, defaultOutdoorTemp, defaultOutdoorHumidity]) //get the interactions and components for the device when it is selected from the dropdown useEffect(()=>{ @@ -214,7 +225,8 @@ if (!selectedDevice) return; const buildHeaterInteraction = ( sensor: Plenum | Ambient, - heater: Controller + heater: Controller, + customPreset?: DevicePreset ): pond.InteractionSettings => { let interaction = pond.InteractionSettings.create({ source: sensor.location(), @@ -236,11 +248,10 @@ if (!selectedDevice) return; let temp = 0; let hum = 0; - //if a preset was selected use its temp/hum values - if (selectedPreset !== undefined) { - temp = selectedPreset.temp(); - hum = selectedPreset.hum(); + if (customPreset !== undefined) { + temp = customPreset.temp(); + hum = customPreset.hum(); } else { //otherwise use default values switch(binMode){ @@ -303,7 +314,8 @@ if (!selectedDevice) return; sensor: Plenum | Ambient, fan: Controller, tempComparison: "greater" | "less", - humidityComparison: "greater" | "less" + humidityComparison: "greater" | "less", + customPreset?: DevicePreset ) => { let interaction = pond.InteractionSettings.create({ source: sensor.location(), @@ -323,13 +335,13 @@ if (!selectedDevice) return; }) }); - let tempPreset = 0; - let humidityPreset = 0; + let tempPreset = 0; + let humidityPreset = 0; //if a custom preset was selected use it - if (selectedPreset !== undefined) { - tempPreset = selectedPreset.temp(); - humidityPreset = selectedPreset.hum(); + if (customPreset !== undefined) { + tempPreset = customPreset.temp(); + humidityPreset = customPreset.hum(); } else { //otherwise use one of the defaults switch (binMode) { @@ -437,7 +449,6 @@ if (!selectedDevice) return; } componentSets.forEach(set => { set.controllers.forEach(controller => { - console.log(controller) let newStep: PromiseStep = { title: "Updating " + controller.name(), promise: componentAPI.update(selectedDevice.id(), controller.settings) @@ -461,6 +472,7 @@ if (!selectedDevice) return; sets.forEach((set) => { //loop through the controllers set.controllers.forEach(controller => { + let customPreset = set.selectedPresets.get(controller.key()) //filter the conflicting interactions for that controller let c = existingInteractions.filter(i => { let conflicting = false; @@ -478,17 +490,17 @@ if (!selectedDevice) return; case pond.BinMode.BIN_MODE_COOLDOWN: // if the controller is a heater create heater interaction if(controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_HEATER){ - multiInteractionSettings.interactions.push(buildHeaterInteraction(set.sensor, controller)) + multiInteractionSettings.interactions.push(buildHeaterInteraction(set.sensor, controller, customPreset)) }else if(controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_AERATION_FAN || controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_EXHAUST_FAN){ // if the controller is a fan create a fan interaction - multiInteractionSettings.interactions.push(buildFanInteraction(set.sensor, controller, "less", "less")) + multiInteractionSettings.interactions.push(buildFanInteraction(set.sensor, controller, "less", "less", customPreset)) } break; case pond.BinMode.BIN_MODE_DRYING: // if the controller is a heater create heater interaction if(controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_HEATER){ - multiInteractionSettings.interactions.push(buildHeaterInteraction(set.sensor, controller)) + multiInteractionSettings.interactions.push(buildHeaterInteraction(set.sensor, controller, customPreset)) }else if(controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_AERATION_FAN || controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_EXHAUST_FAN){// if the controller is a fan //if it is a combination set using a heater as well just turn the fan on @@ -496,7 +508,7 @@ if (!selectedDevice) return; controller.settings.defaultOutputState = quack.OutputMode.OUTPUT_MODE_ON }else{ //otherwise make a fan interaction - multiInteractionSettings.interactions.push(buildFanInteraction(set.sensor, controller, "greater", "less")) + multiInteractionSettings.interactions.push(buildFanInteraction(set.sensor, controller, "greater", "less", customPreset)) } } break; @@ -505,7 +517,7 @@ if (!selectedDevice) return; if(controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_AERATION_FAN || controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_EXHAUST_FAN){ //create fan interaction - multiInteractionSettings.interactions.push(buildFanInteraction(set.sensor, controller, "less", "greater")) + multiInteractionSettings.interactions.push(buildFanInteraction(set.sensor, controller, "less", "greater", customPreset)) } break; } @@ -563,55 +575,81 @@ if (!selectedDevice) return; //this step will only be used for drying and hydrating const moistureStep = () => { return ( - - - 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" - /> - + + + 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) + if(changeTargetMoisture && !isNaN(parseFloat(event.target.value))){ + changeTargetMoisture(parseFloat(event.target.value)) + } + }} + InputProps={{ + endAdornment: % + }} + style={{ marginTop: theme.spacing(2) }} + fullWidth + variant="outlined" + /> + + Weather + + { + setOutdoorTempInput(event.target.value) + if(changeOutdoorTemp && !isNaN(parseFloat(event.target.value))){ + //if the users prefs are F will need to convert it to C when sending it back + let val = parseFloat(event.target.value) + if(getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT){ + val = fahrenheitToCelsius(val) + } + changeOutdoorTemp(val) + } + }} + InputProps={{ + endAdornment: ( + + {getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT + ? "°F" + : "℃"} + + ) + }} + style={{ marginTop: theme.spacing(2) }} + fullWidth + variant="outlined" + /> + { + setOutdoorHumidityInput(event.target.value) + if(changeOutdoorHumidity && !isNaN(parseFloat(event.target.value))){ + changeOutdoorHumidity(parseFloat(event.target.value)) + } + }} + InputProps={{ + endAdornment: % + }} + style={{ marginTop: theme.spacing(2) }} + fullWidth + variant="outlined" + /> + ) }; @@ -652,6 +690,8 @@ if (!selectedDevice) return; ambients={ambients} fans={fans} heaters={heaters} + presets={presets} + binMode={binMode} updateSets={(sets) => { //update the component sets that will be used for the interactions //when the sets change it will create the interactions and any conflicting ones that need to be removed will be put into a list @@ -673,9 +713,13 @@ if (!selectedDevice) return; { + startChange() + }} onComplete={()=>{ - console.log("COMPLETED PROMISES") + close(true) setCurrentStep(0)//reset the stepper changeComplete()//change the boolean preventing more mode changes }} @@ -686,13 +730,13 @@ if (!selectedDevice) return; const stepper = () => { return ( - - {dialogSteps.map(dialogStep => { + + {dialogSteps.map((dialogStep, i) => { const labelProps: { optional?: React.ReactNode; } = {}; return ( - + {dialogStep.label} ); @@ -705,24 +749,24 @@ if (!selectedDevice) return; let buttons: JSX.Element[] = [] //this button should be on every step of all of them buttons.push( - + ) // this button should appear if the step they are on is not 0 and not the last step if(currentStep !== 0 && currentStep !== dialogSteps.length-1){ buttons.push( - + ) } // this button should appear if the step they are on is less than the last step -1 if(currentStep < dialogSteps.length-2){ buttons.push( - + ) } - if((binMode === pond.BinMode.BIN_MODE_STORAGE || binMode === pond.BinMode.BIN_MODE_COOLDOWN && currentStep === 0) || - binMode === pond.BinMode.BIN_MODE_DRYING || binMode === pond.BinMode.BIN_MODE_HYDRATING && currentStep === 1){ + if(((binMode === pond.BinMode.BIN_MODE_STORAGE || binMode === pond.BinMode.BIN_MODE_COOLDOWN) && currentStep === 0) || + (binMode === pond.BinMode.BIN_MODE_DRYING || binMode === pond.BinMode.BIN_MODE_HYDRATING) && currentStep === 1){ buttons.push( - ) } @@ -778,13 +821,6 @@ if (!selectedDevice) return; return ( - {/* { - setOpenProgress(false) - close(refresh) - }} /> */} {close(false)}}> {binMode === pond.BinMode.BIN_MODE_STORAGE ? "Entering Storage Mode" : "Choose Device for " + modeDescription() + " Method"} diff --git a/src/bin/conditioning/pickComponentSet.tsx b/src/bin/conditioning/pickComponentSet.tsx index 87572ed..7848547 100644 --- a/src/bin/conditioning/pickComponentSet.tsx +++ b/src/bin/conditioning/pickComponentSet.tsx @@ -1,30 +1,89 @@ import { CheckBox, ExpandMore } from "@mui/icons-material"; -import { Accordion, AccordionDetails, AccordionSummary, Box, Checkbox, FormControlLabel, Typography } from "@mui/material"; +import { Accordion, AccordionDetails, AccordionSummary, Autocomplete, Box, Checkbox, FormControlLabel, Grid2, TextField, Typography } from "@mui/material"; import { cloneDeep } from "lodash"; import { Ambient } from "models/Ambient"; import { Controller } from "models/Controller"; +import { DevicePreset } from "models/DevicePreset"; import { Plenum } from "models/Plenum"; +import { pond } from "protobuf-ts/pond"; import { quack } from "protobuf-ts/quack"; import React, { useEffect, useState } from "react"; export interface ComponentSet { sensor: Plenum | Ambient, controllers: Controller[] + selectedPresets: Map combo?: boolean //if the set contains both a heater and a fan } +interface Option { + label: string; + id: number; + icon?: string; +} + interface Props { sensor: Plenum | Ambient + binMode: pond.BinMode heaters?: Controller[] fans?: Controller[] + presets?: DevicePreset[] updateSet: (sensorKey: string, componentSet?: ComponentSet) => void } export default function PickComponentSet(props: Props) { - const {sensor, heaters, fans, updateSet} = props + const {sensor, heaters, fans, updateSet, presets, binMode} = props const [sensorSelected, setSensorSelected] = useState(false) const [currentSet, setCurrentSet] = useState() + useEffect(()=>{ + if (presets){ + let presetOptions: Option[] = []; + presets.forEach((devicePreset, i) => { + //add the preset to the possible options + if (devicePreset.favorite()) { + //if it is favorited add it to the front of the array + presetOptions.unshift({ + label: devicePreset.name, + id: i + }); + } else { + //otherwise add it to the end + presetOptions.push({ + label: devicePreset.name, + id: i + }); + } + + }); + } + },[presets]) + + const getOptionsForController = (controllerType: pond.ControllerType) => { + let validOps: Option[] = [] + if(presets){ + presets.forEach((devicePreset, i) => { + if(devicePreset.controllerType() === controllerType && devicePreset.compareToBinMode(binMode)){ + //add the preset to the possible options + if (devicePreset.favorite()) { + //if it is favorited add it to the front of the array + validOps.unshift({ + label: devicePreset.name, + id: i + }); + } else { + //otherwise add it to the end + validOps.push({ + label: devicePreset.name, + id: i + }); + } + } + }) + } + return validOps + } + const controllerIndex = (key: string) => { if(!currentSet) return -1 let index = -1 @@ -64,7 +123,8 @@ export default function PickComponentSet(props: Props) { if(checked){ newSet = { sensor: sensor, - controllers: [] + controllers: [], + selectedPresets: new Map() } } setCurrentSet(newSet) @@ -78,32 +138,59 @@ export default function PickComponentSet(props: Props) { }>Controllers Selected: {currentSet.controllers.length} - {heaters && heaters.length > 0 && + {heaters && heaters.length > 0 && binMode !== pond.BinMode.BIN_MODE_HYDRATING && Heaters {heaters.map(heater => ( - { - let set = cloneDeep(currentSet) - if(checked){//if checked need to add it to the controllers - set.controllers.push(heater) - }else{//will need to make sure it is not in the controllers - let index = controllerIndex(heater.key()) - if (index !== -1){ - set.controllers.splice(index, 1) - } - } - set.combo = comboCheck(set) - setCurrentSet(set) - updateSet(sensor.key(), set) - }} + + + { + let set = cloneDeep(currentSet) + if(checked){//if checked need to add it to the controllers + set.controllers.push(heater) + }else{//will need to make sure it is not in the controllers + let index = controllerIndex(heater.key()) + if (index !== -1){ + set.controllers.splice(index, 1) + } + } + set.combo = comboCheck(set) + setCurrentSet(set) + updateSet(sensor.key(), set) + }} + /> + } + label={heater.name()} /> - } - label={heater.name()} - /> + + + {presets && getOptionsForController(pond.ControllerType.CONTROLLER_TYPE_HEATER).length > 0 && + option.label || ""} + onChange={(_, newValue) => { + if (newValue) { + let presetId = newValue.id; + if (presets && presets[presetId]) { + //setSelectedPreset(presets[presetId]); + let set = cloneDeep(currentSet) + set.selectedPresets.set(heater.key(), presets[presetId]) + set.combo = comboCheck(set) + setCurrentSet(set) + updateSet(sensor.key(), set) + } + } + }} + renderInput={params => } + /> + } + + ))} } @@ -111,28 +198,55 @@ export default function PickComponentSet(props: Props) { Fans {fans.map(fan => ( - { - let set = cloneDeep(currentSet) - if(checked){//if checked need to add it to the controllers - set.controllers.push(fan) - }else{//will need to make sure it is not in the controllers - let index = controllerIndex(fan.key()) - if (index !== -1){ - set.controllers.splice(index, 1) + + + { + let set = cloneDeep(currentSet) + if(checked){//if checked need to add it to the controllers + set.controllers.push(fan) + }else{//will need to make sure it is not in the controllers + let index = controllerIndex(fan.key()) + if (index !== -1){ + set.controllers.splice(index, 1) + } + } + set.combo = comboCheck(set) + setCurrentSet(set) + updateSet(sensor.key(), set) + }} + /> + } + label={fan.name()} + /> + + + {presets && getOptionsForController(pond.ControllerType.CONTROLLER_TYPE_FAN).length > 0 && + option.label || ""} + onChange={(_, newValue) => { + if (newValue) { + let presetId = newValue.id; + if (presets && presets[presetId]) { + //setSelectedPreset(presets[presetId]); + let set = cloneDeep(currentSet) + set.selectedPresets.set(fan.key(), presets[presetId]) + set.combo = comboCheck(set) + setCurrentSet(set) + updateSet(sensor.key(), set) } } - set.combo = comboCheck(set) - setCurrentSet(set) - updateSet(sensor.key(), set) }} + renderInput={params => } /> } - label={fan.name()} - /> + + ))} } diff --git a/src/common/PromiseProgress.tsx b/src/common/PromiseProgress.tsx index 1b3b1d8..41b71cd 100644 --- a/src/common/PromiseProgress.tsx +++ b/src/common/PromiseProgress.tsx @@ -1,6 +1,5 @@ import { AxiosResponse } from "axios" -import ResponsiveDialog from "./ResponsiveDialog" -import { Box, Button, CircularProgress, DialogActions, DialogContent, DialogTitle, Icon, Typography } from "@mui/material" +import { Box, Button, CircularProgress, DialogActions, DialogContent, DialogTitle, Typography } from "@mui/material" import { useEffect, useState } from "react" import { CheckCircle, Error, Pending } from "@mui/icons-material" import React from "react" @@ -19,7 +18,9 @@ export interface Step { interface Props { stages: Stage[] + automaticStart: boolean failFast?: boolean + onStart?: () => void onComplete?: () => void } @@ -31,7 +32,7 @@ enum progress { } export function PromiseProgress(props: Props){ - const {stages, failFast, onComplete} = props + const {stages, failFast, onStart, onComplete, automaticStart} = props const [completion, setCompletion] = useState>(new Map()) const execute = () => { @@ -52,7 +53,7 @@ export function PromiseProgress(props: Props){ progMap.set(i+"-"+k, progress.InProgress); setCompletion(new Map(progMap)); return step.promise - .then(resp => { + .then(() => { // mark step as completed progMap.set(i+"-"+k, progress.Complete); setCompletion(new Map(progMap)); @@ -73,13 +74,22 @@ export function PromiseProgress(props: Props){ await Promise.all(stepPromises); } }; - runStages().catch(err => { - console.error("Execution stopped due to error:", err); - }).finally(() => { - if(onComplete) onComplete() - }); + if(onStart){ + onStart() + } + runStages().catch(err => { + console.error("Execution stopped due to error:", err); + }).finally(() => { + if(onComplete) onComplete() + }); } + useEffect(() => { + if(automaticStart){ + execute() + } + },[automaticStart, execute]) + const getCompletion = (completion?: progress) => { switch(completion){ case progress.InProgress: @@ -116,11 +126,13 @@ export function PromiseProgress(props: Props){ {stages.map((stage, number) => displayStage(stage, number))} - + {!automaticStart && + + } ) diff --git a/src/pages/Bin.tsx b/src/pages/Bin.tsx index c352634..1fb51fb 100644 --- a/src/pages/Bin.tsx +++ b/src/pages/Bin.tsx @@ -698,8 +698,6 @@ export default function Bin(props: Props) { components={components} componentDevices={componentDevices} devices={devices} - interactions={interactions} - //changeBinMode={changeBinMode} afterUpdate={refresh} refresh={refresh} preferences={preferences}