diff --git a/src/bin/binModes/BinModeController.tsx b/src/bin/binModes/BinModeController.tsx
index f4f825e..32dd6af 100644
--- a/src/bin/binModes/BinModeController.tsx
+++ b/src/bin/binModes/BinModeController.tsx
@@ -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
default:
- return {}}/>
+ return {
+ buildStages(deviceData)
+ setShowProgress(true)
+ }}/>
}
}
diff --git a/src/bin/binModes/DryingMode.tsx b/src/bin/binModes/DryingMode.tsx
index 40d071a..d0f7bb0 100644
--- a/src/bin/binModes/DryingMode.tsx
+++ b/src/bin/binModes/DryingMode.tsx
@@ -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([])
const [heaters, setHeaters] = useState([])
const [fans, setFans] = useState([])
+ const [cables, setCables] = useState([])
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 = 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 && }
{/* confirm - tells the parent to build the stages using the data, only visible on the last step */}
- {currentStep === steps.length - 1 && interactionsToAdd && selectedDevice && }
+ {currentStep === steps.length - 1 && interactionsToAdd && selectedDevice && }
)
}
diff --git a/src/bin/binModes/StorageMode.tsx b/src/bin/binModes/StorageMode.tsx
index 70c4f72..e9a0926 100644
--- a/src/bin/binModes/StorageMode.tsx
+++ b/src/bin/binModes/StorageMode.tsx
@@ -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
+ bin: Bin
binPrefs?: Map
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([])
+ const [loading, setLoading] = useState(false)
+ const [loadErr, setLoadErr] = useState(false)
useEffect(()=>{
let interactionPromises: Promise[] = []
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()
+
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 (
Storage Mode
{/* could have the explanation of what changing to storage mode does here */}
-
+
+
+
)
}
\ No newline at end of file
diff --git a/src/models/Ambient.ts b/src/models/Ambient.ts
index d2c7577..4fde1ea 100644
--- a/src/models/Ambient.ts
+++ b/src/models/Ambient.ts
@@ -108,18 +108,25 @@ export class Ambient {
return quack.ComponentID.fromObject({
type: this.settings.type,
addressType: this.settings.addressType,
- address: this.settings.address
+ address: this.settings.address,
+ expansionLine: this.settings.expansionLine,
+ muxLine: this.settings.muxLine
});
}
public locationString(): string {
- return (
- or(this.settings.type, 0).toString() +
+ let compositeLocation = or(this.settings.type, 0).toString() +
"-" +
or(this.settings.addressType, 0).toString() +
"-" +
or(this.settings.address, 0).toString()
- );
+ if(this.settings.expansionLine){
+ compositeLocation = compositeLocation + "-" + this.settings.expansionLine
+ }
+ if(this.settings.muxLine){
+ compositeLocation = compositeLocation + ":" + this.settings.muxLine
+ }
+ return compositeLocation;
}
public type(): quack.ComponentType {
diff --git a/src/models/Controller.ts b/src/models/Controller.ts
index 4e7170c..c29ba52 100644
--- a/src/models/Controller.ts
+++ b/src/models/Controller.ts
@@ -82,18 +82,25 @@ export class Controller {
return quack.ComponentID.fromObject({
type: this.settings.type,
addressType: this.settings.addressType,
- address: this.settings.address
+ address: this.settings.address,
+ expansionLine: this.settings.expansionLine,
+ muxLine: this.settings.muxLine
});
}
public locationString(): string {
- return (
- or(this.settings.type, 0).toString() +
+ let compositeLocation = or(this.settings.type, 0).toString() +
"-" +
or(this.settings.addressType, 0).toString() +
"-" +
or(this.settings.address, 0).toString()
- );
+ if(this.settings.expansionLine){
+ compositeLocation = compositeLocation + "-" + this.settings.expansionLine
+ }
+ if(this.settings.muxLine){
+ compositeLocation = compositeLocation + ":" + this.settings.muxLine
+ }
+ return compositeLocation;
}
public type(): quack.ComponentType {
diff --git a/src/models/GrainCable.ts b/src/models/GrainCable.ts
index 4de1ae4..e3e988b 100644
--- a/src/models/GrainCable.ts
+++ b/src/models/GrainCable.ts
@@ -230,18 +230,25 @@ export class GrainCable {
return quack.ComponentID.fromObject({
type: this.settings.type,
addressType: this.settings.addressType,
- address: this.settings.address
+ address: this.settings.address,
+ expansionLine: this.settings.expansionLine,
+ muxLine: this.settings.muxLine
});
}
public locationString(): string {
- return (
- or(this.settings.type, 0).toString() +
+ let compositeLocation = or(this.settings.type, 0).toString() +
"-" +
or(this.settings.addressType, 0).toString() +
"-" +
or(this.settings.address, 0).toString()
- );
+ if(this.settings.expansionLine){
+ compositeLocation = compositeLocation + "-" + this.settings.expansionLine
+ }
+ if(this.settings.muxLine){
+ compositeLocation = compositeLocation + ":" + this.settings.muxLine
+ }
+ return compositeLocation;
}
public type(): quack.ComponentType {
diff --git a/src/models/Plenum.ts b/src/models/Plenum.ts
index 5ec161f..15f99bc 100644
--- a/src/models/Plenum.ts
+++ b/src/models/Plenum.ts
@@ -115,13 +115,18 @@ export class Plenum {
}
public locationString(): string {
- return (
- or(this.settings.type, 0).toString() +
+ let compositeLocation = or(this.settings.type, 0).toString() +
"-" +
or(this.settings.addressType, 0).toString() +
"-" +
or(this.settings.address, 0).toString()
- );
+ if(this.settings.expansionLine){
+ compositeLocation = compositeLocation + "-" + this.settings.expansionLine
+ }
+ if(this.settings.muxLine){
+ compositeLocation = compositeLocation + ":" + this.settings.muxLine
+ }
+ return compositeLocation;
}
public type(): quack.ComponentType {