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.inventoryControl = inventoryControl
|
||||||
form.inventory.autoThreshold = autoFillThreshold
|
form.inventory.autoThreshold = autoFillThreshold
|
||||||
}
|
}
|
||||||
console.log(form)
|
|
||||||
binAPI
|
binAPI
|
||||||
.updateBin(bin.key(), form, as)
|
.updateBin(bin.key(), form, as)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
|
|
|
||||||
|
|
@ -2194,6 +2194,7 @@ export default function BinVisualizer(props: Props) {
|
||||||
<ModeChangeDialog
|
<ModeChangeDialog
|
||||||
binKey={bin.key()}
|
binKey={bin.key()}
|
||||||
binMode={newBinMode}
|
binMode={newBinMode}
|
||||||
|
grain={bin.settings.inventory?.grainType}
|
||||||
devices={devices}
|
devices={devices}
|
||||||
open={openModeChange}
|
open={openModeChange}
|
||||||
binCables={cables}
|
binCables={cables}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,12 @@ import { useEffect, useState } from "react";
|
||||||
import ConditioningSelector from "./conditioningSelector";
|
import ConditioningSelector from "./conditioningSelector";
|
||||||
import CableTopNodeSummary from "bin/CableTopNodeSummary";
|
import CableTopNodeSummary from "bin/CableTopNodeSummary";
|
||||||
import { ComponentSet } from "./pickComponentSet";
|
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 {
|
interface Props {
|
||||||
open: boolean
|
open: boolean
|
||||||
|
|
@ -44,7 +50,8 @@ export default function ModeChangeDialog(props: Props){
|
||||||
binKey,
|
binKey,
|
||||||
preferences,
|
preferences,
|
||||||
binCables,
|
binCables,
|
||||||
compDevMap
|
compDevMap,
|
||||||
|
grain
|
||||||
} = props
|
} = props
|
||||||
const [{as}] = useGlobalState()
|
const [{as}] = useGlobalState()
|
||||||
const [componentSets, setComponentSets] = useState<ComponentSet[]>([])
|
const [componentSets, setComponentSets] = useState<ComponentSet[]>([])
|
||||||
|
|
@ -62,6 +69,9 @@ export default function ModeChangeDialog(props: Props){
|
||||||
const [ambients, setAmbients] = useState<Ambient[]>([])
|
const [ambients, setAmbients] = useState<Ambient[]>([])
|
||||||
const [heaters, setHeaters] = useState<Controller[]>([])
|
const [heaters, setHeaters] = useState<Controller[]>([])
|
||||||
const [fans, setFans] = 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
|
//get the interactions and components for the device when it is selected from the dropdown
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
if (!binKey) return;
|
if (!binKey) return;
|
||||||
|
|
@ -85,6 +95,7 @@ export default function ModeChangeDialog(props: Props){
|
||||||
newDComponents.push(c);
|
newDComponents.push(c);
|
||||||
dComponents.set(deviceNumber.toString(), newDComponents);
|
dComponents.set(deviceNumber.toString(), newDComponents);
|
||||||
});
|
});
|
||||||
|
console.log(dComponents)
|
||||||
setDeviceComponents(dComponents);
|
setDeviceComponents(dComponents);
|
||||||
setExistingInteractions(interactionResp);
|
setExistingInteractions(interactionResp);
|
||||||
})
|
})
|
||||||
|
|
@ -141,9 +152,266 @@ if (!selectedDevice) return;
|
||||||
setOptions(o);
|
setOptions(o);
|
||||||
}, [devices, setOptions]);
|
}, [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
|
//function to loop through the interaction sets building the settings
|
||||||
const createInteractions = () => {
|
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 = () => {
|
const deviceSelector = () => {
|
||||||
|
|
@ -246,6 +514,7 @@ if (!selectedDevice) return;
|
||||||
updateSets={(sets) => {
|
updateSets={(sets) => {
|
||||||
//update the component sets that will be used for the interactions
|
//update the component sets that will be used for the interactions
|
||||||
console.log(sets)
|
console.log(sets)
|
||||||
|
setComponentSets(sets)
|
||||||
}}/>
|
}}/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
|
|
@ -255,8 +524,8 @@ if (!selectedDevice) return;
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="contained" color="primary" onClick={()=>{
|
<Button variant="contained" color="primary" onClick={()=>{
|
||||||
//remove comflicting interactions
|
|
||||||
//add new interactions to the component sets
|
//add new interactions to the component sets
|
||||||
|
createInteractions()
|
||||||
close(true)
|
close(true)
|
||||||
}}>
|
}}>
|
||||||
Submit
|
Submit
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,13 @@ import { cloneDeep } from "lodash";
|
||||||
import { Ambient } from "models/Ambient";
|
import { Ambient } from "models/Ambient";
|
||||||
import { Controller } from "models/Controller";
|
import { Controller } from "models/Controller";
|
||||||
import { Plenum } from "models/Plenum";
|
import { Plenum } from "models/Plenum";
|
||||||
|
import { quack } from "protobuf-ts/quack";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
export interface ComponentSet {
|
export interface ComponentSet {
|
||||||
sensor: Plenum | Ambient,
|
sensor: Plenum | Ambient,
|
||||||
controllers: Controller[]
|
controllers: Controller[]
|
||||||
|
combo?: boolean //if the set contains both a heater and a fan
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
@ -34,6 +36,21 @@ export default function PickComponentSet(props: Props) {
|
||||||
return index
|
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 (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
|
|
@ -79,6 +96,7 @@ export default function PickComponentSet(props: Props) {
|
||||||
set.controllers.splice(index, 1)
|
set.controllers.splice(index, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
set.combo = comboCheck(set)
|
||||||
setCurrentSet(set)
|
setCurrentSet(set)
|
||||||
updateSet(sensor.key(), set)
|
updateSet(sensor.key(), set)
|
||||||
}}
|
}}
|
||||||
|
|
@ -107,6 +125,7 @@ export default function PickComponentSet(props: Props) {
|
||||||
set.controllers.splice(index, 1)
|
set.controllers.splice(index, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
set.combo = comboCheck(set)
|
||||||
setCurrentSet(set)
|
setCurrentSet(set)
|
||||||
updateSet(sensor.key(), set)
|
updateSet(sensor.key(), set)
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue