frontend/src/bin/binModes/BinModeController.tsx

207 lines
No EOL
7.6 KiB
TypeScript

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<string, pond.BinComponentPreferences>
devices: Device[]
componentDevices: Map<string, number>
componentMap: Map<string, Component>
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<Map<number, Component[]>>(new Map())
const [promiseStages, setPromiseStages] = useState<Stage[]>([])
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<number, Component[]> = 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 (
<React.Fragment>
<DialogTitle>Change Mode</DialogTitle>
<DialogContent>
<PromiseProgress
stages={promiseStages}
description="These are the changes that will occur in order to change your bin mode with the given options. Press start to begin."
failFast
onStart={() => {
//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()
}}
/>
</DialogContent>
<DialogActions>
<Button onClick={closeDialog}>Close</Button>
</DialogActions>
</React.Fragment>
)
}
const modeContent = () => {
switch(newMode){
case pond.BinMode.BIN_MODE_DRYING:
//setNumSteps(DryingStepCount)
return <DryingMode
grain={bin.settings.inventory?.grainType}
devices={devices}
binPrefs={binPrefs}
deviceComponents={deviceComponents}
cancel={closeDialog}
confirm={(deviceData) => {
buildStages(deviceData)
setShowProgress(true)
}}
/>
case pond.BinMode.BIN_MODE_HYDRATING:
return <HydratingMode />
case pond.BinMode.BIN_MODE_COOLDOWN:
return <CooldownMode />
default:
return <StorageMode devices={devices} confirm={()=>{}}/>
}
}
return (
<ResponsiveDialog open={open} onClose={closeDialog}>
{showProgress ? progressContent() : modeContent()}
</ResponsiveDialog>
)
}