bin sensors add the bins

This commit is contained in:
Carter 2025-03-13 09:26:47 -06:00
parent c46cb5ce4b
commit b5071dfb40
9 changed files with 7346 additions and 20 deletions

267
src/device/DeviceWizard.tsx Normal file
View file

@ -0,0 +1,267 @@
import {
Avatar,
Box,
Card,
CardContent,
CardHeader,
List,
ListItem,
ListItemIcon,
ListItemText,
ListSubheader,
Menu,
MenuItem,
Theme,
Tooltip,
Typography
} from "@mui/material";
import ComponentSettings from "component/ComponentSettings";
import { Component, Device } from "models";
import { GetComponentIcon } from "pbHelpers/ComponentType";
import {
DeviceAvailabilityMap,
FindAvailablePositions,
OffsetAvailabilityMap
} from "pbHelpers/DeviceAvailability";
import { GetDeviceProductIcon, GetDeviceProductLabel } from "products/DeviceProduct";
import { pond } from "protobuf-ts/pond";
import { quack } from "protobuf-ts/quack";
import { useComponentAPI, useSnackbar } from "providers";
import React, { useState } from "react";
import DeviceSVG, { PortInformation } from "./deviceSVG";
import { makeStyles } from "@mui/styles";
interface Props {
device: Device;
//permissions: pond.Permission[];
components: Component[];
refreshCallback: () => void;
}
const useStyles = makeStyles((theme: Theme) => {
return ({
card: {
// position: "relative",
// display: "flex",
// height: "100%",
// flexDirection: "column",
// overflow: "visible"
minHeight: "200px"
},
cardHeader: {
padding: theme.spacing(1),
paddingLeft: theme.spacing(2)
},
cardContent: {
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
alignItems: "center",
padding: theme.spacing(1),
paddingTop: 0
},
avatarIcon: {
width: theme.spacing(3),
height: theme.spacing(3)
}
})
});
export default function DeviceWizard(props: Props) {
const { device, components, refreshCallback } = props;
const [componentDialogOpen, setComponentDialogOpen] = useState(false);
const [availableOffsets, setAvailableOffsets] = useState<OffsetAvailabilityMap>(new Map());
const [availablePositions, setAvailablePositions] = useState<DeviceAvailabilityMap>(new Map());
const compAPI = useComponentAPI();
const [menuAnchor, setMenuAnchor] = useState<null | Element>(null);
const [portComponents, setPortComponents] = useState<Component[]>([]);
const [componentToUpdate, setComponentToUpdate] = useState<Component | undefined>();
const { error, success } = useSnackbar();
const [ComponentSettingsMode, setComponentSettingsMode] = useState<"add" | "remove" | "update">(
"add"
);
const classes = useStyles();
const [selectedPort, setSelectedPort] = useState<PortInformation>();
//const [addressType, setAddressType] = useState<quack.AddressType>(quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY);
const wizardMenu = () => {
return (
<Menu
id="groupMenu"
anchorEl={menuAnchor}
open={Boolean(menuAnchor)}
onClose={() => setMenuAnchor(null)}
keepMounted
disableAutoFocusItem>
<MenuItem
key={"add"}
onClick={() => {
setComponentSettingsMode("add");
setComponentToUpdate(undefined);
setComponentDialogOpen(true);
}}>
Add Component
</MenuItem>
{/* <MenuItem key={"list_error"} onClick={() => {
}}>
List Errors
</MenuItem> */}
{selectedPort && selectedPort.autoDetectable && (
<Tooltip title="Used to Detect Grain Cables">
<MenuItem
key={"auto"}
onClick={() => {
setAutoDetect();
}}>
Auto Detect
</MenuItem>
</Tooltip>
)}
{portComponents.length > 0 && (
<List>
<ListSubheader>Current Components</ListSubheader>
{portComponents.map(comp => (
<ListItem
key={comp.key()}
onClick={() => {
setComponentToUpdate(comp);
setComponentSettingsMode("update");
setComponentDialogOpen(true);
}}>
<ListItemIcon>
<Avatar
className={classes.avatarIcon}
alt={comp.subTypeName() + "Icon"}
src={GetComponentIcon(comp.type(), comp.subType())}
/>
</ListItemIcon>
<ListItemText>{comp.name()}</ListItemText>
</ListItem>
))}
</List>
)}
</Menu>
);
};
/**
* sets an autodetect component on the port that was selected
* TODO: when the auto detect gets expanded out of being a grain cable component this will have to be re-factored to use the new component type since it is hard coded
*/
const setAutoDetect = () => {
if (selectedPort) {
let componentSettings = pond.ComponentSettings.create();
componentSettings.type = quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE; // use the grain cable component
componentSettings.subtype = quack.GrainCableSubtype.GRAIN_CABLE_SUBTYPE_OPI_DIAG; //use the port diagnostic subtype
componentSettings.addressType = quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY;
componentSettings.address = selectedPort.address; // the address of the selected port
componentSettings.measurementPeriodMs = 600000; //set the measurement period to 10 mins
componentSettings.reportPeriodMs = 600000;
componentSettings.name = "Port " + selectedPort.label + " Auto Detect";
compAPI
.add(device.id(), componentSettings)
.then(resp => {
success("Added Auto Detect Component to Port");
})
.catch(err => {
error("Failed to Add Auto Detect Component to Port");
})
.finally(() => {
refreshCallback();
});
}
};
/**
* function that restricts the pin availabilty in the availability map to the pin of the port that was selected
* does nothing for I2C ports as the restrictions for that are handled by the component type
* @param port port that was selected
* @param availability availability map of the device
* @returns modified availability map
*/
const adjustAvailablePositions = (port: PortInformation, availability: DeviceAvailabilityMap) => {
let currentAvailability = availability;
switch (port.addressType) {
case quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY:
currentAvailability.set(port.addressType, [{ address: port.address, label: port.label }]);
}
return currentAvailability;
};
const findPortComponents = (components: Component[], port: PortInformation) => {
let pc: Component[] = [];
switch (port.addressType) {
case quack.AddressType.ADDRESS_TYPE_I2C:
components.forEach(comp => {
if (comp.settings.addressType === quack.AddressType.ADDRESS_TYPE_I2C) {
pc.push(comp);
}
});
break;
case quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY:
components.forEach(comp => {
if (comp.settings.address === port.address) {
pc.push(comp);
}
});
}
return pc;
};
const setupCard = () => {
return (
<Card className={classes.card} raised>
<CardHeader
avatar={
<Avatar
variant="square"
src={GetDeviceProductIcon(device.settings)}
className={classes.avatarIcon}
alt={"devIcon"}
/>
}
title={GetDeviceProductLabel(device.settings.product) + " - ID:" + device.id()}
className={classes.cardHeader}
/>
<CardContent style={{ paddingTop: 0 }}>
<Typography variant="caption">
Select a port on the device below to modify components
</Typography>
<Box paddingLeft={"15%"} paddingRight={"15%"} paddingTop={"20px"}>
<DeviceSVG
device={device}
openMenu={(e, port) => {
let available = FindAvailablePositions(components, device.settings.product);
setAvailableOffsets(available.offsetAvailability);
setAvailablePositions(adjustAvailablePositions(port, available.availability));
//setAddressType(port.addressType);
setSelectedPort(port);
setPortComponents(findPortComponents(components, port));
setMenuAnchor(e.currentTarget);
}}
/>
</Box>
</CardContent>
</Card>
);
};
return (
<React.Fragment>
{setupCard()}
<ComponentSettings
mode={ComponentSettingsMode}
component={componentToUpdate}
device={device}
isDialogOpen={componentDialogOpen}
closeDialogCallback={() => setComponentDialogOpen(false)}
availablePositions={availablePositions}
availableOffsets={availableOffsets}
refreshCallback={refreshCallback}
canEdit
addressTypeRestriction={selectedPort && selectedPort.addressType}
/>
{wizardMenu()}
</React.Fragment>
);
}