import { Box, Button, DialogActions, DialogContent, DialogTitle } from "@mui/material"; import ResponsiveDialog from "common/ResponsiveDialog"; import { Bin, Component, Device, Interaction } from "models"; import { pond } from "protobuf-ts/pond"; import React, { useEffect, useState } from "react"; import StorageMode from "./StorageMode"; import DryingMode from "./DryingMode"; import HydratingMode from "./HydratingMode"; import CooldownMode from "./CooldownMode"; import { PromiseProgress, Stage, Step } from "common/PromiseProgress"; import { useBinAPI, useComponentAPI, useInteractionsAPI } from "providers"; import { ComponentSet } from "bin/conditioning/pickComponentSet"; import { cloneDeep } from "lodash"; export interface DeviceChangeData { deviceId: number toRemove: Interaction[] toAdd?: pond.MultiInteractionSettings componentSets: ComponentSet[] } interface Props { newMode: pond.BinMode //used to control which dialog to open open: boolean onClose: () => void bin: Bin binPrefs?: Map devices: Device[] componentDevices: Map componentMap: Map modeUpdated?: (mode: pond.BinMode) => void } export default function BinModeController(props: Props) { const {open, onClose, newMode, devices, componentDevices, componentMap, binPrefs, bin, modeUpdated} = props const [showProgress, setShowProgress] = useState(false) const [deviceComponents, setDeviceComponents] = useState>(new Map()) const [promiseStages, setPromiseStages] = useState([]) const interactionAPI = useInteractionsAPI() const componentAPI = useComponentAPI() const binAPI = useBinAPI(); //the use effects for the initial setup of what will be needed useEffect(()=>{ let newMap:Map = new Map() componentMap.forEach((comp, key) => { //first we need to get the device id this component belongs to let dev = componentDevices.get(key) if(dev){ //check if the key exists in the new map yet if(newMap.has(dev)){ newMap.get(dev)?.push(comp) }else{ newMap.set(dev, [comp]) } } }) setDeviceComponents(newMap) },[componentDevices, componentMap]) //steps involved for devices when changing the bin mode: //remove conflicting interactions //add new interactions //updating the controllers const closeDialog = () => { setShowProgress(false) onClose() } const buildStages = (deviceData: DeviceChangeData[]) => { //build the stages to pass into the PromiseProgress //stage one is to remove conflicting interactions let stages: Stage[] = [] let stage1: Stage = { title: "Remove Interactions", steps: [] } let stage2: Stage = { title: "Add New Interactions", steps: [] } let stage3: Stage = { title: "Update Controllers", steps: [] } deviceData.forEach(data => { data.toRemove.forEach(interaction => { let newStep: Step = { title: "Removing Interaction", promise: () => interactionAPI.removeInteraction(data.deviceId, interaction.key()) } stage1.steps.push(newStep); }) let newInteractions = data.toAdd if(newInteractions){ stage2.steps.push({ title: "Adding Interactions", promise: () => interactionAPI.addMultiInteractions(data.deviceId, newInteractions) }) } data.componentSets.forEach(set => { set.controllers.forEach(controller => { let newStep: Step = { title: "Updating " + controller.name(), promise: () => componentAPI.update(data.deviceId, controller.settings) } stage3.steps.push(newStep) }) }) }) if(stage1.steps.length > 0){ stages.push(stage1) } if(stage2.steps.length > 0){ stages.push(stage2) } if(stage3.steps.length > 0){ stages.push(stage3) } //stage 4 is to update the bin with the new mode let stage4: Stage = { title: "Update Bin With New Mode", steps: [ { title: "Update " + bin.name(), promise: () => { let clone = cloneDeep(bin.settings) clone.mode = newMode //this will set the bin to use the auto top nodes for cables when the bin is going into storage mode, and turn them off if the bin is being conditioned if(newMode === pond.BinMode.BIN_MODE_STORAGE){ clone.autoGrainNode = true }else{ clone.autoGrainNode = false } return binAPI.updateBin(bin.key(), clone) }, onComplete: () => modeUpdated?.(newMode) } ] } stages.push(stage4) //set those stages to a state variable setPromiseStages(stages) } /** * the progress step is the last step of each mode change that shows what is being done and what succeeded/failed * initially i was going to put it here because it would effectively be the same thing for each mode, however depending * on how i decide to handle the api calls, and since i am thinking about passing the functions into the children it may go into each mode component * */ const progressContent = () => { return ( Change Mode { //this is just a function that is rin as soo as the start button is clicked it can be used to disable things while the change is in progress if we want to }} onComplete={()=>{ closeDialog() }} /> ) } const modeContent = () => { switch(newMode){ case pond.BinMode.BIN_MODE_DRYING: //setNumSteps(DryingStepCount) return { buildStages(deviceData) setShowProgress(true) }} /> case pond.BinMode.BIN_MODE_HYDRATING: return case pond.BinMode.BIN_MODE_COOLDOWN: return default: return {}}/> } } return ( {showProgress ? progressContent() : modeContent()} ) }