Merge branch 'bin_page_fan_control' into staging_environment
This commit is contained in:
commit
002959ef47
3 changed files with 203 additions and 52 deletions
168
src/bin/BinControllerDisplay.tsx
Normal file
168
src/bin/BinControllerDisplay.tsx
Normal file
|
|
@ -0,0 +1,168 @@
|
||||||
|
import { Component } from "models"
|
||||||
|
import React, { useEffect, useState } from "react"
|
||||||
|
import { makeStyles } from "@mui/styles";
|
||||||
|
import { Box, Button, DialogActions, DialogContent, DialogTitle, Typography } from "@mui/material";
|
||||||
|
import AerationFanIcon from "products/AgIcons/AerationFanIcon";
|
||||||
|
import { quack } from "protobuf-ts/quack";
|
||||||
|
import { useComponentAPI, useMobile, useSnackbar } from "hooks";
|
||||||
|
import ButtonGroup from "common/ButtonGroup";
|
||||||
|
import ObjectHeaterIcon from "products/Construction/ObjectHeaterIcon";
|
||||||
|
import ResponsiveDialog from "common/ResponsiveDialog";
|
||||||
|
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
deviceID: number
|
||||||
|
component: Component
|
||||||
|
currentState?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = makeStyles(() => {
|
||||||
|
return ({
|
||||||
|
controllerDisplay: {
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
paddingLeft: 5,
|
||||||
|
paddingRight: 5
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
export default function BinControllerDisplay(props: Props) {
|
||||||
|
const {component, deviceID, currentState} = props
|
||||||
|
const [controllerState, setControllerState] = useState(0)
|
||||||
|
const [openDialog, setOpenDialog] = useState(false)
|
||||||
|
const [newOutputState, setNewOutputState] = useState(0)
|
||||||
|
const {openSnack} = useSnackbar()
|
||||||
|
const componentAPI = useComponentAPI()
|
||||||
|
const isMobile = useMobile()
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
setControllerState(component.settings.defaultOutputState)
|
||||||
|
},[component])
|
||||||
|
|
||||||
|
|
||||||
|
const controllerIcon = () => {
|
||||||
|
if(component.subType() === quack.BooleanOutputSubtype.BOOLEAN_OUTPUT_SUBTYPE_HEATER){
|
||||||
|
return <ObjectHeaterIcon />
|
||||||
|
}else{
|
||||||
|
return <AerationFanIcon />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const setController = () => {
|
||||||
|
let newSettings = component.settings
|
||||||
|
newSettings.defaultOutputState = newOutputState
|
||||||
|
componentAPI.update(deviceID, newSettings).then(resp => {
|
||||||
|
openSnack("Updated controllers output state")
|
||||||
|
setControllerState(newOutputState)
|
||||||
|
}).catch(err => {
|
||||||
|
openSnack("There was a problem updating the controller")
|
||||||
|
}).finally(() => {
|
||||||
|
setOpenDialog(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const controllerDialog = () => {
|
||||||
|
return (
|
||||||
|
<ResponsiveDialog open={openDialog} onClose={() => {setOpenDialog(false)}}>
|
||||||
|
<DialogTitle>Set Controller State</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
This Action will change the output state of the controller
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={()=>{setOpenDialog(false)}}>Cancel</Button>
|
||||||
|
<Button onClick={()=>{setController()}} variant="contained" color="primary">Confirm</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</ResponsiveDialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const componentOn = () => {
|
||||||
|
if(currentState){
|
||||||
|
return currentState
|
||||||
|
}else{
|
||||||
|
let state = false
|
||||||
|
component.status.lastGoodMeasurement.forEach(um => {
|
||||||
|
if(um.type === quack.MeasurementType.MEASUREMENT_TYPE_BOOLEAN){
|
||||||
|
if(um.values.length > 0){
|
||||||
|
let readings = um.values
|
||||||
|
if(readings[readings.length-1].values.length > 0){
|
||||||
|
let nodes = readings[readings.length-1].values
|
||||||
|
if (nodes[nodes.length -1] === 1){
|
||||||
|
state = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{controllerDialog()}
|
||||||
|
<Box className={classes.controllerDisplay} key={component.key()} marginTop={1} marginBottom={1}>
|
||||||
|
<Box display="flex" marginY="auto">
|
||||||
|
<Box margin="auto">
|
||||||
|
{controllerIcon()}
|
||||||
|
</Box>
|
||||||
|
<Typography
|
||||||
|
align="center"
|
||||||
|
style={{
|
||||||
|
marginBottom: "auto",
|
||||||
|
marginTop: "auto",
|
||||||
|
paddingLeft: 5,
|
||||||
|
fontSize: isMobile ? "0.85rem" : "1.0rem",
|
||||||
|
fontWeight: 650
|
||||||
|
}}>
|
||||||
|
{component.name()}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Box display="flex">
|
||||||
|
<ButtonGroup
|
||||||
|
buttons={[
|
||||||
|
{
|
||||||
|
title: "Auto",
|
||||||
|
function: () => {
|
||||||
|
setNewOutputState(0)
|
||||||
|
setOpenDialog(true)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "On",
|
||||||
|
function: () => {
|
||||||
|
setNewOutputState(1)
|
||||||
|
setOpenDialog(true)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Off",
|
||||||
|
function: () => {
|
||||||
|
setNewOutputState(2)
|
||||||
|
setOpenDialog(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
toggledButtons={[controllerState]}
|
||||||
|
toggle
|
||||||
|
/>
|
||||||
|
<Typography
|
||||||
|
style={{
|
||||||
|
minWidth: 30,
|
||||||
|
marginLeft: "10px",
|
||||||
|
marginBottom: "auto",
|
||||||
|
marginTop: "auto",
|
||||||
|
fontSize: isMobile ? "0.85rem" : "1.0rem",
|
||||||
|
fontWeight: 650,
|
||||||
|
color: componentOn() ? "green" : "orange"
|
||||||
|
}}>
|
||||||
|
{componentOn() ? "ON" : "OFF"}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -31,7 +31,7 @@ import { round } from "lodash";
|
||||||
import { Bin, Component, Device } from "models";
|
import { Bin, Component, Device } from "models";
|
||||||
import { GetComponentIcon } from "pbHelpers/ComponentType";
|
import { GetComponentIcon } from "pbHelpers/ComponentType";
|
||||||
import { describeMeasurement } from "pbHelpers/MeasurementDescriber";
|
import { describeMeasurement } from "pbHelpers/MeasurementDescriber";
|
||||||
import { useMobile } from "hooks";
|
import { useComponentAPI, useMobile } from "hooks";
|
||||||
import { pond } from "protobuf-ts/pond";
|
import { pond } from "protobuf-ts/pond";
|
||||||
import { quack } from "protobuf-ts/quack";
|
import { quack } from "protobuf-ts/quack";
|
||||||
import { useGlobalState, useSnackbar } from "providers";
|
import { useGlobalState, useSnackbar } from "providers";
|
||||||
|
|
@ -71,6 +71,7 @@ import { makeStyles } from "@mui/styles";
|
||||||
import ButtonGroup from "common/ButtonGroup";
|
import ButtonGroup from "common/ButtonGroup";
|
||||||
import ModeChangeDialog from "./conditioning/modeChangeDialog";
|
import ModeChangeDialog from "./conditioning/modeChangeDialog";
|
||||||
import CustomGrainSelector from "grain/CustomGrainSelector";
|
import CustomGrainSelector from "grain/CustomGrainSelector";
|
||||||
|
import BinControllerDisplay from "./BinControllerDisplay";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => {
|
const useStyles = makeStyles((theme: Theme) => {
|
||||||
return ({
|
return ({
|
||||||
|
|
@ -180,7 +181,7 @@ interface Props {
|
||||||
bin: Bin;
|
bin: Bin;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
components: Map<string, Component>;
|
components: Map<string, Component>;
|
||||||
componentDevices?: Map<string, number>;
|
componentDevices: Map<string, number>;
|
||||||
devices: Device[];
|
devices: Device[];
|
||||||
plenums?: Plenum[];
|
plenums?: Plenum[];
|
||||||
ambient?: Ambient;
|
ambient?: Ambient;
|
||||||
|
|
@ -289,6 +290,12 @@ export default function BinVisualizer(props: Props) {
|
||||||
const [modeChangeInProgress, setModeChangeInProgress] = useState(false)
|
const [modeChangeInProgress, setModeChangeInProgress] = useState(false)
|
||||||
const [newGrainSettings, setNewGrainSettings] = useState<pond.GrainSettings>()
|
const [newGrainSettings, setNewGrainSettings] = useState<pond.GrainSettings>()
|
||||||
|
|
||||||
|
const componentAPI = useComponentAPI()
|
||||||
|
const [openControllerDIalog, setOpenControllerDialog] = useState(false)
|
||||||
|
const [controllerOutputStates, setControllerOutputStates] = useState<Map<string, number>>(new Map<string, number>())
|
||||||
|
const [selectedController, setSelectedController] = useState<Component>()
|
||||||
|
const [newOutputState, setNewOutputState] = useState()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setModeTime(moment(bin.status.lastModeChange));
|
setModeTime(moment(bin.status.lastModeChange));
|
||||||
}, [bin.status.lastModeChange]);
|
}, [bin.status.lastModeChange]);
|
||||||
|
|
@ -1544,54 +1551,24 @@ export default function BinVisualizer(props: Props) {
|
||||||
Controllers
|
Controllers
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box className={classes.displayBox}>
|
<Box className={classes.displayBox}>
|
||||||
{bin.status.fans.map(fan => (
|
{bin.status.fans.map(fan => {
|
||||||
<Box className={classes.controllerDisplay} key={fan.key}>
|
let fanComp = components.get(fan.key)
|
||||||
<Box display="flex">
|
let device = componentDevices.get(fan.key)
|
||||||
<AerationFanIcon />
|
if(fanComp && device){
|
||||||
<Typography
|
return (
|
||||||
align="center"
|
<BinControllerDisplay component={fanComp} deviceID={device} currentState/>
|
||||||
style={{
|
)
|
||||||
paddingLeft: 5,
|
}
|
||||||
fontSize: isMobile ? "0.85rem" : "1.0rem",
|
})}
|
||||||
fontWeight: 650
|
{bin.status.heaters.map(heater => {
|
||||||
}}>
|
let heaterComp = components.get(heater.key)
|
||||||
{fan.name}
|
let device = componentDevices.get(heater.key)
|
||||||
</Typography>
|
if(heaterComp && device){
|
||||||
</Box>
|
return (
|
||||||
<Typography
|
<BinControllerDisplay component={heaterComp} deviceID={device} currentState={heater.state}/>
|
||||||
style={{
|
)
|
||||||
fontSize: isMobile ? "0.85rem" : "1.0rem",
|
}
|
||||||
fontWeight: 650,
|
})}
|
||||||
color: fan.state ? "green" : "orange"
|
|
||||||
}}>
|
|
||||||
{fan.state ? "ON" : "OFF"}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
{bin.status.heaters.map(heater => (
|
|
||||||
<Box className={classes.controllerDisplay} key={heater.key}>
|
|
||||||
<Box display="flex">
|
|
||||||
<ObjectHeaterIcon />
|
|
||||||
<Typography
|
|
||||||
align="center"
|
|
||||||
style={{
|
|
||||||
paddingLeft: 5,
|
|
||||||
fontSize: isMobile ? "0.85rem" : "1.0rem",
|
|
||||||
fontWeight: 650
|
|
||||||
}}>
|
|
||||||
{heater.name}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Typography
|
|
||||||
style={{
|
|
||||||
fontSize: isMobile ? "0.85rem" : "1.0rem",
|
|
||||||
fontWeight: 650,
|
|
||||||
color: heater.state ? "green" : "orange"
|
|
||||||
}}>
|
|
||||||
{heater.state ? "ON" : "OFF"}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,10 @@ interface Props {
|
||||||
* @default undefined
|
* @default undefined
|
||||||
*/
|
*/
|
||||||
toggledButtons?: number[]
|
toggledButtons?: number[]
|
||||||
|
/**
|
||||||
|
* This can be used to set which button starts toggled by default, and from there it will be controlled internally, toggledButtons will override this
|
||||||
|
*/
|
||||||
|
defaultToggle?: number
|
||||||
/**
|
/**
|
||||||
* Numerical value for the size of the text for buttons that dont use the icon
|
* Numerical value for the size of the text for buttons that dont use the icon
|
||||||
*/
|
*/
|
||||||
|
|
@ -97,7 +101,7 @@ const useStyles = makeStyles((theme: Theme) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
export default function ButtonGroup(props: Props){
|
export default function ButtonGroup(props: Props){
|
||||||
const { buttons, toggle, toggledButtons, multiToggle, textSize, disableAll } = props
|
const { buttons, toggle, toggledButtons, defaultToggle, multiToggle, textSize, disableAll } = props
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const [currentToggle, setCurrentToggle] = useState<number[]>([])
|
const [currentToggle, setCurrentToggle] = useState<number[]>([])
|
||||||
|
|
||||||
|
|
@ -107,6 +111,8 @@ export default function ButtonGroup(props: Props){
|
||||||
let toggled: number [] = [0]
|
let toggled: number [] = [0]
|
||||||
if(toggledButtons){
|
if(toggledButtons){
|
||||||
toggled = toggledButtons
|
toggled = toggledButtons
|
||||||
|
} else if(defaultToggle){
|
||||||
|
toggled = [defaultToggle]
|
||||||
}
|
}
|
||||||
setCurrentToggle(toggled)
|
setCurrentToggle(toggled)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue