320 lines
11 KiB
TypeScript
320 lines
11 KiB
TypeScript
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, useDeviceAPI, useGlobalState, 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;
|
|
scanInProgress?: boolean
|
|
}
|
|
|
|
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, scanInProgress } = props;
|
|
const [{as}] = useGlobalState();
|
|
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 deviceAPI = useDeviceAPI()
|
|
const { openSnack } = useSnackbar()
|
|
const [buttonClicked, setButtonClicked] = useState(false);
|
|
//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>
|
|
)}
|
|
{selectedPort && selectedPort.addressType === quack.AddressType.ADDRESS_TYPE_I2C && device.featureSupported("detectI2C") && (
|
|
<Tooltip title="Used to tell the Device to scan its I2C port for any plugged in sensors">
|
|
<MenuItem
|
|
key={"scanI2C"}
|
|
disabled={scanInProgress || buttonClicked}
|
|
onClick={() => {
|
|
setButtonClicked(true)
|
|
deviceAPI.detectI2C(device.id())
|
|
.then(resp => {
|
|
openSnack("Device will scan port on next check-in")
|
|
}).catch(err => {
|
|
openSnack("Command failed to send to device")
|
|
})
|
|
}}>
|
|
Scan Port
|
|
</MenuItem>
|
|
</Tooltip>
|
|
)}
|
|
{selectedPort &&
|
|
selectedPort.addressType === quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY &&
|
|
device.featureSupported("detectOneWire") &&
|
|
selectedPort.autoDetectable && (
|
|
<Tooltip title="Used to tell the Device to scan this port for any plugged in sensors">
|
|
<MenuItem
|
|
key={"scanOneWire"}
|
|
disabled={scanInProgress || buttonClicked}
|
|
onClick={() => {
|
|
setButtonClicked(true)
|
|
console.log(selectedPort)
|
|
deviceAPI.detectOneWire(device.id(), selectedPort.address)
|
|
.then(resp => {
|
|
openSnack("Device will scan port on next check-in")
|
|
}).catch(err => {
|
|
openSnack("Command failed to send to device")
|
|
})
|
|
}}>
|
|
Scan Port
|
|
</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, as)
|
|
.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
|
|
* @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 }]);
|
|
//since we now have a component type that can be pins or i2c need to remove i2c options if they selected a pin port
|
|
currentAvailability.set(quack.AddressType.ADDRESS_TYPE_I2C, new Map());
|
|
break;
|
|
case quack.AddressType.ADDRESS_TYPE_I2C:
|
|
//since we now have a component type that can be pins or i2c need to remove pin options if they selected the i2c port
|
|
currentAvailability.set(quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY, []);
|
|
break;
|
|
}
|
|
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 = () => {
|
|
const deviceIcon = GetDeviceProductIcon(device)
|
|
return (
|
|
<Card className={classes.card} raised>
|
|
<CardHeader
|
|
avatar={
|
|
<Avatar
|
|
variant="square"
|
|
src={deviceIcon}
|
|
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>
|
|
);
|
|
}
|