finished the refactoring of the bin mode change to allow for more components to be selected
This commit is contained in:
parent
70bd91bbe7
commit
3df6db8a01
6 changed files with 350 additions and 272 deletions
|
|
@ -7,11 +7,9 @@ import {
|
||||||
darken,
|
darken,
|
||||||
DialogActions,
|
DialogActions,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogContentText,
|
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
Grid2 as Grid,
|
Grid2 as Grid,
|
||||||
IconButton,
|
IconButton,
|
||||||
InputAdornment,
|
|
||||||
lighten,
|
lighten,
|
||||||
Link,
|
Link,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
|
|
@ -19,8 +17,6 @@ import {
|
||||||
Switch,
|
Switch,
|
||||||
TextField,
|
TextField,
|
||||||
Theme,
|
Theme,
|
||||||
ToggleButton,
|
|
||||||
ToggleButtonGroup,
|
|
||||||
Typography,
|
Typography,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
@ -31,8 +27,8 @@ import HumidityIcon from "component/HumidityIcon";
|
||||||
import TemperatureIcon from "component/TemperatureIcon";
|
import TemperatureIcon from "component/TemperatureIcon";
|
||||||
import GrainDescriber, { GrainOptions, ToGrainOption } from "grain/GrainDescriber";
|
import GrainDescriber, { GrainOptions, ToGrainOption } from "grain/GrainDescriber";
|
||||||
import useViewport from "hooks/useViewport";
|
import useViewport from "hooks/useViewport";
|
||||||
import { cloneDeep, round } from "lodash";
|
import { round } from "lodash";
|
||||||
import { Bin, Component, Device, Interaction } from "models";
|
import { Bin, Component, Device } from "models";
|
||||||
import { GetComponentIcon } from "pbHelpers/ComponentType";
|
import { GetComponentIcon } from "pbHelpers/ComponentType";
|
||||||
import { describeMeasurement } from "pbHelpers/MeasurementDescriber";
|
import { describeMeasurement } from "pbHelpers/MeasurementDescriber";
|
||||||
import { useMobile } from "hooks";
|
import { useMobile } from "hooks";
|
||||||
|
|
@ -49,7 +45,6 @@ import { useBinAPI } from "providers/pond/binAPI";
|
||||||
import BindaptIcon from "products/Bindapt/BindaptIcon";
|
import BindaptIcon from "products/Bindapt/BindaptIcon";
|
||||||
import {
|
import {
|
||||||
AccessTime,
|
AccessTime,
|
||||||
ArrowForward,
|
|
||||||
ArrowForwardIos,
|
ArrowForwardIos,
|
||||||
CheckCircleOutline,
|
CheckCircleOutline,
|
||||||
Error,
|
Error,
|
||||||
|
|
@ -59,7 +54,6 @@ import {
|
||||||
TrendingUp,
|
TrendingUp,
|
||||||
Warning
|
Warning
|
||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
import DevicePresetsFromPicker from "device/DevicePresetsFromPicker";
|
|
||||||
import { Plenum } from "models/Plenum";
|
import { Plenum } from "models/Plenum";
|
||||||
import { Pressure } from "models/Pressure";
|
import { Pressure } from "models/Pressure";
|
||||||
import { GrainCable } from "models/GrainCable";
|
import { GrainCable } from "models/GrainCable";
|
||||||
|
|
@ -73,7 +67,7 @@ import { Ambient } from "models/Ambient";
|
||||||
import { ExtractMoisture } from "grain";
|
import { ExtractMoisture } from "grain";
|
||||||
import SearchSelect, { Option } from "common/SearchSelect";
|
import SearchSelect, { Option } from "common/SearchSelect";
|
||||||
import Edit from "@mui/icons-material/Edit";
|
import Edit from "@mui/icons-material/Edit";
|
||||||
import { makeStyles, styled } from "@mui/styles";
|
import { makeStyles } from "@mui/styles";
|
||||||
import ButtonGroup from "common/ButtonGroup";
|
import ButtonGroup from "common/ButtonGroup";
|
||||||
import ModeChangeDialog from "./conditioning/modeChangeDialog";
|
import ModeChangeDialog from "./conditioning/modeChangeDialog";
|
||||||
|
|
||||||
|
|
@ -191,8 +185,6 @@ interface Props {
|
||||||
ambient?: Ambient;
|
ambient?: Ambient;
|
||||||
cables?: GrainCable[];
|
cables?: GrainCable[];
|
||||||
pressures?: Pressure[];
|
pressures?: Pressure[];
|
||||||
interactions?: Interaction[];
|
|
||||||
//changeBinMode: (binMode: pond.BinMode) => boolean;
|
|
||||||
refresh: (showSnack?: boolean) => void;
|
refresh: (showSnack?: boolean) => void;
|
||||||
afterUpdate?(): void;
|
afterUpdate?(): void;
|
||||||
preferences?: Map<string, pond.BinComponentPreferences>;
|
preferences?: Map<string, pond.BinComponentPreferences>;
|
||||||
|
|
@ -204,8 +196,6 @@ interface Props {
|
||||||
export default function BinVisualizer(props: Props) {
|
export default function BinVisualizer(props: Props) {
|
||||||
const {
|
const {
|
||||||
bin,
|
bin,
|
||||||
//changeBinMode,
|
|
||||||
//changeGrainAmount,
|
|
||||||
plenums,
|
plenums,
|
||||||
ambient,
|
ambient,
|
||||||
pressures,
|
pressures,
|
||||||
|
|
@ -216,7 +206,6 @@ export default function BinVisualizer(props: Props) {
|
||||||
preferences,
|
preferences,
|
||||||
componentDevices,
|
componentDevices,
|
||||||
permissions,
|
permissions,
|
||||||
//interactions,
|
|
||||||
refresh,
|
refresh,
|
||||||
updateComponentCallback,
|
updateComponentCallback,
|
||||||
binPresets
|
binPresets
|
||||||
|
|
@ -243,9 +232,9 @@ export default function BinVisualizer(props: Props) {
|
||||||
const pressColour = describeMeasurement(quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE).colour();
|
const pressColour = describeMeasurement(quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE).colour();
|
||||||
const emcColour = describeMeasurement(quack.MeasurementType.MEASUREMENT_TYPE_GRAIN_EMC).colour();
|
const emcColour = describeMeasurement(quack.MeasurementType.MEASUREMENT_TYPE_GRAIN_EMC).colour();
|
||||||
// const [showInputMoisture, setShowInputMoisture] = useState<boolean>(false);
|
// const [showInputMoisture, setShowInputMoisture] = useState<boolean>(false);
|
||||||
const [moistureInput, setMoistureInput] = useState<string>("");
|
const [targetMoisture, setTargetMoisture] = useState(0);
|
||||||
const [tempInput, setTempInput] = useState<string>("");
|
const [outdoorTemp, setOutdoorTemp] = useState(0);
|
||||||
const [outdoorHumidityInput, setOutdoorHumidityInput] = useState<string>("");
|
const [outdoorHumidity, setOutdoorHumidity] = useState(0);
|
||||||
const [modeTime, setModeTime] = useState<Moment>(moment());
|
const [modeTime, setModeTime] = useState<Moment>(moment());
|
||||||
const [grainUpdate, setGrainUpdate] = useState<boolean>(false);
|
const [grainUpdate, setGrainUpdate] = useState<boolean>(false);
|
||||||
const [openStorageTime, setOpenStorageTime] = useState(false);
|
const [openStorageTime, setOpenStorageTime] = useState(false);
|
||||||
|
|
@ -331,7 +320,7 @@ export default function BinVisualizer(props: Props) {
|
||||||
setStorageType(bin.storage());
|
setStorageType(bin.storage());
|
||||||
// setNewPreset(pond.BinMode.BIN_MODE_NONE);
|
// setNewPreset(pond.BinMode.BIN_MODE_NONE);
|
||||||
if (bin.settings.inventory) {
|
if (bin.settings.inventory) {
|
||||||
setMoistureInput(bin.settings.inventory.initialMoisture.toString());
|
setTargetMoisture(bin.settings.inventory.initialMoisture);
|
||||||
setCustomTypeName(bin.settings.inventory.customTypeName);
|
setCustomTypeName(bin.settings.inventory.customTypeName);
|
||||||
setGrainType(bin.settings.inventory.grainType);
|
setGrainType(bin.settings.inventory.grainType);
|
||||||
setGrainOption(ToGrainOption(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) {
|
if (user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
|
||||||
t = CtoF(t);
|
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]);
|
}, [bin, user]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -774,7 +763,7 @@ export default function BinVisualizer(props: Props) {
|
||||||
setIsCustomInventory(bin.storage() !== pond.BinStorage.BIN_STORAGE_SUPPORTED_GRAIN);
|
setIsCustomInventory(bin.storage() !== pond.BinStorage.BIN_STORAGE_SUPPORTED_GRAIN);
|
||||||
setStorageType(bin.storage());
|
setStorageType(bin.storage());
|
||||||
if (bin.settings.inventory) {
|
if (bin.settings.inventory) {
|
||||||
setMoistureInput(bin.settings.inventory.initialMoisture.toString());
|
setTargetMoisture(bin.settings.inventory.initialMoisture);
|
||||||
setCustomTypeName(bin.settings.inventory.customTypeName);
|
setCustomTypeName(bin.settings.inventory.customTypeName);
|
||||||
setGrainType(bin.settings.inventory.grainType);
|
setGrainType(bin.settings.inventory.grainType);
|
||||||
setGrainOption(ToGrainOption(bin.settings.inventory.grainType));
|
setGrainOption(ToGrainOption(bin.settings.inventory.grainType));
|
||||||
|
|
@ -1960,83 +1949,6 @@ export default function BinVisualizer(props: Props) {
|
||||||
setOpenModeChange(true)
|
setOpenModeChange(true)
|
||||||
};
|
};
|
||||||
|
|
||||||
// const closeMoistureDialog = () => {
|
|
||||||
// // setNewPreset(0);
|
|
||||||
// setNewBinMode(bin.settings.mode);
|
|
||||||
// setShowInputMoisture(false);
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const moistureDialog = () => {
|
|
||||||
// return (
|
|
||||||
// <ResponsiveDialog open={showInputMoisture} onClose={closeMoistureDialog}>
|
|
||||||
// <DialogTitle>Input new grain moisture</DialogTitle>
|
|
||||||
// <DialogContent>
|
|
||||||
// <DialogContentText style={{ paddingBottom: theme.spacing(0) }}>
|
|
||||||
// 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.
|
|
||||||
// </DialogContentText>
|
|
||||||
// <TextField
|
|
||||||
// label={"Grain Moisture"}
|
|
||||||
// value={moistureInput}
|
|
||||||
// onChange={event => setMoistureInput(event.target.value)}
|
|
||||||
// InputProps={{
|
|
||||||
// endAdornment: <InputAdornment position="end">%</InputAdornment>
|
|
||||||
// }}
|
|
||||||
// style={{ marginTop: theme.spacing(2) }}
|
|
||||||
// fullWidth
|
|
||||||
// variant="outlined"
|
|
||||||
// />
|
|
||||||
// <Typography variant="body1" style={{ marginTop: theme.spacing(2) }}>
|
|
||||||
// Weather
|
|
||||||
// </Typography>
|
|
||||||
// <TextField
|
|
||||||
// label={"Outdoor Temperature"}
|
|
||||||
// value={tempInput}
|
|
||||||
// onChange={event => setTempInput(event.target.value)}
|
|
||||||
// InputProps={{
|
|
||||||
// endAdornment: (
|
|
||||||
// <InputAdornment position="end">
|
|
||||||
// {getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT
|
|
||||||
// ? "°F"
|
|
||||||
// : "℃"}
|
|
||||||
// </InputAdornment>
|
|
||||||
// )
|
|
||||||
// }}
|
|
||||||
// style={{ marginTop: theme.spacing(2) }}
|
|
||||||
// fullWidth
|
|
||||||
// variant="outlined"
|
|
||||||
// />
|
|
||||||
// <TextField
|
|
||||||
// label={"Outdoor Humidity"}
|
|
||||||
// value={outdoorHumidityInput}
|
|
||||||
// onChange={event => setOutdoorHumidityInput(event.target.value)}
|
|
||||||
// InputProps={{
|
|
||||||
// endAdornment: <InputAdornment position="end">%</InputAdornment>
|
|
||||||
// }}
|
|
||||||
// style={{ marginTop: theme.spacing(2) }}
|
|
||||||
// fullWidth
|
|
||||||
// variant="outlined"
|
|
||||||
// />
|
|
||||||
// </DialogContent>
|
|
||||||
// <DialogActions>
|
|
||||||
// <Button
|
|
||||||
// onClick={() => {
|
|
||||||
// // setNewPreset(0);
|
|
||||||
// setNewBinMode(bin.settings.mode);
|
|
||||||
// setShowInputMoisture(false);
|
|
||||||
// }}
|
|
||||||
// color="primary">
|
|
||||||
// Cancel
|
|
||||||
// </Button>
|
|
||||||
// <Button onClick={setModeConditioning} color="primary">
|
|
||||||
// Update
|
|
||||||
// </Button>
|
|
||||||
// </DialogActions>
|
|
||||||
// </ResponsiveDialog>
|
|
||||||
// );
|
|
||||||
// };
|
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <Skeleton variant="rectangular" height={460} />;
|
return <Skeleton variant="rectangular" height={460} />;
|
||||||
}
|
}
|
||||||
|
|
@ -2046,7 +1958,7 @@ export default function BinVisualizer(props: Props) {
|
||||||
b.settings.storage = storageType;
|
b.settings.storage = storageType;
|
||||||
if (b.settings.inventory) {
|
if (b.settings.inventory) {
|
||||||
if (b.settings.inventory.initialMoisture)
|
if (b.settings.inventory.initialMoisture)
|
||||||
b.settings.inventory.initialMoisture = Number(moistureInput);
|
b.settings.inventory.initialMoisture = targetMoisture;
|
||||||
//update all the grain stuff
|
//update all the grain stuff
|
||||||
b.settings.inventory.grainType = grainType;
|
b.settings.inventory.grainType = grainType;
|
||||||
b.settings.inventory.customTypeName = customTypeName;
|
b.settings.inventory.customTypeName = customTypeName;
|
||||||
|
|
@ -2057,14 +1969,14 @@ export default function BinVisualizer(props: Props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (b.settings.outdoorTemp !== undefined) {
|
if (b.settings.outdoorTemp !== undefined) {
|
||||||
let t = Number(tempInput);
|
let t = outdoorTemp;
|
||||||
if (user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
|
if (user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
|
||||||
t = FtoC(Number(tempInput));
|
t = FtoC(outdoorTemp);
|
||||||
}
|
}
|
||||||
b.settings.outdoorTemp = t;
|
b.settings.outdoorTemp = t;
|
||||||
}
|
}
|
||||||
if (b.settings.outdoorHumidity !== undefined)
|
if (b.settings.outdoorHumidity !== undefined)
|
||||||
b.settings.outdoorHumidity = Number(outdoorHumidityInput);
|
b.settings.outdoorHumidity = outdoorHumidity;
|
||||||
|
|
||||||
if (b.settings.mode != newBinMode){
|
if (b.settings.mode != newBinMode){
|
||||||
if(b.settings.inventory){
|
if(b.settings.inventory){
|
||||||
|
|
@ -2080,26 +1992,10 @@ export default function BinVisualizer(props: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card raised className={classes.cardContent}>
|
<Card raised className={classes.cardContent}>
|
||||||
{/* {moistureDialog()} */}
|
|
||||||
{cfmLowDialog()}
|
{cfmLowDialog()}
|
||||||
{cfmHighDialog()}
|
{cfmHighDialog()}
|
||||||
{changeGrain()}
|
{changeGrain()}
|
||||||
{storageTimeDialog()}
|
{storageTimeDialog()}
|
||||||
{/* <DevicePresetsFromPicker
|
|
||||||
preferences={preferences}
|
|
||||||
binKey={bin.key()}
|
|
||||||
devices={devices}
|
|
||||||
refreshCallback={success => {
|
|
||||||
setShowInputMoisture(false);
|
|
||||||
setNewPreset(0)
|
|
||||||
if (success) updateBin();
|
|
||||||
}}
|
|
||||||
parentPreset={newPreset}
|
|
||||||
grain={bin.settings.inventory?.grainType}
|
|
||||||
binCables={cables}
|
|
||||||
compDevMap={componentDevices}
|
|
||||||
presets={binPresets}
|
|
||||||
/> */}
|
|
||||||
<ModeChangeDialog
|
<ModeChangeDialog
|
||||||
binKey={bin.key()}
|
binKey={bin.key()}
|
||||||
binMode={newBinMode}
|
binMode={newBinMode}
|
||||||
|
|
@ -2107,18 +2003,32 @@ export default function BinVisualizer(props: Props) {
|
||||||
devices={devices}
|
devices={devices}
|
||||||
open={openModeChange}
|
open={openModeChange}
|
||||||
binCables={cables}
|
binCables={cables}
|
||||||
|
presets={binPresets}
|
||||||
compDevMap={componentDevices}
|
compDevMap={componentDevices}
|
||||||
preferences={preferences}
|
preferences={preferences}
|
||||||
onClose={(refresh) => {
|
onClose={(refresh) => {
|
||||||
setOpenModeChange(false)
|
setOpenModeChange(false)
|
||||||
if(refresh) updateBin();
|
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={() => {
|
startChange={() => {
|
||||||
setModeChangeInProgress(true)
|
setModeChangeInProgress(true)
|
||||||
}}
|
}}
|
||||||
changeComplete={() => {
|
changeComplete={() => {
|
||||||
setModeChangeInProgress(false)
|
setModeChangeInProgress(false)
|
||||||
}}
|
}}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
<Box paddingRight={1}>
|
<Box paddingRight={1}>
|
||||||
{binMode()}
|
{binMode()}
|
||||||
|
|
|
||||||
|
|
@ -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 { Ambient } from "models/Ambient"
|
||||||
import { Controller } from "models/Controller"
|
import { Controller } from "models/Controller"
|
||||||
import { Plenum } from "models/Plenum"
|
import { Plenum } from "models/Plenum"
|
||||||
import React, { useEffect, useState } from "react"
|
import React, { useState } from "react"
|
||||||
import PickComponentSet, { ComponentSet } from "./pickComponentSet"
|
import PickComponentSet, { ComponentSet } from "./pickComponentSet"
|
||||||
import { cloneDeep } from "lodash"
|
import { cloneDeep } from "lodash"
|
||||||
|
import { DevicePreset } from "models/DevicePreset"
|
||||||
|
import { pond } from "protobuf-ts/pond"
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
@ -12,12 +14,14 @@ interface Props {
|
||||||
ambients: Ambient[]
|
ambients: Ambient[]
|
||||||
heaters: Controller[]
|
heaters: Controller[]
|
||||||
fans: Controller[]
|
fans: Controller[]
|
||||||
|
binMode: pond.BinMode
|
||||||
|
presets?: DevicePreset[]
|
||||||
updateSets: (sets: ComponentSet[]) => void
|
updateSets: (sets: ComponentSet[]) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default function ConditioningSelector(props: Props){
|
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<ComponentSet[]>([])
|
const [currentSets, setCurrentSets] = useState<ComponentSet[]>([])
|
||||||
|
|
||||||
const sensorIndex = (key: string) => {
|
const sensorIndex = (key: string) => {
|
||||||
|
|
@ -64,6 +68,8 @@ export default function ConditioningSelector(props: Props){
|
||||||
sensor={plenum}
|
sensor={plenum}
|
||||||
heaters={heaters}
|
heaters={heaters}
|
||||||
fans={fans}
|
fans={fans}
|
||||||
|
binMode={binMode}
|
||||||
|
presets={presets}
|
||||||
updateSet={setUpdate}
|
updateSet={setUpdate}
|
||||||
/>
|
/>
|
||||||
)})}
|
)})}
|
||||||
|
|
@ -74,6 +80,8 @@ export default function ConditioningSelector(props: Props){
|
||||||
sensor={ambient}
|
sensor={ambient}
|
||||||
heaters={heaters}
|
heaters={heaters}
|
||||||
fans={fans}
|
fans={fans}
|
||||||
|
binMode={binMode}
|
||||||
|
presets={presets}
|
||||||
updateSet={setUpdate}
|
updateSet={setUpdate}
|
||||||
/>
|
/>
|
||||||
)})}
|
)})}
|
||||||
|
|
|
||||||
|
|
@ -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 ResponsiveDialog from "common/ResponsiveDialog";
|
||||||
import { useComponentAPI, useInteractionsAPI } from "hooks";
|
import { useComponentAPI, useInteractionsAPI } from "hooks";
|
||||||
import { Component, Device, Interaction } from "models";
|
import { Component, Device, Interaction } from "models";
|
||||||
|
|
@ -19,7 +19,7 @@ import { sameComponentID } from "pbHelpers/Component";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { lowerCase } from "lodash";
|
import { lowerCase } from "lodash";
|
||||||
import { describeMeasurement } from "pbHelpers/MeasurementDescriber";
|
import { describeMeasurement } from "pbHelpers/MeasurementDescriber";
|
||||||
import { getTemperatureUnit } from "utils";
|
import { fahrenheitToCelsius, getTemperatureUnit } from "utils";
|
||||||
import { GetGrainExtensionMap } from "grain";
|
import { GetGrainExtensionMap } from "grain";
|
||||||
import { PromiseProgress, Stage, Step as PromiseStep } from "common/PromiseProgress";
|
import { PromiseProgress, Stage, Step as PromiseStep } from "common/PromiseProgress";
|
||||||
|
|
||||||
|
|
@ -37,10 +37,11 @@ interface Props {
|
||||||
startChange: () => void;
|
startChange: () => void;
|
||||||
changeComplete: () => void;
|
changeComplete: () => void;
|
||||||
defaultTargetMoisture?: number;
|
defaultTargetMoisture?: number;
|
||||||
changeTargetMoisture?: (newTarget: number) => {}
|
changeTargetMoisture?: (newTarget: number) => void
|
||||||
defaultOutdoorTemp?: number;
|
defaultOutdoorTemp?: number;
|
||||||
changeOutdoorTemp?: (newTemp: number) => {}
|
changeOutdoorTemp?: (newTemp: number) => void
|
||||||
defaultOutdoorHumidity?: number;
|
defaultOutdoorHumidity?: number;
|
||||||
|
changeOutdoorHumidity?: (newHumidity: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Option {
|
interface Option {
|
||||||
|
|
@ -67,7 +68,13 @@ export default function ModeChangeDialog(props: Props){
|
||||||
grain,
|
grain,
|
||||||
startChange,
|
startChange,
|
||||||
changeComplete,
|
changeComplete,
|
||||||
defaultTargetMoisture
|
defaultTargetMoisture,
|
||||||
|
defaultOutdoorTemp,
|
||||||
|
defaultOutdoorHumidity,
|
||||||
|
changeTargetMoisture,
|
||||||
|
changeOutdoorTemp,
|
||||||
|
changeOutdoorHumidity,
|
||||||
|
presets
|
||||||
} = props
|
} = props
|
||||||
const [{as}] = useGlobalState()
|
const [{as}] = useGlobalState()
|
||||||
const [componentSets, setComponentSets] = useState<ComponentSet[]>([])
|
const [componentSets, setComponentSets] = useState<ComponentSet[]>([])
|
||||||
|
|
@ -85,20 +92,24 @@ export default function ModeChangeDialog(props: Props){
|
||||||
const [ambients, setAmbients] = useState<Ambient[]>([])
|
const [ambients, setAmbients] = useState<Ambient[]>([])
|
||||||
const [heaters, setHeaters] = useState<Controller[]>([])
|
const [heaters, setHeaters] = useState<Controller[]>([])
|
||||||
const [fans, setFans] = useState<Controller[]>([])
|
const [fans, setFans] = useState<Controller[]>([])
|
||||||
const [selectedPreset, setSelectedPreset] = useState<DevicePreset | undefined>(undefined);
|
|
||||||
const grainExtensionMap = GetGrainExtensionMap();
|
const grainExtensionMap = GetGrainExtensionMap();
|
||||||
const [conflictingSetInteractions, setConflictingSetInteractions] = useState<Interaction[]>([])
|
const [conflictingSetInteractions, setConflictingSetInteractions] = useState<Interaction[]>([])
|
||||||
const [interactionsToAdd, setInteractionsToAdd] = useState<pond.MultiInteractionSettings>()
|
const [interactionsToAdd, setInteractionsToAdd] = useState<pond.MultiInteractionSettings>()
|
||||||
const [promiseStages, setPromiseStages] = useState<Stage[]>([])
|
const [promiseStages, setPromiseStages] = useState<Stage[]>([])
|
||||||
const [dialogSteps, setDialogSteps] = useState<DialogStep[]>([])
|
const [dialogSteps, setDialogSteps] = useState<DialogStep[]>([])
|
||||||
const [currentStep, setCurrentStep] = useState(0)
|
const [currentStep, setCurrentStep] = useState(0)
|
||||||
|
const theme = useTheme()
|
||||||
|
|
||||||
//the state variables for the moisture
|
//the state variables for the moisture
|
||||||
const [moistureInput, setMoistureInput] = useState<string>("")
|
const [moistureInput, setMoistureInput] = useState<string>("")
|
||||||
|
const [outdoorTempInput, setOutdoorTempInput] = useState<string>("")
|
||||||
|
const [outdoorHumidityInput, setOutdoorHumidityInput] = useState<string>("")
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
setMoistureInput(defaultTargetMoisture?.toFixed(2) ?? "0")
|
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
|
//get the interactions and components for the device when it is selected from the dropdown
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
|
|
@ -214,7 +225,8 @@ if (!selectedDevice) return;
|
||||||
|
|
||||||
const buildHeaterInteraction = (
|
const buildHeaterInteraction = (
|
||||||
sensor: Plenum | Ambient,
|
sensor: Plenum | Ambient,
|
||||||
heater: Controller
|
heater: Controller,
|
||||||
|
customPreset?: DevicePreset
|
||||||
): pond.InteractionSettings => {
|
): pond.InteractionSettings => {
|
||||||
let interaction = pond.InteractionSettings.create({
|
let interaction = pond.InteractionSettings.create({
|
||||||
source: sensor.location(),
|
source: sensor.location(),
|
||||||
|
|
@ -236,11 +248,10 @@ if (!selectedDevice) return;
|
||||||
|
|
||||||
let temp = 0;
|
let temp = 0;
|
||||||
let hum = 0;
|
let hum = 0;
|
||||||
|
|
||||||
//if a preset was selected use its temp/hum values
|
//if a preset was selected use its temp/hum values
|
||||||
if (selectedPreset !== undefined) {
|
if (customPreset !== undefined) {
|
||||||
temp = selectedPreset.temp();
|
temp = customPreset.temp();
|
||||||
hum = selectedPreset.hum();
|
hum = customPreset.hum();
|
||||||
} else {
|
} else {
|
||||||
//otherwise use default values
|
//otherwise use default values
|
||||||
switch(binMode){
|
switch(binMode){
|
||||||
|
|
@ -303,7 +314,8 @@ if (!selectedDevice) return;
|
||||||
sensor: Plenum | Ambient,
|
sensor: Plenum | Ambient,
|
||||||
fan: Controller,
|
fan: Controller,
|
||||||
tempComparison: "greater" | "less",
|
tempComparison: "greater" | "less",
|
||||||
humidityComparison: "greater" | "less"
|
humidityComparison: "greater" | "less",
|
||||||
|
customPreset?: DevicePreset
|
||||||
) => {
|
) => {
|
||||||
let interaction = pond.InteractionSettings.create({
|
let interaction = pond.InteractionSettings.create({
|
||||||
source: sensor.location(),
|
source: sensor.location(),
|
||||||
|
|
@ -323,13 +335,13 @@ if (!selectedDevice) return;
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let tempPreset = 0;
|
let tempPreset = 0;
|
||||||
let humidityPreset = 0;
|
let humidityPreset = 0;
|
||||||
|
|
||||||
//if a custom preset was selected use it
|
//if a custom preset was selected use it
|
||||||
if (selectedPreset !== undefined) {
|
if (customPreset !== undefined) {
|
||||||
tempPreset = selectedPreset.temp();
|
tempPreset = customPreset.temp();
|
||||||
humidityPreset = selectedPreset.hum();
|
humidityPreset = customPreset.hum();
|
||||||
} else {
|
} else {
|
||||||
//otherwise use one of the defaults
|
//otherwise use one of the defaults
|
||||||
switch (binMode) {
|
switch (binMode) {
|
||||||
|
|
@ -437,7 +449,6 @@ if (!selectedDevice) return;
|
||||||
}
|
}
|
||||||
componentSets.forEach(set => {
|
componentSets.forEach(set => {
|
||||||
set.controllers.forEach(controller => {
|
set.controllers.forEach(controller => {
|
||||||
console.log(controller)
|
|
||||||
let newStep: PromiseStep = {
|
let newStep: PromiseStep = {
|
||||||
title: "Updating " + controller.name(),
|
title: "Updating " + controller.name(),
|
||||||
promise: componentAPI.update(selectedDevice.id(), controller.settings)
|
promise: componentAPI.update(selectedDevice.id(), controller.settings)
|
||||||
|
|
@ -461,6 +472,7 @@ if (!selectedDevice) return;
|
||||||
sets.forEach((set) => {
|
sets.forEach((set) => {
|
||||||
//loop through the controllers
|
//loop through the controllers
|
||||||
set.controllers.forEach(controller => {
|
set.controllers.forEach(controller => {
|
||||||
|
let customPreset = set.selectedPresets.get(controller.key())
|
||||||
//filter the conflicting interactions for that controller
|
//filter the conflicting interactions for that controller
|
||||||
let c = existingInteractions.filter(i => {
|
let c = existingInteractions.filter(i => {
|
||||||
let conflicting = false;
|
let conflicting = false;
|
||||||
|
|
@ -478,17 +490,17 @@ if (!selectedDevice) return;
|
||||||
case pond.BinMode.BIN_MODE_COOLDOWN:
|
case pond.BinMode.BIN_MODE_COOLDOWN:
|
||||||
// if the controller is a heater create heater interaction
|
// if the controller is a heater create heater interaction
|
||||||
if(controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_HEATER){
|
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 ||
|
}else if(controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_AERATION_FAN ||
|
||||||
controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_EXHAUST_FAN){
|
controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_EXHAUST_FAN){
|
||||||
// if the controller is a fan create a fan interaction
|
// 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;
|
break;
|
||||||
case pond.BinMode.BIN_MODE_DRYING:
|
case pond.BinMode.BIN_MODE_DRYING:
|
||||||
// if the controller is a heater create heater interaction
|
// if the controller is a heater create heater interaction
|
||||||
if(controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_HEATER){
|
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 ||
|
}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
|
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
|
//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
|
controller.settings.defaultOutputState = quack.OutputMode.OUTPUT_MODE_ON
|
||||||
}else{
|
}else{
|
||||||
//otherwise make a fan interaction
|
//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;
|
break;
|
||||||
|
|
@ -505,7 +517,7 @@ if (!selectedDevice) return;
|
||||||
if(controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_AERATION_FAN ||
|
if(controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_AERATION_FAN ||
|
||||||
controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_EXHAUST_FAN){
|
controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_EXHAUST_FAN){
|
||||||
//create fan interaction
|
//create fan interaction
|
||||||
multiInteractionSettings.interactions.push(buildFanInteraction(set.sensor, controller, "less", "greater"))
|
multiInteractionSettings.interactions.push(buildFanInteraction(set.sensor, controller, "less", "greater", customPreset))
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -563,55 +575,81 @@ if (!selectedDevice) return;
|
||||||
//this step will only be used for drying and hydrating
|
//this step will only be used for drying and hydrating
|
||||||
const moistureStep = () => {
|
const moistureStep = () => {
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<DialogContentText style={{ paddingBottom: theme.spacing(0) }}>
|
<DialogContentText style={{ paddingBottom: theme.spacing(0) }}>
|
||||||
Updating the current grain moisture will calibrate the estimate for more accurate
|
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
|
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.
|
updating with drastic changes in the weather or using a predicted average.
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
<TextField
|
<TextField
|
||||||
label={"Grain Moisture"}
|
label={"Grain Moisture"}
|
||||||
value={moistureInput}
|
value={moistureInput}
|
||||||
onChange={event => setMoistureInput(event.target.value)}
|
error={isNaN(parseFloat(moistureInput))}
|
||||||
InputProps={{
|
helperText={isNaN(parseFloat(moistureInput)) ? "Must be a valid number" : ""}
|
||||||
endAdornment: <InputAdornment position="end">%</InputAdornment>
|
onChange={event => {
|
||||||
}}
|
setMoistureInput(event.target.value)
|
||||||
style={{ marginTop: theme.spacing(2) }}
|
if(changeTargetMoisture && !isNaN(parseFloat(event.target.value))){
|
||||||
fullWidth
|
changeTargetMoisture(parseFloat(event.target.value))
|
||||||
variant="outlined"
|
}
|
||||||
/>
|
}}
|
||||||
<Typography variant="body1" style={{ marginTop: theme.spacing(2) }}>
|
InputProps={{
|
||||||
Weather
|
endAdornment: <InputAdornment position="end">%</InputAdornment>
|
||||||
</Typography>
|
}}
|
||||||
<TextField
|
style={{ marginTop: theme.spacing(2) }}
|
||||||
label={"Outdoor Temperature"}
|
fullWidth
|
||||||
value={tempInput}
|
variant="outlined"
|
||||||
onChange={event => setTempInput(event.target.value)}
|
/>
|
||||||
InputProps={{
|
<Typography variant="body1" style={{ marginTop: theme.spacing(2) }}>
|
||||||
endAdornment: (
|
Weather
|
||||||
<InputAdornment position="end">
|
</Typography>
|
||||||
{getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT
|
<TextField
|
||||||
? "°F"
|
label={"Outdoor Temperature"}
|
||||||
: "℃"}
|
value={outdoorTempInput}
|
||||||
</InputAdornment>
|
error={isNaN(parseFloat(outdoorTempInput))}
|
||||||
)
|
helperText={isNaN(parseFloat(outdoorTempInput)) ? "Must be a valid number" : ""}
|
||||||
}}
|
onChange={event => {
|
||||||
style={{ marginTop: theme.spacing(2) }}
|
setOutdoorTempInput(event.target.value)
|
||||||
fullWidth
|
if(changeOutdoorTemp && !isNaN(parseFloat(event.target.value))){
|
||||||
variant="outlined"
|
//if the users prefs are F will need to convert it to C when sending it back
|
||||||
/>
|
let val = parseFloat(event.target.value)
|
||||||
<TextField
|
if(getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT){
|
||||||
label={"Outdoor Humidity"}
|
val = fahrenheitToCelsius(val)
|
||||||
value={outdoorHumidityInput}
|
}
|
||||||
onChange={event => setOutdoorHumidityInput(event.target.value)}
|
changeOutdoorTemp(val)
|
||||||
InputProps={{
|
}
|
||||||
endAdornment: <InputAdornment position="end">%</InputAdornment>
|
}}
|
||||||
}}
|
InputProps={{
|
||||||
style={{ marginTop: theme.spacing(2) }}
|
endAdornment: (
|
||||||
fullWidth
|
<InputAdornment position="end">
|
||||||
variant="outlined"
|
{getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT
|
||||||
/>
|
? "°F"
|
||||||
</Box>
|
: "℃"}
|
||||||
|
</InputAdornment>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
style={{ marginTop: theme.spacing(2) }}
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
label={"Outdoor Humidity"}
|
||||||
|
value={outdoorHumidityInput}
|
||||||
|
error={isNaN(parseFloat(outdoorHumidityInput))}
|
||||||
|
helperText={isNaN(parseFloat(outdoorHumidityInput)) ? "Must be a valid number" : ""}
|
||||||
|
onChange={event => {
|
||||||
|
setOutdoorHumidityInput(event.target.value)
|
||||||
|
if(changeOutdoorHumidity && !isNaN(parseFloat(event.target.value))){
|
||||||
|
changeOutdoorHumidity(parseFloat(event.target.value))
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
endAdornment: <InputAdornment position="end">%</InputAdornment>
|
||||||
|
}}
|
||||||
|
style={{ marginTop: theme.spacing(2) }}
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -652,6 +690,8 @@ if (!selectedDevice) return;
|
||||||
ambients={ambients}
|
ambients={ambients}
|
||||||
fans={fans}
|
fans={fans}
|
||||||
heaters={heaters}
|
heaters={heaters}
|
||||||
|
presets={presets}
|
||||||
|
binMode={binMode}
|
||||||
updateSets={(sets) => {
|
updateSets={(sets) => {
|
||||||
//update the component sets that will be used for the interactions
|
//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
|
//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;
|
||||||
<Box>
|
<Box>
|
||||||
<PromiseProgress
|
<PromiseProgress
|
||||||
stages={promiseStages}
|
stages={promiseStages}
|
||||||
|
automaticStart
|
||||||
failFast
|
failFast
|
||||||
|
onStart={() => {
|
||||||
|
startChange()
|
||||||
|
}}
|
||||||
onComplete={()=>{
|
onComplete={()=>{
|
||||||
console.log("COMPLETED PROMISES")
|
close(true)
|
||||||
setCurrentStep(0)//reset the stepper
|
setCurrentStep(0)//reset the stepper
|
||||||
changeComplete()//change the boolean preventing more mode changes
|
changeComplete()//change the boolean preventing more mode changes
|
||||||
}}
|
}}
|
||||||
|
|
@ -686,13 +730,13 @@ if (!selectedDevice) return;
|
||||||
|
|
||||||
const stepper = () => {
|
const stepper = () => {
|
||||||
return (
|
return (
|
||||||
<Stepper nonLinear activeStep={currentStep} style={{ width: "100%" }}>
|
<Stepper nonLinear activeStep={currentStep} style={{ width: "100%", marginBottom: theme.spacing(2) }}>
|
||||||
{dialogSteps.map(dialogStep => {
|
{dialogSteps.map((dialogStep, i) => {
|
||||||
const labelProps: {
|
const labelProps: {
|
||||||
optional?: React.ReactNode;
|
optional?: React.ReactNode;
|
||||||
} = {};
|
} = {};
|
||||||
return (
|
return (
|
||||||
<Step key={dialogStep.label} completed={dialogStep.completed}>
|
<Step key={i} completed={dialogStep.completed}>
|
||||||
<StepLabel {...labelProps}>{dialogStep.label}</StepLabel>
|
<StepLabel {...labelProps}>{dialogStep.label}</StepLabel>
|
||||||
</Step>
|
</Step>
|
||||||
);
|
);
|
||||||
|
|
@ -705,24 +749,24 @@ if (!selectedDevice) return;
|
||||||
let buttons: JSX.Element[] = []
|
let buttons: JSX.Element[] = []
|
||||||
//this button should be on every step of all of them
|
//this button should be on every step of all of them
|
||||||
buttons.push(
|
buttons.push(
|
||||||
<Button onClick={() => {close(false)}}>Close</Button>
|
<Button key={"close"} onClick={() => {close(false)}}>Close</Button>
|
||||||
)
|
)
|
||||||
// this button should appear if the step they are on is not 0 and not the last step
|
// 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){
|
if(currentStep !== 0 && currentStep !== dialogSteps.length-1){
|
||||||
buttons.push(
|
buttons.push(
|
||||||
<Button onClick={()=>{setCurrentStep(currentStep-1)}}>Back</Button>
|
<Button key={"back"} onClick={()=>{setCurrentStep(currentStep-1)}}>Back</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// this button should appear if the step they are on is less than the last step -1
|
// this button should appear if the step they are on is less than the last step -1
|
||||||
if(currentStep < dialogSteps.length-2){
|
if(currentStep < dialogSteps.length-2){
|
||||||
buttons.push(
|
buttons.push(
|
||||||
<Button onClick={() => {setCurrentStep(currentStep+1)}}>Next</Button>
|
<Button key={"next"} onClick={() => {setCurrentStep(currentStep+1)}}>Next</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if((binMode === pond.BinMode.BIN_MODE_STORAGE || binMode === pond.BinMode.BIN_MODE_COOLDOWN && currentStep === 0) ||
|
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){
|
(binMode === pond.BinMode.BIN_MODE_DRYING || binMode === pond.BinMode.BIN_MODE_HYDRATING) && currentStep === 1){
|
||||||
buttons.push(
|
buttons.push(
|
||||||
<Button onClick={() => {
|
<Button key={"confirm"} onClick={() => {
|
||||||
//if the bin mode is storage, just update the controllers for the selected device and close
|
//if the bin mode is storage, just update the controllers for the selected device and close
|
||||||
if(binMode === pond.BinMode.BIN_MODE_STORAGE){
|
if(binMode === pond.BinMode.BIN_MODE_STORAGE){
|
||||||
close(true)
|
close(true)
|
||||||
|
|
@ -741,11 +785,10 @@ if (!selectedDevice) return;
|
||||||
.catch();
|
.catch();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}else{
|
}else{//if it is cooldown, drying, or hydrating, it will need to go to the progress step and initiate all of the promises
|
||||||
setCurrentStep(dialogSteps.length-1)//set the stepper to the progress step
|
|
||||||
buildPromises()
|
buildPromises()
|
||||||
|
setCurrentStep(dialogSteps.length-1)//set the stepper to the progress step
|
||||||
}
|
}
|
||||||
//if it is cooldown, drying, or hydrating, it will need to go to the progress step and initiate all of the promises
|
|
||||||
}}>Confirm</Button>
|
}}>Confirm</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -778,13 +821,6 @@ if (!selectedDevice) return;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{/* <PromiseProgress
|
|
||||||
open={openProgress}
|
|
||||||
stages={promiseStages}
|
|
||||||
onClose={()=>{
|
|
||||||
setOpenProgress(false)
|
|
||||||
close(refresh)
|
|
||||||
}} /> */}
|
|
||||||
<ResponsiveDialog open={open} onClose={() => {close(false)}}>
|
<ResponsiveDialog open={open} onClose={() => {close(false)}}>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
{binMode === pond.BinMode.BIN_MODE_STORAGE ? "Entering Storage Mode" : "Choose Device for " + modeDescription() + " Method"}
|
{binMode === pond.BinMode.BIN_MODE_STORAGE ? "Entering Storage Mode" : "Choose Device for " + modeDescription() + " Method"}
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,89 @@
|
||||||
import { CheckBox, ExpandMore } from "@mui/icons-material";
|
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 { cloneDeep } from "lodash";
|
||||||
import { Ambient } from "models/Ambient";
|
import { Ambient } from "models/Ambient";
|
||||||
import { Controller } from "models/Controller";
|
import { Controller } from "models/Controller";
|
||||||
|
import { DevicePreset } from "models/DevicePreset";
|
||||||
import { Plenum } from "models/Plenum";
|
import { Plenum } from "models/Plenum";
|
||||||
|
import { pond } from "protobuf-ts/pond";
|
||||||
import { quack } from "protobuf-ts/quack";
|
import { quack } from "protobuf-ts/quack";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
export interface ComponentSet {
|
export interface ComponentSet {
|
||||||
sensor: Plenum | Ambient,
|
sensor: Plenum | Ambient,
|
||||||
controllers: Controller[]
|
controllers: Controller[]
|
||||||
|
selectedPresets: Map<string, DevicePreset>
|
||||||
combo?: boolean //if the set contains both a heater and a fan
|
combo?: boolean //if the set contains both a heater and a fan
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Option {
|
||||||
|
label: string;
|
||||||
|
id: number;
|
||||||
|
icon?: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
sensor: Plenum | Ambient
|
sensor: Plenum | Ambient
|
||||||
|
binMode: pond.BinMode
|
||||||
heaters?: Controller[]
|
heaters?: Controller[]
|
||||||
fans?: Controller[]
|
fans?: Controller[]
|
||||||
|
presets?: DevicePreset[]
|
||||||
updateSet: (sensorKey: string, componentSet?: ComponentSet) => void
|
updateSet: (sensorKey: string, componentSet?: ComponentSet) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PickComponentSet(props: Props) {
|
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 [sensorSelected, setSensorSelected] = useState(false)
|
||||||
const [currentSet, setCurrentSet] = useState<ComponentSet | undefined>()
|
const [currentSet, setCurrentSet] = useState<ComponentSet | undefined>()
|
||||||
|
|
||||||
|
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) => {
|
const controllerIndex = (key: string) => {
|
||||||
if(!currentSet) return -1
|
if(!currentSet) return -1
|
||||||
let index = -1
|
let index = -1
|
||||||
|
|
@ -64,7 +123,8 @@ export default function PickComponentSet(props: Props) {
|
||||||
if(checked){
|
if(checked){
|
||||||
newSet = {
|
newSet = {
|
||||||
sensor: sensor,
|
sensor: sensor,
|
||||||
controllers: []
|
controllers: [],
|
||||||
|
selectedPresets: new Map<string, DevicePreset>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setCurrentSet(newSet)
|
setCurrentSet(newSet)
|
||||||
|
|
@ -78,32 +138,59 @@ export default function PickComponentSet(props: Props) {
|
||||||
<Accordion>
|
<Accordion>
|
||||||
<AccordionSummary expandIcon={<ExpandMore />}>Controllers Selected: {currentSet.controllers.length}</AccordionSummary>
|
<AccordionSummary expandIcon={<ExpandMore />}>Controllers Selected: {currentSet.controllers.length}</AccordionSummary>
|
||||||
<AccordionDetails>
|
<AccordionDetails>
|
||||||
{heaters && heaters.length > 0 &&
|
{heaters && heaters.length > 0 && binMode !== pond.BinMode.BIN_MODE_HYDRATING &&
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Typography>Heaters</Typography>
|
<Typography>Heaters</Typography>
|
||||||
{heaters.map(heater => (
|
{heaters.map(heater => (
|
||||||
<FormControlLabel
|
<Grid2 key={heater.key()} container justifyContent="space-between" direction="row" alignContent="center" alignItems="center">
|
||||||
key={heater.key()}
|
<Grid2 size={{xs: 12, sm: 6}}>
|
||||||
control={
|
<FormControlLabel
|
||||||
<Checkbox
|
control={
|
||||||
onChange={(_, checked) => {
|
<Checkbox
|
||||||
let set = cloneDeep(currentSet)
|
onChange={(_, checked) => {
|
||||||
if(checked){//if checked need to add it to the controllers
|
let set = cloneDeep(currentSet)
|
||||||
set.controllers.push(heater)
|
if(checked){//if checked need to add it to the controllers
|
||||||
}else{//will need to make sure it is not in the controllers
|
set.controllers.push(heater)
|
||||||
let index = controllerIndex(heater.key())
|
}else{//will need to make sure it is not in the controllers
|
||||||
if (index !== -1){
|
let index = controllerIndex(heater.key())
|
||||||
set.controllers.splice(index, 1)
|
if (index !== -1){
|
||||||
}
|
set.controllers.splice(index, 1)
|
||||||
}
|
}
|
||||||
set.combo = comboCheck(set)
|
}
|
||||||
setCurrentSet(set)
|
set.combo = comboCheck(set)
|
||||||
updateSet(sensor.key(), set)
|
setCurrentSet(set)
|
||||||
}}
|
updateSet(sensor.key(), set)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={heater.name()}
|
||||||
/>
|
/>
|
||||||
}
|
</Grid2>
|
||||||
label={heater.name()}
|
<Grid2 size={{xs: 12, sm: 6}}>
|
||||||
/>
|
{presets && getOptionsForController(pond.ControllerType.CONTROLLER_TYPE_HEATER).length > 0 &&
|
||||||
|
<Autocomplete
|
||||||
|
disablePortal
|
||||||
|
options={getOptionsForController(pond.ControllerType.CONTROLLER_TYPE_HEATER)}
|
||||||
|
fullWidth
|
||||||
|
getOptionLabel={option => 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 => <TextField {...params} variant="outlined" label="Preset" />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</Grid2>
|
||||||
|
</Grid2>
|
||||||
))}
|
))}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
|
|
@ -111,28 +198,55 @@ export default function PickComponentSet(props: Props) {
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Typography>Fans</Typography>
|
<Typography>Fans</Typography>
|
||||||
{fans.map(fan => (
|
{fans.map(fan => (
|
||||||
<FormControlLabel
|
<Grid2 key={fan.key()} container justifyContent="space-between" direction="row" alignContent="center" alignItems="center">
|
||||||
key={fan.key()}
|
<Grid2 size={{xs: 12, sm: 6}}>
|
||||||
control={
|
<FormControlLabel
|
||||||
<Checkbox
|
control={
|
||||||
onChange={(_, checked) => {
|
<Checkbox
|
||||||
let set = cloneDeep(currentSet)
|
onChange={(_, checked) => {
|
||||||
if(checked){//if checked need to add it to the controllers
|
let set = cloneDeep(currentSet)
|
||||||
set.controllers.push(fan)
|
if(checked){//if checked need to add it to the controllers
|
||||||
}else{//will need to make sure it is not in the controllers
|
set.controllers.push(fan)
|
||||||
let index = controllerIndex(fan.key())
|
}else{//will need to make sure it is not in the controllers
|
||||||
if (index !== -1){
|
let index = controllerIndex(fan.key())
|
||||||
set.controllers.splice(index, 1)
|
if (index !== -1){
|
||||||
|
set.controllers.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set.combo = comboCheck(set)
|
||||||
|
setCurrentSet(set)
|
||||||
|
updateSet(sensor.key(), set)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={fan.name()}
|
||||||
|
/>
|
||||||
|
</Grid2>
|
||||||
|
<Grid2 size={{xs: 12, sm: 6}}>
|
||||||
|
{presets && getOptionsForController(pond.ControllerType.CONTROLLER_TYPE_FAN).length > 0 &&
|
||||||
|
<Autocomplete
|
||||||
|
disablePortal
|
||||||
|
options={getOptionsForController(pond.ControllerType.CONTROLLER_TYPE_FAN)}
|
||||||
|
fullWidth
|
||||||
|
getOptionLabel={option => 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 => <TextField {...params} variant="outlined" label="Preset" />}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label={fan.name()}
|
</Grid2>
|
||||||
/>
|
</Grid2>
|
||||||
))}
|
))}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { AxiosResponse } from "axios"
|
import { AxiosResponse } from "axios"
|
||||||
import ResponsiveDialog from "./ResponsiveDialog"
|
import { Box, Button, CircularProgress, DialogActions, DialogContent, DialogTitle, Typography } from "@mui/material"
|
||||||
import { Box, Button, CircularProgress, DialogActions, DialogContent, DialogTitle, Icon, Typography } from "@mui/material"
|
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
import { CheckCircle, Error, Pending } from "@mui/icons-material"
|
import { CheckCircle, Error, Pending } from "@mui/icons-material"
|
||||||
import React from "react"
|
import React from "react"
|
||||||
|
|
@ -19,7 +18,9 @@ export interface Step {
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
stages: Stage[]
|
stages: Stage[]
|
||||||
|
automaticStart: boolean
|
||||||
failFast?: boolean
|
failFast?: boolean
|
||||||
|
onStart?: () => void
|
||||||
onComplete?: () => void
|
onComplete?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,7 +32,7 @@ enum progress {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PromiseProgress(props: Props){
|
export function PromiseProgress(props: Props){
|
||||||
const {stages, failFast, onComplete} = props
|
const {stages, failFast, onStart, onComplete, automaticStart} = props
|
||||||
const [completion, setCompletion] = useState<Map<string, progress>>(new Map())
|
const [completion, setCompletion] = useState<Map<string, progress>>(new Map())
|
||||||
|
|
||||||
const execute = () => {
|
const execute = () => {
|
||||||
|
|
@ -52,7 +53,7 @@ export function PromiseProgress(props: Props){
|
||||||
progMap.set(i+"-"+k, progress.InProgress);
|
progMap.set(i+"-"+k, progress.InProgress);
|
||||||
setCompletion(new Map(progMap));
|
setCompletion(new Map(progMap));
|
||||||
return step.promise
|
return step.promise
|
||||||
.then(resp => {
|
.then(() => {
|
||||||
// mark step as completed
|
// mark step as completed
|
||||||
progMap.set(i+"-"+k, progress.Complete);
|
progMap.set(i+"-"+k, progress.Complete);
|
||||||
setCompletion(new Map(progMap));
|
setCompletion(new Map(progMap));
|
||||||
|
|
@ -73,13 +74,22 @@ export function PromiseProgress(props: Props){
|
||||||
await Promise.all(stepPromises);
|
await Promise.all(stepPromises);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
runStages().catch(err => {
|
if(onStart){
|
||||||
console.error("Execution stopped due to error:", err);
|
onStart()
|
||||||
}).finally(() => {
|
}
|
||||||
if(onComplete) onComplete()
|
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) => {
|
const getCompletion = (completion?: progress) => {
|
||||||
switch(completion){
|
switch(completion){
|
||||||
case progress.InProgress:
|
case progress.InProgress:
|
||||||
|
|
@ -116,11 +126,13 @@ export function PromiseProgress(props: Props){
|
||||||
{stages.map((stage, number) => displayStage(stage, number))}
|
{stages.map((stage, number) => displayStage(stage, number))}
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={() => {
|
{!automaticStart &&
|
||||||
execute()
|
<Button onClick={() => {
|
||||||
}}>
|
execute()
|
||||||
Start
|
}}>
|
||||||
</Button>
|
Start
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -698,8 +698,6 @@ export default function Bin(props: Props) {
|
||||||
components={components}
|
components={components}
|
||||||
componentDevices={componentDevices}
|
componentDevices={componentDevices}
|
||||||
devices={devices}
|
devices={devices}
|
||||||
interactions={interactions}
|
|
||||||
//changeBinMode={changeBinMode}
|
|
||||||
afterUpdate={refresh}
|
afterUpdate={refresh}
|
||||||
refresh={refresh}
|
refresh={refresh}
|
||||||
preferences={preferences}
|
preferences={preferences}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue