built out the functions to create the interactions
This commit is contained in:
parent
393a2f77dc
commit
e70c971a88
4 changed files with 291 additions and 3 deletions
|
|
@ -485,7 +485,6 @@ export default function BinSettings(props: Props) {
|
|||
form.inventory.inventoryControl = inventoryControl
|
||||
form.inventory.autoThreshold = autoFillThreshold
|
||||
}
|
||||
console.log(form)
|
||||
binAPI
|
||||
.updateBin(bin.key(), form, as)
|
||||
.then(response => {
|
||||
|
|
|
|||
|
|
@ -2194,6 +2194,7 @@ export default function BinVisualizer(props: Props) {
|
|||
<ModeChangeDialog
|
||||
binKey={bin.key()}
|
||||
binMode={newBinMode}
|
||||
grain={bin.settings.inventory?.grainType}
|
||||
devices={devices}
|
||||
open={openModeChange}
|
||||
binCables={cables}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,12 @@ 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 { getTemperatureUnit } from "utils";
|
||||
import { GetGrainExtensionMap } from "grain";
|
||||
|
||||
interface Props {
|
||||
open: boolean
|
||||
|
|
@ -44,7 +50,8 @@ export default function ModeChangeDialog(props: Props){
|
|||
binKey,
|
||||
preferences,
|
||||
binCables,
|
||||
compDevMap
|
||||
compDevMap,
|
||||
grain
|
||||
} = props
|
||||
const [{as}] = useGlobalState()
|
||||
const [componentSets, setComponentSets] = useState<ComponentSet[]>([])
|
||||
|
|
@ -62,6 +69,9 @@ export default function ModeChangeDialog(props: Props){
|
|||
const [ambients, setAmbients] = useState<Ambient[]>([])
|
||||
const [heaters, setHeaters] = useState<Controller[]>([])
|
||||
const [fans, setFans] = useState<Controller[]>([])
|
||||
const [selectedPreset, setSelectedPreset] = useState<DevicePreset | undefined>(undefined);
|
||||
const grainExtensionMap = GetGrainExtensionMap();
|
||||
|
||||
//get the interactions and components for the device when it is selected from the dropdown
|
||||
useEffect(()=>{
|
||||
if (!binKey) return;
|
||||
|
|
@ -85,6 +95,7 @@ export default function ModeChangeDialog(props: Props){
|
|||
newDComponents.push(c);
|
||||
dComponents.set(deviceNumber.toString(), newDComponents);
|
||||
});
|
||||
console.log(dComponents)
|
||||
setDeviceComponents(dComponents);
|
||||
setExistingInteractions(interactionResp);
|
||||
})
|
||||
|
|
@ -141,9 +152,266 @@ if (!selectedDevice) return;
|
|||
setOptions(o);
|
||||
}, [devices, setOptions]);
|
||||
|
||||
const buildHeaterInteraction = (
|
||||
sensor: Plenum | Ambient,
|
||||
heater: Controller
|
||||
): pond.InteractionSettings => {
|
||||
let interaction = pond.InteractionSettings.create({
|
||||
source: sensor.location(),
|
||||
sink: heater.location(),
|
||||
schedule: pond.InteractionSchedule.create({
|
||||
timeOfDayStart: "00:00",
|
||||
timeOfDayEnd: "24:00",
|
||||
timezone: moment.tz.guess(),
|
||||
weekdays: moment.weekdays().map(d => lowerCase(d))
|
||||
}),
|
||||
notifications: pond.InteractionNotifications.create({
|
||||
reports: true
|
||||
}),
|
||||
result: pond.InteractionResult.create({
|
||||
type: quack.InteractionResultType.INTERACTION_RESULT_TYPE_TOGGLE,
|
||||
value: 1
|
||||
})
|
||||
});
|
||||
|
||||
let temp = 0;
|
||||
let hum = 0;
|
||||
|
||||
//if a preset was selected use its temp/hum values
|
||||
if (selectedPreset !== undefined) {
|
||||
temp = selectedPreset.temp();
|
||||
hum = selectedPreset.hum();
|
||||
} else {
|
||||
//otherwise use default values
|
||||
switch(binMode){
|
||||
case pond.BinMode.BIN_MODE_COOLDOWN:
|
||||
temp = 10
|
||||
hum = 45
|
||||
break;
|
||||
case pond.BinMode.BIN_MODE_DRYING:
|
||||
if(grain){
|
||||
//get the values using the grain type
|
||||
let ext = grainExtensionMap.get(grain)
|
||||
if (ext) {
|
||||
temp = ext.setTempC
|
||||
hum = ext.targetMC
|
||||
}
|
||||
}else{//otherwise use the default drying interaction
|
||||
temp = 40
|
||||
hum = 20
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let conditions = [];
|
||||
let humidityCondition = pond.InteractionCondition.create({
|
||||
measurementType: quack.MeasurementType.MEASUREMENT_TYPE_PERCENT,
|
||||
comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN,
|
||||
value: Math.round(
|
||||
describeMeasurement(
|
||||
quack.MeasurementType.MEASUREMENT_TYPE_PERCENT,
|
||||
sensor.settings.type,
|
||||
sensor.settings.subtype
|
||||
).toStored(hum)
|
||||
)
|
||||
});
|
||||
conditions.push(humidityCondition);
|
||||
|
||||
//since the measurement describers function to stored does a conversion into celsius if the users pref is fahrenheit for temperature we need to convert the preset value into fahrenheit
|
||||
if (getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
|
||||
temp = Math.round((temp * (9 / 5) + 32) * 100) / 100;
|
||||
}
|
||||
let tempVal = describeMeasurement(
|
||||
quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE,
|
||||
sensor.settings.type,
|
||||
sensor.settings.subtype
|
||||
).toStored(temp);
|
||||
|
||||
let tempCondition = pond.InteractionCondition.create({
|
||||
measurementType: quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE,
|
||||
comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_LESS_THAN,
|
||||
value: Math.round(tempVal) //and then round the converted value since interaction conditions wont take floats
|
||||
});
|
||||
conditions.push(tempCondition);
|
||||
interaction.conditions = conditions;
|
||||
|
||||
return interaction;
|
||||
};
|
||||
|
||||
const buildFanInteraction = (
|
||||
sensor: Plenum | Ambient,
|
||||
fan: Controller,
|
||||
tempComparison: "greater" | "less",
|
||||
humidityComparison: "greater" | "less"
|
||||
) => {
|
||||
let interaction = pond.InteractionSettings.create({
|
||||
source: sensor.location(),
|
||||
sink: fan.location(),
|
||||
schedule: pond.InteractionSchedule.create({
|
||||
timeOfDayStart: "00:00",
|
||||
timeOfDayEnd: "24:00",
|
||||
timezone: moment.tz.guess(),
|
||||
weekdays: moment.weekdays().map(d => lowerCase(d))
|
||||
}),
|
||||
notifications: pond.InteractionNotifications.create({
|
||||
reports: true
|
||||
}),
|
||||
result: pond.InteractionResult.create({
|
||||
type: quack.InteractionResultType.INTERACTION_RESULT_TYPE_TOGGLE,
|
||||
value: 1
|
||||
})
|
||||
});
|
||||
|
||||
let tempPreset = 0;
|
||||
let humidityPreset = 0;
|
||||
|
||||
//if a custom preset was selected use it
|
||||
if (selectedPreset !== undefined) {
|
||||
tempPreset = selectedPreset.temp();
|
||||
humidityPreset = selectedPreset.hum();
|
||||
} else {
|
||||
//otherwise use one of the defaults
|
||||
switch (binMode) {
|
||||
case pond.BinMode.BIN_MODE_COOLDOWN:
|
||||
tempPreset = 15;
|
||||
humidityPreset = 60;
|
||||
break;
|
||||
case pond.BinMode.BIN_MODE_DRYING:
|
||||
if(grain){
|
||||
//get the values using the grain type
|
||||
let ext = grainExtensionMap.get(grain)
|
||||
if (ext) {
|
||||
tempPreset = ext.setTempC
|
||||
humidityPreset = ext.targetMC
|
||||
}
|
||||
}else{//otherwise use the default drying interaction
|
||||
tempPreset = 40
|
||||
humidityPreset = 20
|
||||
}
|
||||
break;
|
||||
case pond.BinMode.BIN_MODE_HYDRATING:
|
||||
tempPreset = 25;
|
||||
humidityPreset = 60;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let conditions = [];
|
||||
let fanConditionOne = pond.InteractionCondition.create({
|
||||
measurementType: quack.MeasurementType.MEASUREMENT_TYPE_PERCENT,
|
||||
comparison:
|
||||
humidityComparison === "greater"
|
||||
? quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN
|
||||
: quack.RelationalOperator.RELATIONAL_OPERATOR_LESS_THAN,
|
||||
value: describeMeasurement(
|
||||
quack.MeasurementType.MEASUREMENT_TYPE_PERCENT,
|
||||
sensor.settings.type,
|
||||
sensor.settings.subtype
|
||||
).toStored(humidityPreset)
|
||||
});
|
||||
conditions.push(fanConditionOne);
|
||||
|
||||
//since the measurement describers function toStored does a conversion into celsius for temperature if the users pref is fahrenheit we need to convert the preset value into fahrenheit
|
||||
if (getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
|
||||
tempPreset = Math.round((tempPreset * (9 / 5) + 32) * 100) / 100;
|
||||
}
|
||||
let tempVal = describeMeasurement(
|
||||
quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE,
|
||||
sensor.settings.type,
|
||||
sensor.settings.subtype
|
||||
).toStored(tempPreset);
|
||||
|
||||
let fanConditionTwo = pond.InteractionCondition.create({
|
||||
measurementType: quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE,
|
||||
comparison:
|
||||
tempComparison === "greater"
|
||||
? quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN
|
||||
: quack.RelationalOperator.RELATIONAL_OPERATOR_LESS_THAN,
|
||||
value: Math.round(tempVal) //and then round the converted value since the interaction does not take decimals
|
||||
});
|
||||
conditions.push(fanConditionTwo);
|
||||
|
||||
interaction.conditions = conditions;
|
||||
return interaction;
|
||||
};
|
||||
|
||||
|
||||
//function to loop through the interaction sets building the settings
|
||||
const createInteractions = () => {
|
||||
if(!selectedDevice) return //if there is no device that was selected, do nothing
|
||||
let multiInteractionSettings: pond.MultiInteractionSettings = pond.MultiInteractionSettings.create()
|
||||
let linkedComponents = deviceComponents.get(selectedDevice.id().toString());
|
||||
//loop through the sets
|
||||
componentSets.forEach(async (set) => {
|
||||
//loop through the controllers
|
||||
set.controllers.forEach(controller => {
|
||||
//filter the conflicting interactions for that controller
|
||||
let conflictingInteractions = existingInteractions.filter(i => {
|
||||
let conflicting = false;
|
||||
if (linkedComponents) {
|
||||
linkedComponents.forEach(comp => {
|
||||
if (sameComponentID(comp.location(), i.settings.sink)) {
|
||||
conflicting = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
return conflicting;
|
||||
});
|
||||
//create an array of promises to delete them
|
||||
let deleteConflictingInteractions = conflictingInteractions.map(i => {
|
||||
return interactionAPI.removeInteraction(selectedDevice.id(), i.key(), as);
|
||||
});
|
||||
//execute the promises
|
||||
Promise.all([...deleteConflictingInteractions]).finally(() => {
|
||||
//and one they are done make the new interaction to be added for the sensor and controller pair
|
||||
switch(binMode){
|
||||
case pond.BinMode.BIN_MODE_COOLDOWN:
|
||||
// if the controller is a heater create heater interaction
|
||||
if(controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_HEATER){
|
||||
multiInteractionSettings.interactions.push(buildHeaterInteraction(set.sensor, controller))
|
||||
}else if(controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_AERATION_FAN ||
|
||||
controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_EXHAUST_FAN){
|
||||
// if the controller is a fan create a fan interaction
|
||||
multiInteractionSettings.interactions.push(buildFanInteraction(set.sensor, controller, "less", "less"))
|
||||
}
|
||||
break;
|
||||
case pond.BinMode.BIN_MODE_DRYING:
|
||||
// if the controller is a heater create heater interaction
|
||||
if(controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_HEATER){
|
||||
multiInteractionSettings.interactions.push(buildHeaterInteraction(set.sensor, controller))
|
||||
}else if(controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_AERATION_FAN ||
|
||||
controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_EXHAUST_FAN){// if the controller is a fan
|
||||
//if it is a combination set using a heater as well just turn the fan on
|
||||
if(set.combo){
|
||||
controller.settings.defaultOutputState = quack.OutputMode.OUTPUT_MODE_ON
|
||||
}else{
|
||||
//otherwise make a fan interaction
|
||||
multiInteractionSettings.interactions.push(buildFanInteraction(set.sensor, controller, "greater", "less"))
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
case pond.BinMode.BIN_MODE_HYDRATING:
|
||||
//check to make sure the controller is a fan, you cant hydrate with a heater, if the controller is a heater then it will not add an interaction
|
||||
if(controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_AERATION_FAN ||
|
||||
controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_EXHAUST_FAN){
|
||||
//create fan interaction
|
||||
multiInteractionSettings.interactions.push(buildFanInteraction(set.sensor, controller, "less", "greater"))
|
||||
}
|
||||
break;
|
||||
}
|
||||
console.log(multiInteractionSettings)
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
//add all of the interactions that were created (multiInteraction)
|
||||
interactionAPI.addMultiInteractions(selectedDevice.id(), multiInteractionSettings).then(resp => {
|
||||
//then once the interactions are added update the controllers to the proper states
|
||||
|
||||
}).catch(err => {
|
||||
//there was a problem adding the interactions
|
||||
})
|
||||
}
|
||||
|
||||
const deviceSelector = () => {
|
||||
|
|
@ -246,6 +514,7 @@ if (!selectedDevice) return;
|
|||
updateSets={(sets) => {
|
||||
//update the component sets that will be used for the interactions
|
||||
console.log(sets)
|
||||
setComponentSets(sets)
|
||||
}}/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
|
|
@ -255,8 +524,8 @@ if (!selectedDevice) return;
|
|||
Cancel
|
||||
</Button>
|
||||
<Button variant="contained" color="primary" onClick={()=>{
|
||||
//remove comflicting interactions
|
||||
//add new interactions to the component sets
|
||||
createInteractions()
|
||||
close(true)
|
||||
}}>
|
||||
Submit
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@ import { cloneDeep } from "lodash";
|
|||
import { Ambient } from "models/Ambient";
|
||||
import { Controller } from "models/Controller";
|
||||
import { Plenum } from "models/Plenum";
|
||||
import { quack } from "protobuf-ts/quack";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
export interface ComponentSet {
|
||||
sensor: Plenum | Ambient,
|
||||
controllers: Controller[]
|
||||
combo?: boolean //if the set contains both a heater and a fan
|
||||
}
|
||||
|
||||
interface Props {
|
||||
|
|
@ -34,6 +36,21 @@ export default function PickComponentSet(props: Props) {
|
|||
return index
|
||||
}
|
||||
|
||||
const comboCheck = (set: ComponentSet) => {
|
||||
let heaterFound = false
|
||||
let fanFound = false
|
||||
set.controllers.forEach(controller => {
|
||||
if(controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_HEATER){
|
||||
heaterFound = true
|
||||
}
|
||||
if(controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_AERATION_FAN ||
|
||||
controller.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_EXHAUST_FAN){
|
||||
fanFound = true
|
||||
}
|
||||
})
|
||||
return heaterFound && fanFound
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<FormControlLabel
|
||||
|
|
@ -79,6 +96,7 @@ export default function PickComponentSet(props: Props) {
|
|||
set.controllers.splice(index, 1)
|
||||
}
|
||||
}
|
||||
set.combo = comboCheck(set)
|
||||
setCurrentSet(set)
|
||||
updateSet(sensor.key(), set)
|
||||
}}
|
||||
|
|
@ -107,6 +125,7 @@ export default function PickComponentSet(props: Props) {
|
|||
set.controllers.splice(index, 1)
|
||||
}
|
||||
}
|
||||
set.combo = comboCheck(set)
|
||||
setCurrentSet(set)
|
||||
updateSet(sensor.key(), set)
|
||||
}}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue