Merge branch 'object_status' into staging_environment

This commit is contained in:
csawatzky 2026-01-22 16:12:44 -06:00
commit cc4d0d5736
7 changed files with 295 additions and 93 deletions

4
package-lock.json generated
View file

@ -42,7 +42,7 @@
"mui-tel-input": "^7.0.0", "mui-tel-input": "^7.0.0",
"notistack": "^3.0.1", "notistack": "^3.0.1",
"openweathermap-ts": "^1.2.10", "openweathermap-ts": "^1.2.10",
"protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#object_status", "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#staging",
"query-string": "^9.2.1", "query-string": "^9.2.1",
"react": "^18.3.1", "react": "^18.3.1",
"react-beautiful-dnd": "^13.1.1", "react-beautiful-dnd": "^13.1.1",
@ -10953,7 +10953,7 @@
}, },
"node_modules/protobuf-ts": { "node_modules/protobuf-ts": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#75f64f81d9b4ba7639d9978af37c361fca34c279", "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#c9ef7906fd97cda8ef4bd149ec4a796159a7c067",
"dependencies": { "dependencies": {
"protobufjs": "^6.8.8" "protobufjs": "^6.8.8"
} }

View file

@ -54,7 +54,7 @@
"mui-tel-input": "^7.0.0", "mui-tel-input": "^7.0.0",
"notistack": "^3.0.1", "notistack": "^3.0.1",
"openweathermap-ts": "^1.2.10", "openweathermap-ts": "^1.2.10",
"protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#object_status", "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#staging",
"query-string": "^9.2.1", "query-string": "^9.2.1",
"react": "^18.3.1", "react": "^18.3.1",
"react-beautiful-dnd": "^13.1.1", "react-beautiful-dnd": "^13.1.1",

View file

@ -1,4 +1,5 @@
import { Button, DialogActions, DialogContent, DialogTitle, Typography } from "@mui/material"; import { CheckBox } from "@mui/icons-material";
import { Button, Checkbox, DialogActions, DialogContent, DialogTitle, FormControl, FormControlLabel, FormLabel, InputAdornment, TextField, Typography } from "@mui/material";
import ResponsiveDialog from "common/ResponsiveDialog"; import ResponsiveDialog from "common/ResponsiveDialog";
import { Component, Device } from "models"; import { Component, Device } from "models";
import { Gate } from "models/Gate"; import { Gate } from "models/Gate";
@ -6,6 +7,7 @@ import moment from "moment";
import { pond, quack } from "protobuf-ts/pond"; import { pond, quack } from "protobuf-ts/pond";
import { useGlobalState, useInteractionsAPI, useSnackbar } from "providers"; import { useGlobalState, useInteractionsAPI, useSnackbar } from "providers";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { getPressureUnit } from "utils";
interface Props { interface Props {
open: boolean; open: boolean;
@ -37,6 +39,7 @@ const densityMap = new Map<number, number>([
export default function GateDeviceInteraction(props: Props) { export default function GateDeviceInteraction(props: Props) {
const { open, close, gate, compDevice, densityTemp } = props; const { open, close, gate, compDevice, densityTemp } = props;
const [{ user, as }] = useGlobalState(); const [{ user, as }] = useGlobalState();
const [device, setDevice] = useState<Device>(Device.create())
const [lowDelta, setLowDelta] = useState(0); const [lowDelta, setLowDelta] = useState(0);
const [highDelta, setHighDelta] = useState(0); const [highDelta, setHighDelta] = useState(0);
const [greenComponent, setGreenComponent] = useState<Component>(); const [greenComponent, setGreenComponent] = useState<Component>();
@ -44,7 +47,12 @@ export default function GateDeviceInteraction(props: Props) {
const [pressureComponent, setPressureComponent] = useState<Component>(); const [pressureComponent, setPressureComponent] = useState<Component>();
const interactionsAPI = useInteractionsAPI(); const interactionsAPI = useInteractionsAPI();
const [adding, setAdding] = useState(false); const [adding, setAdding] = useState(false);
//boolean to determine if a third condition should be put on the interaction for the red light, requires a V2 device running at least fromware version 2.1.9
const [useRedOffCondition, setUseRedOffCondition] = useState(false)
//if the pressure is under this value then the red light should be off
const [redThreshold, setRedThreshold] = useState("0")
const { openSnack } = useSnackbar(); const { openSnack } = useSnackbar();
const [pressureSource, setPressureSource] = useState<quack.ComponentID>(quack.ComponentID.create())
useEffect(() => { useEffect(() => {
//math to determine what the delta pressures to set will be //math to determine what the delta pressures to set will be
@ -95,46 +103,64 @@ export default function GateDeviceInteraction(props: Props) {
gate.preferences[component.key()] === pond.GateComponentType.GATE_COMPONENT_TYPE_PRESSURE gate.preferences[component.key()] === pond.GateComponentType.GATE_COMPONENT_TYPE_PRESSURE
) { ) {
setPressureComponent(component); setPressureComponent(component);
setPressureSource(
quack.ComponentID.create({
type: component.type(),
address: component.settings.address,
addressType: component.settings.addressType
})
)
} }
} }
}); });
if(compDevice.device){
setDevice(Device.create(compDevice.device))
}
}, [gate, densityTemp, user, compDevice]); }, [gate, densityTemp, user, compDevice]);
// useEffect(() => { const buttonDisabled = () => {
// //load current interactions for the device if (greenComponent === undefined) return true
// let deviceID = Device.any(compDevice.device).id(); if (redComponent === undefined) return true
// interactionsAPI.listInteractionsByDevice(deviceID).then(resp => { if (adding) return true
// setCurrentInteractions(resp); if (useRedOffCondition && isNaN(parseFloat(redThreshold))) return true
// }); return false
// }, [compDevice.device, interactionsAPI]); }
// const removeCurrentInteractions = () => {
// let deviceID = Device.any(compDevice.device).id();
// currentInteractions.forEach(interaction => {
// interactionsAPI.removeInteraction(deviceID, interaction.key());
// });
// };
const createInteractions = async () => { const createInteractions = async () => {
//the interactions to be made //the interactions to be made
//TOGGLE green ON when pressure within range of upper and lower
if ( if (
greenComponent !== undefined && greenComponent !== undefined &&
redComponent !== undefined && redComponent !== undefined &&
pressureComponent !== undefined pressureComponent !== undefined
) { ) {
let greenToggle: pond.InteractionSettings = pond.InteractionSettings.create({
sink: quack.ComponentID.create({ let lightInteractions: pond.InteractionSettings[] = []
//making variables for the parameters of the interactions
const redSink = quack.ComponentID.create({
type: redComponent.type(),
address: redComponent.settings.address,
addressType: redComponent.settings.addressType
})
const greenSink = quack.ComponentID.create({
type: greenComponent.type(), type: greenComponent.type(),
address: greenComponent.settings.address, address: greenComponent.settings.address,
addressType: greenComponent.settings.addressType addressType: greenComponent.settings.addressType
}), })
source: quack.ComponentID.create({
type: pressureComponent.type(), const lightSchedule = pond.InteractionSchedule.create({
address: pressureComponent.settings.address, timezone: moment.tz.guess(),
addressType: pressureComponent.settings.addressType weekdays: ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"],
}), timeOfDayStart: "00:00",
timeOfDayEnd: "24:00"
})
//TOGGLE green ON when pressure within range of upper and lower, this will always be the same regardless
lightInteractions.push(pond.InteractionSettings.create({
sink: greenSink,
source: pressureSource,
conditions: [ conditions: [
pond.InteractionCondition.create({ pond.InteractionCondition.create({
comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN, comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN,
@ -150,30 +176,49 @@ export default function GateDeviceInteraction(props: Props) {
nodeOne: 2, nodeOne: 2,
nodeTwo: 1, nodeTwo: 1,
subtype: 18, subtype: 18,
schedule: pond.InteractionSchedule.create({ schedule: lightSchedule,
timezone: moment.tz.guess(),
weekdays: ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"],
timeOfDayStart: "00:00",
timeOfDayEnd: "24:00"
}),
result: pond.InteractionResult.create({ result: pond.InteractionResult.create({
type: quack.InteractionResultType.INTERACTION_RESULT_TYPE_TOGGLE, type: quack.InteractionResultType.INTERACTION_RESULT_TYPE_TOGGLE,
value: 1 value: 1
}) })
}); }))
//TOGGLE red OFF when pressure within range of upper and lower //if they want the red light off when below a certain pressure, it will need 4 seperate SET interactions
let redToggle: pond.InteractionSettings = pond.InteractionSettings.create({ if(useRedOffCondition){
sink: quack.ComponentID.create({ //need to make sure the redthreshold is in pascals
type: redComponent.type(), let thresholdPascals = 0
address: redComponent.settings.address, if(!isNaN(parseFloat(redThreshold))){
addressType: redComponent.settings.addressType if(getPressureUnit() === pond.PressureUnit.PRESSURE_UNIT_KILOPASCALS){
}), thresholdPascals = parseFloat(redThreshold)*1000
source: quack.ComponentID.create({ }else if (getPressureUnit() === pond.PressureUnit.PRESSURE_UNIT_INCHES_OF_WATER){
type: pressureComponent.type(), thresholdPascals = parseFloat(redThreshold)*249
address: pressureComponent.settings.address, }
addressType: pressureComponent.settings.addressType }
}),
//SET red on when above high
lightInteractions.push(pond.InteractionSettings.create({
sink: redSink,
source: pressureSource,
conditions: [
pond.InteractionCondition.create({
comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_LESS_THAN,
value: -highDelta,
measurementType: quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE
})
],
nodeOne: 2,
nodeTwo: 1,
subtype: 18,
schedule: lightSchedule,
result: pond.InteractionResult.create({
type: quack.InteractionResultType.INTERACTION_RESULT_TYPE_SET,
value: 1
})
}))
//SET red off when below high and above low
lightInteractions.push(pond.InteractionSettings.create({
sink: redSink,
source: pressureSource,
conditions: [ conditions: [
pond.InteractionCondition.create({ pond.InteractionCondition.create({
comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN, comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN,
@ -189,22 +234,88 @@ export default function GateDeviceInteraction(props: Props) {
nodeOne: 2, nodeOne: 2,
nodeTwo: 1, nodeTwo: 1,
subtype: 18, subtype: 18,
schedule: pond.InteractionSchedule.create({ schedule: lightSchedule,
timezone: moment.tz.guess(), result: pond.InteractionResult.create({
weekdays: ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"], type: quack.InteractionResultType.INTERACTION_RESULT_TYPE_SET,
timeOfDayStart: "00:00", value: 0
timeOfDayEnd: "24:00" })
}))
//SET red on when below low and above threshold
lightInteractions.push(pond.InteractionSettings.create({
sink: redSink,
source: pressureSource,
conditions: [
pond.InteractionCondition.create({
comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN,
value: -lowDelta,
measurementType: quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE
}), }),
pond.InteractionCondition.create({
comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_LESS_THAN,
value: -thresholdPascals,
measurementType: quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE
}),
],
nodeOne: 2,
nodeTwo: 1,
subtype: 18,
schedule: lightSchedule,
result: pond.InteractionResult.create({
type: quack.InteractionResultType.INTERACTION_RESULT_TYPE_SET,
value: 1
})
}))
//SET red off when below threshold
lightInteractions.push(pond.InteractionSettings.create({
sink: redSink,
source: pressureSource,
conditions: [
pond.InteractionCondition.create({
comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_LESS_THAN,
value: thresholdPascals,
measurementType: quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE
}),
],
subtype: 1,
schedule: lightSchedule,
result: pond.InteractionResult.create({
type: quack.InteractionResultType.INTERACTION_RESULT_TYPE_SET,
value: 0
})
}))
}else{
//otherwise use the regular single TOGGLE interaction
//TOGGLE red OFF when pressure within range of upper and lower
lightInteractions.push(pond.InteractionSettings.create({
sink: redSink,
source: pressureSource,
conditions: [
pond.InteractionCondition.create({
comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN,
value: -highDelta,
measurementType: quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE
}),
pond.InteractionCondition.create({
comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_LESS_THAN,
value: -lowDelta,
measurementType: quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE
})
],
nodeOne: 2,
nodeTwo: 1,
subtype: 18,
schedule: lightSchedule,
result: pond.InteractionResult.create({ result: pond.InteractionResult.create({
type: quack.InteractionResultType.INTERACTION_RESULT_TYPE_TOGGLE, type: quack.InteractionResultType.INTERACTION_RESULT_TYPE_TOGGLE,
value: 0 value: 0
}) })
}); }))
}
let deviceID = Device.any(compDevice.device).id(); let deviceID = Device.any(compDevice.device).id();
if (deviceID !== undefined) { if (deviceID !== undefined) {
let multi = pond.MultiInteractionSettings.create({ let multi = pond.MultiInteractionSettings.create({
interactions: [greenToggle, redToggle] interactions: lightInteractions
}); });
interactionsAPI interactionsAPI
.addMultiInteractions(deviceID, multi, as) .addMultiInteractions(deviceID, multi, as)
@ -230,11 +341,51 @@ export default function GateDeviceInteraction(props: Props) {
}}> }}>
<DialogTitle>Set Interaction For Light Toggle</DialogTitle> <DialogTitle>Set Interaction For Light Toggle</DialogTitle>
<DialogContent> <DialogContent>
Your Delta Pressures, in pascals, will be: {/* Your Delta Pressures, in pascals, will be:
<Typography>low = {lowDelta}</Typography> <Typography>low = {lowDelta}</Typography>
<Typography>high = {highDelta}</Typography> <Typography>high = {highDelta}</Typography>
<Typography>{greenComponent ? "" : "Green LED Component not found"}</Typography> <Typography>{greenComponent ? "" : "Green LED Component not found"}</Typography>
<Typography>{redComponent ? "" : "Red LED Component not found"}</Typography> <Typography>{redComponent ? "" : "Red LED Component not found"}</Typography> */}
<Typography>
This will clear existing interactions on the pressure chain and set new ones to toggle the green light on and the red light off
when the difference between the pressures falls within this range and vice versa:
</Typography>
<br />
<Typography>Upper Limit: {getPressureUnit() === pond.PressureUnit.PRESSURE_UNIT_INCHES_OF_WATER ? (highDelta/249.089).toFixed(2) + " iwg" : highDelta/1000 + " kPa"}</Typography>
<Typography>Lower Limit: {getPressureUnit() === pond.PressureUnit.PRESSURE_UNIT_INCHES_OF_WATER ? (lowDelta/249.089).toFixed(2) + " iwg" : lowDelta/1000 + " kPa"}</Typography>
<br />
<Typography>If you would like to have the interactions set in such a way that if the average pressure falls below a given value set the use off condition and set the value to use</Typography>
<FormControlLabel
control={
<Checkbox
checked={useRedOffCondition}
onChange={(e, checked) => {
setUseRedOffCondition(checked)
}}
/>
}
label={<Typography>Use Off Condition</Typography>}
/>
<TextField
type="number"
fullWidth
disabled={!useRedOffCondition}
value={redThreshold}
error={isNaN(parseFloat(redThreshold))}
helperText={isNaN(parseFloat(redThreshold)) ? "Must be a valid number" : ""}
InputProps={{
endAdornment: (
<InputAdornment position="end">
{getPressureUnit() === pond.PressureUnit.PRESSURE_UNIT_INCHES_OF_WATER ? "iwg" : "kPa"}
</InputAdornment>
)
}}
onChange={e => {
setRedThreshold(e.target.value)
}}
/>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button <Button
@ -244,9 +395,13 @@ export default function GateDeviceInteraction(props: Props) {
//TODO: Once we figure out how to fix the backend to handle rapid addition/removal of interactions //TODO: Once we figure out how to fix the backend to handle rapid addition/removal of interactions
//removeCurrentInteractions(); //removeCurrentInteractions();
setAdding(true); setAdding(true);
interactionsAPI.clearInteractions(device.id(), [pressureSource]).then(resp => {
createInteractions(); createInteractions();
}).catch(err => {
console.error(err)
})
}} }}
disabled={greenComponent === undefined || redComponent === undefined || adding}> disabled={buttonDisabled()}>
{adding ? "Adding Interaction" : "Create Interaction"} {adding ? "Adding Interaction" : "Create Interaction"}
</Button> </Button>
</DialogActions> </DialogActions>

View file

@ -70,13 +70,19 @@ export default function GateFlowGraph(props: Props) {
const classes = useStyles(); const classes = useStyles();
const [flowEvents, setFlowEvents] = useState<pond.AirFlowEvent[]>([]); const [flowEvents, setFlowEvents] = useState<pond.AirFlowEvent[]>([]);
const [eventsLoading, setEventsLoading] = useState(false); const [eventsLoading, setEventsLoading] = useState(false);
//these two constants could be entered by the user at time of retrieval //these two constants could be entered by the user at time of retrieval or set in the gates settings
const eventThreshold = 5; //the threshold that if crossed from one measurement to the next during a flow event will end the current flow event and start a new one const eventThreshold = 5; //the threshold that if crossed from one measurement to the next during a flow event will end the current flow event and start a new one
const idleFlow = 3.5; //the mass air flow that must be crossed in order to start a flow event, anything below this will not start an event but must go below this to end an event //const idleFlow = 3.5; //the mass air flow that must be crossed in order to start a flow event, anything below this will not start an event but must go below this to end an event
const [idleFlow, setIdleFlow] = useState(3.5)
useEffect(() => { useEffect(() => {
if (loadingChartData) return; if (loadingChartData) return;
if (ambient && pressureComponent) { if (ambient && pressureComponent) {
let gateIdle = idleFlow
if(gate.settings.idleFlow){
gateIdle = gate.settings.idleFlow
}
let recent: SSAreaDataPoint | undefined; let recent: SSAreaDataPoint | undefined;
setLoadingChartData(true); setLoadingChartData(true);
gateAPI gateAPI
@ -110,11 +116,11 @@ export default function GateFlowGraph(props: Props) {
/** determine runtime */ /** determine runtime */
// set the start time if the values is greater than the idleFlow and start is not already set // set the start time if the values is greater than the idleFlow and start is not already set
if (val.airFlow >= idleFlow && !start) { if (val.airFlow >= gateIdle && !start) {
start = time; start = time;
} }
// set the stop time when off or at the last measurements and the start time is set // set the stop time when off or at the last measurements and the start time is set
if ((val.airFlow < idleFlow || i === resp.data.values.length - idleFlow) && start) { if ((val.airFlow < gateIdle || i === resp.data.values.length - gateIdle) && start) {
stop = time; stop = time;
} }
// if both start and stop are set calculate add the timeframe to the total runtime // if both start and stop are set calculate add the timeframe to the total runtime

View file

@ -11,7 +11,7 @@ import ResponsiveTable, { Column } from "common/ResponsiveTable";
import AddGateFab from "./AddGateFab"; import AddGateFab from "./AddGateFab";
import GateSettings from "./GateSettings"; import GateSettings from "./GateSettings";
import { useGateAPI, useGlobalState } from "providers"; import { useGateAPI, useGlobalState } from "providers";
import { CheckCircleOutline, DoNotDisturb, ErrorOutline, RemoveCircleOutline, Settings } from "@mui/icons-material"; import { CheckCircleOutline, DoNotDisturb, ErrorOutline, HelpOutlineOutlined, Settings } from "@mui/icons-material";
import { cloneDeep } from "lodash"; import { cloneDeep } from "lodash";
import moment from "moment"; import moment from "moment";
import { pond } from "protobuf-ts/pond"; import { pond } from "protobuf-ts/pond";
@ -98,9 +98,9 @@ export default function GateList(props: Props) {
case pond.PCAState.PCA_STATE_OUT_BOUNDS: case pond.PCAState.PCA_STATE_OUT_BOUNDS:
return <ErrorOutline sx={{color: "red"}} /> return <ErrorOutline sx={{color: "red"}} />
case pond.PCAState.PCA_STATE_OFF: case pond.PCAState.PCA_STATE_OFF:
return <RemoveCircleOutline />
default:
return <DoNotDisturb /> return <DoNotDisturb />
default:
return <HelpOutlineOutlined />
} }
} }

View file

@ -76,6 +76,7 @@ export default function GateSettings(props: Props) {
//user set identifier to be shown on the marker on the map //user set identifier to be shown on the marker on the map
const [terminalIdentifier, setTerminalIdentifier] = useState(""); const [terminalIdentifier, setTerminalIdentifier] = useState("");
const [gateIdentifier, setGateIdentifier] = useState(""); const [gateIdentifier, setGateIdentifier] = useState("");
const [idleLimit, setIdleLimit] = useState<number>(0);
const [hourlyPCA, setHourlyPCA] = useState<number>(0); const [hourlyPCA, setHourlyPCA] = useState<number>(0);
const [hourlyAPU, setHourlyAPU] = useState<number>(0); const [hourlyAPU, setHourlyAPU] = useState<number>(0);
@ -140,6 +141,7 @@ export default function GateSettings(props: Props) {
settings.thermalResistance = ductProps.thermalResistance; settings.thermalResistance = ductProps.thermalResistance;
settings.lowerFlow = lowerFlowBound; settings.lowerFlow = lowerFlowBound;
settings.upperFlow = upperFlowBound; settings.upperFlow = upperFlowBound;
settings.idleFlow = idleLimit;
settings.ductName = ductName; settings.ductName = ductName;
settings.pcaType = pcaUnit; settings.pcaType = pcaUnit;
settings.letterIdentifier = terminalIdentifier; settings.letterIdentifier = terminalIdentifier;
@ -167,6 +169,7 @@ export default function GateSettings(props: Props) {
thermalResistance: ductProps.thermalResistance, thermalResistance: ductProps.thermalResistance,
lowerFlow: lowerFlowBound, lowerFlow: lowerFlowBound,
upperFlow: upperFlowBound, upperFlow: upperFlowBound,
idleFlow: idleLimit,
ductName: ductName, ductName: ductName,
pcaType: pcaUnit, pcaType: pcaUnit,
letterIdentifier: terminalIdentifier, letterIdentifier: terminalIdentifier,
@ -283,16 +286,18 @@ export default function GateSettings(props: Props) {
return ( return (
<React.Fragment> <React.Fragment>
<TextField <TextField
margin="dense"
label="Gate Name" label="Gate Name"
fullWidth fullWidth
value={gateName} value={gateName}
onChange={e => setGateName(e.target.value)} onChange={e => setGateName(e.target.value)}
/> />
<Select <TextField
margin="dense"
id="terminal" id="terminal"
label="Terminal" label="Terminal"
fullWidth fullWidth
displayEmpty select
value={terminalKey} value={terminalKey}
onChange={e => { onChange={e => {
setTerminalKey(e.target.value as string); setTerminalKey(e.target.value as string);
@ -305,8 +310,9 @@ export default function GateSettings(props: Props) {
{terminal.name} {terminal.name}
</MenuItem> </MenuItem>
))} ))}
</Select> </TextField>
<TextField <TextField
margin="dense"
label="Low Mass Air Flow" label="Low Mass Air Flow"
fullWidth fullWidth
type="number" type="number"
@ -314,19 +320,30 @@ export default function GateSettings(props: Props) {
onChange={e => setLowerFlowBound(+e.target.value)} onChange={e => setLowerFlowBound(+e.target.value)}
/> />
<TextField <TextField
margin="dense"
label="High Mass Air Flow" label="High Mass Air Flow"
fullWidth fullWidth
type="number" type="dense"
value={upperFlowBound} value={upperFlowBound}
onChange={e => setUpperFlowBound(+e.target.value)} onChange={e => setUpperFlowBound(+e.target.value)}
/> />
<TextField <TextField
margin="dense"
label="Idle Mass Air Flow"
fullWidth
type="number"
value={idleLimit}
onChange={e => setIdleLimit(+e.target.value)}
/>
<TextField
margin="dense"
label="PCA Unit" label="PCA Unit"
fullWidth fullWidth
value={pcaUnit} value={pcaUnit}
onChange={e => setPcaUnit(e.target.value)} onChange={e => setPcaUnit(e.target.value)}
/> />
<TextField <TextField
margin="dense"
style={{ width: "45%" }} style={{ width: "45%" }}
select select
label="Terminal Identifier" label="Terminal Identifier"
@ -340,6 +357,7 @@ export default function GateSettings(props: Props) {
{tiOptions} {tiOptions}
</TextField> </TextField>
<TextField <TextField
margin="dense"
style={{ width: "45%", marginLeft: "10%" }} style={{ width: "45%", marginLeft: "10%" }}
label="Gate Identifier" label="Gate Identifier"
select select
@ -351,6 +369,7 @@ export default function GateSettings(props: Props) {
{numberOptions} {numberOptions}
</TextField> </TextField>
<TextField <TextField
margin="dense"
label="Hourly PCA Cost" label="Hourly PCA Cost"
fullWidth fullWidth
type="number" type="number"
@ -361,6 +380,7 @@ export default function GateSettings(props: Props) {
onChange={e => setHourlyPCA(+e.target.value)} onChange={e => setHourlyPCA(+e.target.value)}
/> />
<TextField <TextField
margin="dense"
label="Hourly APU Cost" label="Hourly APU Cost"
fullWidth fullWidth
type="number" type="number"

View file

@ -36,6 +36,7 @@ export interface IInteractionsAPIContext {
alerts: pond.AlertData[], alerts: pond.AlertData[],
otherTeam?: string otherTeam?: string
) => Promise<AxiosResponse<pond.SetAlertInteractionsResponse>>; ) => Promise<AxiosResponse<pond.SetAlertInteractionsResponse>>;
clearInteractions: (device: number, sources?: quack.ComponentID[], otherTeam?: string) => Promise<AxiosResponse<pond.ClearInteractionsResponse>>
} }
export const InteractionsAPIContext = createContext<IInteractionsAPIContext>( export const InteractionsAPIContext = createContext<IInteractionsAPIContext>(
@ -285,6 +286,25 @@ export default function InteractionProvider(props: PropsWithChildren<Props>) {
}) })
}; };
const clearInteractions = (device: number, sources?: quack.ComponentID[], otherTeam?: string): Promise<AxiosResponse<pond.ClearInteractionsResponse>> => {
const view = otherTeam ? otherTeam : as
let sourceArray: string[] = []
if (sources){
sources.forEach(source => {
sourceArray.push(componentIDToString(source))
})
}
let url = pondURL("/devices/"+ device + "/interactions/clear" + (view ? "?as=" + view : "") + (sources ? "&sources=" + sourceArray.toString() : ""))
return new Promise<AxiosResponse<pond.ClearInteractionsResponse>>((resolve, reject) => {
post<pond.ClearInteractionsResponse>(url).then(resp => {
resp.data = pond.ClearInteractionsResponse.fromObject(resp.data)
return resolve(resp)
}).catch(err => {
return reject(err)
})
})
}
return ( return (
<InteractionsAPIContext.Provider <InteractionsAPIContext.Provider
value={{ value={{
@ -296,7 +316,8 @@ export default function InteractionProvider(props: PropsWithChildren<Props>) {
updateInteractionPondSettings, updateInteractionPondSettings,
removeInteraction, removeInteraction,
listInteractionsByDevice, listInteractionsByDevice,
listInteractionsByComponent listInteractionsByComponent,
clearInteractions
}}> }}>
{children} {children}
</InteractionsAPIContext.Provider> </InteractionsAPIContext.Provider>