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