finished the functional part of storage as well as modified the drying dialog to remove notification interactions from the cables
This commit is contained in:
parent
2c6799bcc1
commit
208a657528
7 changed files with 193 additions and 47 deletions
|
|
@ -11,12 +11,25 @@ import { PromiseProgress, Stage, Step } from "common/PromiseProgress";
|
|||
import { useBinAPI, useComponentAPI, useInteractionsAPI } from "providers";
|
||||
import { ComponentSet } from "bin/conditioning/pickComponentSet";
|
||||
import { cloneDeep } from "lodash";
|
||||
import { Controller } from "models/Controller";
|
||||
|
||||
export interface DeviceChangeData {
|
||||
/**
|
||||
* the id of the device changes are being made to
|
||||
*/
|
||||
deviceId: number
|
||||
/**
|
||||
* the array of interactions to remove from the device
|
||||
*/
|
||||
toRemove: Interaction[]
|
||||
/**
|
||||
* the new interactions to add to the device
|
||||
*/
|
||||
toAdd?: pond.MultiInteractionSettings
|
||||
componentSets: ComponentSet[]
|
||||
/**
|
||||
* the controllers that need to have there state updated
|
||||
*/
|
||||
controllers: Controller[]
|
||||
}
|
||||
|
||||
interface Props {
|
||||
|
|
@ -99,16 +112,16 @@ export default function BinModeController(props: Props) {
|
|||
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)
|
||||
})
|
||||
|
||||
data.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)
|
||||
}
|
||||
|
|
@ -195,7 +208,15 @@ export default function BinModeController(props: Props) {
|
|||
case pond.BinMode.BIN_MODE_COOLDOWN:
|
||||
return <CooldownMode />
|
||||
default:
|
||||
return <StorageMode devices={devices} confirm={()=>{}}/>
|
||||
return <StorageMode
|
||||
devices={devices}
|
||||
binPrefs={binPrefs}
|
||||
bin={bin}
|
||||
deviceComponents={deviceComponents}
|
||||
confirm={(deviceData)=>{
|
||||
buildStages(deviceData)
|
||||
setShowProgress(true)
|
||||
}}/>
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import BinConditioningInteraction from "bin/BinConditioningInteraction";
|
|||
import ConditionDisplay from "./conditionDisplay";
|
||||
import React from "react";
|
||||
import { DeviceChangeData } from "./BinModeController";
|
||||
import { GrainCable } from "models/GrainCable";
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => {
|
||||
|
|
@ -66,6 +67,7 @@ export default function DryingMode(props: Props){
|
|||
const [ambients, setAmbients] = useState<Ambient[]>([])
|
||||
const [heaters, setHeaters] = useState<Controller[]>([])
|
||||
const [fans, setFans] = useState<Controller[]>([])
|
||||
const [cables, setCables] = useState<GrainCable[]>([])
|
||||
const [currentStep, setCurrentStep] = useState(0)
|
||||
const [interactionLoading,setInteractionsLoading] = useState(false)
|
||||
|
||||
|
|
@ -87,7 +89,7 @@ export default function DryingMode(props: Props){
|
|||
if (!deviceComponents.get(selectedDevice.id())) return;
|
||||
var plenums: Plenum[] = [];
|
||||
var ambients: Ambient[] = [];
|
||||
//var grainCables: GrainCable[] = [];
|
||||
var grainCables: GrainCable[] = [];
|
||||
var heaters: Controller[] = [];
|
||||
var fans: Controller[] = [];
|
||||
var sinkMap: Map<string, Component> = new Map()
|
||||
|
|
@ -105,8 +107,10 @@ export default function DryingMode(props: Props){
|
|||
ambients.push(Ambient.create(comp));
|
||||
sourceMap.set(comp.locationString(), comp)
|
||||
}
|
||||
// if (pref.type === pond.BinComponent.BIN_COMPONENT_GRAIN_CABLE)
|
||||
// grainCables.push(GrainCable.create(comp));
|
||||
if (pref.type === pond.BinComponent.BIN_COMPONENT_GRAIN_CABLE){
|
||||
grainCables.push(GrainCable.create(comp));
|
||||
sourceMap.set(comp.locationString(), comp)
|
||||
}
|
||||
if (pref.type === pond.BinComponent.BIN_COMPONENT_HEATER) {
|
||||
let heater = Controller.create(comp);
|
||||
heater.settings.defaultOutputState = quack.OutputMode.OUTPUT_MODE_OFF;
|
||||
|
|
@ -128,7 +132,7 @@ export default function DryingMode(props: Props){
|
|||
setFans(fans);
|
||||
setSourceMap(sourceMap)
|
||||
setSinkMap(sinkMap)
|
||||
|
||||
setCables(grainCables)
|
||||
//also load the interactions for the selected device so that we can find any conflicting ones
|
||||
setInteractionsLoading(true)
|
||||
interactionAPI.listInteractionsByDevice(selectedDevice.id()).then(resp => {
|
||||
|
|
@ -315,11 +319,12 @@ export default function DryingMode(props: Props){
|
|||
|
||||
//this function just takes in the sets as they change and creates the list of conflicting interactions and the settings to create the new ones and returns them
|
||||
const createInteractions = (sets: ComponentSet[]) => {
|
||||
console.log("create interactions")
|
||||
if(!selectedDevice) return undefined //if there is no device that was selected, do nothing
|
||||
let multiInteractionSettings: pond.MultiInteractionSettings = pond.MultiInteractionSettings.create()
|
||||
let conflictingInteractions: Interaction[] = []
|
||||
let linkedComponents = deviceComponents.get(selectedDevice.id());
|
||||
//loop through the sets
|
||||
//loop through the sets to find interactions to remove as well as set the new ones
|
||||
sets.forEach((set) => {
|
||||
//loop through the controllers
|
||||
set.controllers.forEach(controller => {
|
||||
|
|
@ -335,6 +340,7 @@ export default function DryingMode(props: Props){
|
|||
}
|
||||
return conflicting;
|
||||
});
|
||||
|
||||
conflictingInteractions = conflictingInteractions.concat(c)
|
||||
// if the controller is a heater create heater interaction
|
||||
if(controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_HEATER){
|
||||
|
|
@ -351,6 +357,17 @@ export default function DryingMode(props: Props){
|
|||
}
|
||||
})
|
||||
})
|
||||
console.log(cables)
|
||||
cables.forEach(cable => {
|
||||
console.log(cable.name())
|
||||
let c = existingInteractions.filter(i => {
|
||||
if (sameComponentID(cable.location(), i.settings.source) && !i.settings.sink && i.settings.notifications?.notify) {
|
||||
return true
|
||||
}
|
||||
});
|
||||
conflictingInteractions = conflictingInteractions.concat(c)
|
||||
})
|
||||
|
||||
setComponentSets([...sets])
|
||||
return {
|
||||
conflicting: conflictingInteractions,
|
||||
|
|
@ -497,7 +514,7 @@ export default function DryingMode(props: Props){
|
|||
{/* next - goes to the next step, hidden on the last step */}
|
||||
{currentStep !== steps.length - 1 && <Button onClick={() => {setCurrentStep(currentStep+1)}}>Next</Button>}
|
||||
{/* confirm - tells the parent to build the stages using the data, only visible on the last step */}
|
||||
{currentStep === steps.length - 1 && interactionsToAdd && selectedDevice && <Button onClick={() => {confirm([{ toRemove: conflictingInteractions, deviceId: selectedDevice.id(), toAdd: interactionsToAdd, componentSets}])}}>Confirm</Button>}
|
||||
{currentStep === steps.length - 1 && interactionsToAdd && selectedDevice && <Button onClick={() => {confirm([{ toRemove: conflictingInteractions, deviceId: selectedDevice.id(), toAdd: interactionsToAdd, controllers: componentSets.flatMap(set => set.controllers)}])}}>Confirm</Button>}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { DialogContent, DialogTitle, DialogActions } from "@mui/material";
|
||||
import { Component, Device, Interaction } from "models";
|
||||
import { DialogContent, DialogTitle, DialogActions, Button } from "@mui/material";
|
||||
import { Bin, Component, Device, Interaction } from "models";
|
||||
import { Ambient } from "models/Ambient";
|
||||
import { Controller } from "models/Controller";
|
||||
import { GrainCable } from "models/GrainCable";
|
||||
|
|
@ -10,15 +10,16 @@ import React, { useEffect, useState } from "react";
|
|||
import { DeviceChangeData } from "./BinModeController";
|
||||
import { useInteractionsAPI } from "providers";
|
||||
import { AxiosResponse } from "axios";
|
||||
|
||||
interface InteractionToRemove {
|
||||
deviceID: number,
|
||||
}
|
||||
import { componentIDToString } from "pbHelpers/Component";
|
||||
import { ComponentSet } from "bin/conditioning/pickComponentSet";
|
||||
import moment from "moment";
|
||||
import { lowerCase } from "lodash";
|
||||
|
||||
interface Props {
|
||||
// the devices connected to the bin
|
||||
devices: Device[]
|
||||
deviceComponents: Map<number, Component[]>
|
||||
bin: Bin
|
||||
binPrefs?: Map<string, pond.BinComponentPreferences>
|
||||
confirm: (deviceData: DeviceChangeData[]) => void
|
||||
}
|
||||
|
|
@ -29,13 +30,17 @@ interface Props {
|
|||
* @returns
|
||||
*/
|
||||
export default function StorageMode(props: Props){
|
||||
const {devices, deviceComponents, binPrefs} = props
|
||||
const {devices, deviceComponents, bin, binPrefs, confirm} = props
|
||||
const interactionsAPI = useInteractionsAPI()
|
||||
const [data, setData] = useState<DeviceChangeData[]>([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [loadErr, setLoadErr] = useState(false)
|
||||
|
||||
useEffect(()=>{
|
||||
let interactionPromises: Promise<Interaction[]>[] = []
|
||||
let deviceContext: {device: Device, plenums: Plenum[], ambients: Ambient[], cables: GrainCable[], controllers: Controller[]}[] = []
|
||||
devices.forEach(device => {
|
||||
if (!deviceComponents.get(device.id())) return;
|
||||
let plenums: Plenum[] = []
|
||||
let ambients: Ambient[] = []
|
||||
let cables: GrainCable[] = [] //since this mode is going to add notification interactions to cables they should be filtered as well
|
||||
|
|
@ -70,29 +75,106 @@ export default function StorageMode(props: Props){
|
|||
deviceContext.push({device, plenums, ambients, cables, controllers})
|
||||
interactionPromises.push(interactionsAPI.listInteractionsByDevice(device.id()))
|
||||
})
|
||||
|
||||
setLoading(true)
|
||||
let deviceChangeData: DeviceChangeData[] = []
|
||||
Promise.all(interactionPromises).then(resp => {
|
||||
//the index of this should match the device index in the devices array
|
||||
resp.forEach((interactions,i) => {
|
||||
let toRemove: Interaction[] = []
|
||||
let ctx = deviceContext[i]
|
||||
let sensorsAddr = ctx.plenums.concat(ctx.ambients).map(sensor => sensor.locationString())
|
||||
let controllerAddr = ctx.controllers.map(cont => cont.locationString())
|
||||
let cableAddr = ctx.cables.map(cab => cab.locationString())
|
||||
let cablesWithInteraction = new Set<string>()
|
||||
|
||||
interactions.forEach(interaction => {
|
||||
|
||||
//first check if the interaction has a sink in the devices fans or heaters and is being controlled by a plenum or ambient
|
||||
//this should make it so that it only removes conditioning interactions and not all interactions on the device
|
||||
let source = interaction.settings.source
|
||||
let sink = interaction.settings.sink
|
||||
//this checks if the interaction is a 'conditioning' interaction that needs to be removed
|
||||
if(source && sink){
|
||||
if(controllerAddr.includes(componentIDToString(sink)) && sensorsAddr.includes(componentIDToString(source))){
|
||||
toRemove.push(interaction)
|
||||
}
|
||||
}
|
||||
//need to make sure that it is a notification only interaction and not a controlling one
|
||||
//it is possible to control things with cables and we want to leave those alone and not consider them notification interactions
|
||||
//the reason we check that there is no sink is because even control interactions can still send notifications
|
||||
//we want interactions that effectively ONLY send notifications
|
||||
if (source && !sink && cableAddr.includes(componentIDToString(source))) {
|
||||
//check if notifications is true
|
||||
if(interaction.settings.notifications?.notify){
|
||||
cablesWithInteraction.add(componentIDToString(source))
|
||||
}
|
||||
}
|
||||
})
|
||||
let cablesNeedingNotification = ctx.cables.filter(
|
||||
cable => !cablesWithInteraction.has(cable.locationString())
|
||||
)
|
||||
|
||||
let dd: DeviceChangeData = {
|
||||
deviceId: ctx.device.id(),
|
||||
toRemove: toRemove,
|
||||
controllers: ctx.controllers,
|
||||
|
||||
}
|
||||
|
||||
if(cablesNeedingNotification.length > 0){
|
||||
let toAdd: pond.MultiInteractionSettings = pond.MultiInteractionSettings.create()
|
||||
cablesNeedingNotification.forEach(cable => {
|
||||
//create the new interaction here
|
||||
let notification: pond.InteractionSettings = pond.InteractionSettings.create({
|
||||
source: cable.location(),
|
||||
schedule: pond.InteractionSchedule.create({
|
||||
timeOfDayStart: "00:00",
|
||||
timeOfDayEnd: "24:00",
|
||||
timezone: moment.tz.guess(),
|
||||
weekdays: moment.weekdays().map(d => lowerCase(d))
|
||||
}),
|
||||
conditions: [pond.InteractionCondition.create({
|
||||
measurementType: quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE,
|
||||
comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN,
|
||||
value: bin.settings.highTemp
|
||||
})],
|
||||
result: pond.InteractionResult.create({
|
||||
type: quack.InteractionResultType.INTERACTION_RESULT_TYPE_REPORT
|
||||
}),
|
||||
notifications: pond.InteractionNotifications.create({
|
||||
notify: true
|
||||
}),
|
||||
})
|
||||
|
||||
//push it to toAdd.interactions
|
||||
toAdd.interactions.push(notification)
|
||||
|
||||
})
|
||||
//add toAdd to dd
|
||||
dd.toAdd = toAdd
|
||||
}
|
||||
deviceChangeData.push(dd)
|
||||
})
|
||||
setData(deviceChangeData)
|
||||
}).catch(err => {
|
||||
//TODO: display a snackBar message saying there was an issue loading the data for one or more devices
|
||||
setData([])
|
||||
setLoadErr(true)
|
||||
}).finally(() => {
|
||||
setLoading(false)
|
||||
})
|
||||
|
||||
},[devices, binPrefs, deviceComponents])
|
||||
|
||||
//load the interactions for all of the bins connected devices
|
||||
|
||||
//
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<DialogTitle>Storage Mode</DialogTitle>
|
||||
<DialogContent>
|
||||
{/* could have the explanation of what changing to storage mode does here */}
|
||||
</DialogContent>
|
||||
<DialogActions></DialogActions>
|
||||
<DialogActions>
|
||||
<Button onClick={()=>{
|
||||
confirm(data)
|
||||
}} disabled={loading || loadErr}>Confirm</Button>
|
||||
</DialogActions>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue