Skip to content

Commit

Permalink
adding badges to pioreactor icons
Browse files Browse the repository at this point in the history
  • Loading branch information
CamDavidsonPilon committed Mar 11, 2025
1 parent a12d8c5 commit 7b07d5e
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 55 deletions.
2 changes: 0 additions & 2 deletions src/EditExperimentProfile.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ function convertYamlToJson(yamlString){
} catch (error) {
if (["duplicated mapping key"].includes(error.reason)) {
console.log(error)
console.log(yamlString)
return {error: error.message}
}
else {
Expand Down Expand Up @@ -169,7 +168,6 @@ const EditExperimentProfilesContent = ({ initialCode, profileFilename }) => {
>
Save
</Button>
{console.log(errorMsg, isError)}
<p style={{ marginLeft: "10px" }}>{isError ? <Box color="error.main">{errorMsg}</Box> : ""}</p>
</div>
</div>
Expand Down
114 changes: 83 additions & 31 deletions src/Inventory.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,14 @@ import CircularProgress from '@mui/material/CircularProgress';
import { useConfirm } from 'material-ui-confirm';
import { useNavigate } from 'react-router-dom';
import UnderlineSpan from "./components/UnderlineSpan";
import Pioreactor40Icon from "./components/Pioreactor40Icon";
import PioreactorIcon from "./components/PioreactorIcon";
import PioreactorIconWithModel from "./components/PioreactorIconWithModel";
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
import {getConfig, disconnectedGrey, lostRed, inactiveGrey, readyGreen} from "./utilities"
import PlayCircleOutlinedIcon from '@mui/icons-material/PlayCircleOutlined';
import Snackbar from '@mui/material/Snackbar';
import Badge from '@mui/material/Badge';


import { useExperiment } from './providers/ExperimentContext';

Expand Down Expand Up @@ -190,8 +193,10 @@ function AddNewPioreactor(props){
onChange={handleVersionChange}
label="Pioreactor model"
>
<MenuItem value={"1.1"}>20ml, version 1.1</MenuItem>
<MenuItem value={"1.0"}>20ml, version 1.0</MenuItem>
<MenuItem value={"pioreactor_40ml|1.0"}>Pioreactor 40ml, v1.0</MenuItem>
<Divider/>
<MenuItem value={"pioreactor_20ml|1.1"}>Pioreactor 20ml, v1.1</MenuItem>
<MenuItem value={"pioreactor_20ml|1.0"}>Pioreactor 20ml, v1.0</MenuItem>
</Select>
</FormControl>

Expand Down Expand Up @@ -224,13 +229,33 @@ function AddNewPioreactor(props){
</React.Fragment>
);}

function modelStringFromModelNameAndModelVersion(modelName, modelVersion){
if (modelName === "pioreactor_20ml"){
return `Pioreactor 20ml, v${modelVersion}`
} else if (modelName === "pioreactor_40ml"){
return `Pioreactor 40ml, v${modelVersion}`
} else {
return "Unknown model"
}
}

function modelNameAndModelVersionFromModelString(modelString) {
const match = modelString.match(/^Pioreactor (\d+ml), v(.+)$/);

function WorkerCard({worker, config, leaderVersion}) {
if (match) {
const modelName = `pioreactor_${match[1]}`;
const modelVersion = match[2];
return { modelName, modelVersion };
}

return { modelName: "unknown", modelVersion: null };
}

function WorkerCard({worker, config, leaderVersion}) {
const unit = worker.pioreactor_unit
const isLeader = (config['cluster.topology']?.leader_hostname === unit)
const [activeStatus, setActiveStatus] = React.useState(worker.is_active ? "active" : "inactive")
const [model, setModel] = React.useState([worker.model_name, worker.model_version])
const [experimentAssigned, setExperimentAssigned] = React.useState(null)
const {client, subscribeToTopic} = useMQTT();
const [state, setState] = React.useState(null)
Expand All @@ -240,12 +265,20 @@ function WorkerCard({worker, config, leaderVersion}) {
const [ETHAddress, setETHAddress] = React.useState(null)
const { selectExperiment } = useExperiment();
const navigate = useNavigate()
const [snackbarOpen, setSnackbarOpen] = useState(false);


const isActive = () => {
return activeStatus === "active"
}

const handleSnackbarClose = (e, reason) => {
if (reason === 'clickaway') {
return;
}
setSnackbarOpen(false)
}

const onMonitorData = (topic, message, packet) => {
const setting = topic.toString().split('/').pop()
switch (setting) {
Expand Down Expand Up @@ -306,6 +339,20 @@ function WorkerCard({worker, config, leaderVersion}) {
}
}

const handleModelChange = (event) => {
const { modelName, modelVersion } = modelNameAndModelVersionFromModelString(event.target.value);
setModel([modelName, modelVersion]);
setSnackbarOpen(true)
fetch(`/api/workers/${unit}/model`, {
method: "PUT",
body: JSON.stringify({model_name: modelName, model_version: modelVersion}),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
}

const indicatorDotColor = getIndicatorDotColor(state)
const indicatorDotShadow = 2
const indicatorLabel = getInicatorLabel(state, isActive())
Expand Down Expand Up @@ -350,19 +397,6 @@ function WorkerCard({worker, config, leaderVersion}) {
navigate("/overview")
}

const pioreactorString = () => {
if (!(state === "ready" || state === "init")){
return "-"
} else {
if (versions.pioreactor_model) {
return `Pioreactor ${(versions.pioreactor_model || "-").substring(11)}, v${versions.pioreactor_version || "-"}`
}
else {
// ready and not available.
return "Missing! Fix in the configuration file."
}
}
}

const softwareVersion = () => {
const { app: workerVersion } = versions;
Expand All @@ -381,15 +415,10 @@ function WorkerCard({worker, config, leaderVersion}) {
};

return (
<>
<Card sx={{ minWidth: 275 }}>
<CardContent>

<div style={{display: "flex", justifyContent: "space-between"}}>
<Typography sx={{ fontSize: 14 }} color={isActive() ? "text.secondary" : inactiveGrey} gutterBottom>
{isLeader ? "Leader & Worker" : "Worker"}
</Typography>
</div>

<div style={{display: "flex", justifyContent: "space-between"}}>

<div style={{display: "flex", justifyContent: "left"}}>
Expand All @@ -400,12 +429,9 @@ function WorkerCard({worker, config, leaderVersion}) {
...(isActive() ? {} : { color: inactiveGrey }),
}}
gutterBottom>
{(versions.pioreactor_model !== "pioreactor_40ml") &&
<PioreactorIcon style={{verticalAlign: "middle", marginRight: "3px"}} sx={{ display: {xs: 'none', sm: 'none', md: 'inline' } }}/>
}
{(versions.pioreactor_model === "pioreactor_40ml") &&
<Pioreactor40Icon style={{verticalAlign: "middle", marginRight: "3px"}} sx={{ display: {xs: 'none', sm: 'none', md: 'inline' } }}/>
}

<PioreactorIconWithModel model={model[0]} />

{unit}

</Typography>
Expand Down Expand Up @@ -448,7 +474,24 @@ function WorkerCard({worker, config, leaderVersion}) {
Model
</td>
<td >
<code style={{backgroundColor: "rgba(0, 0, 0, 0.07)", padding: "1px 4px"}}>{pioreactorString()}</code>
<Select
labelId="modelSelect"
variant="standard"
value={modelStringFromModelNameAndModelVersion(model[0], model[1])}
onChange={handleModelChange}
label="Model"
disableUnderline={true}
sx={{
"& .MuiSelect-standard": {
color: isActive() ? "inherit" : inactiveGrey
}
}}
>
<MenuItem value={"Pioreactor 40ml, v1.0"}>Pioreactor 40ml, v1.0</MenuItem>
<Divider/>
<MenuItem value={"Pioreactor 20ml, v1.1"}>Pioreactor 20ml, v1.1</MenuItem>
<MenuItem value={"Pioreactor 20ml, v1.0"}>Pioreactor 20ml, v1.0</MenuItem>
</Select>
</td>
</tr>
<tr>
Expand Down Expand Up @@ -507,6 +550,15 @@ function WorkerCard({worker, config, leaderVersion}) {
</Box>
</CardActions>
</Card>
<Snackbar
anchorOrigin={{vertical: "bottom", horizontal: "center"}}
open={snackbarOpen}
onClose={handleSnackbarClose}
message={`Updated ${unit} to ${modelStringFromModelNameAndModelVersion(model[0], model[1])}`}
autoHideDuration={2500}
key={"snackbar" + unit + "model"}
/>
</>
)}


Expand Down Expand Up @@ -597,7 +649,7 @@ function Remove({unit, isLeader}) {

const removeWorker = () => {
confirm({
description: 'Removing this Pioreactor will unassign it from any experiments, halt all activity running, and remove it from your inventory. No experiment data is removed, however.',
description: 'Removing this Pioreactor will unassign it from any experiments, halt all activity running, and remove it from your inventory. No experiment data is removed, and calibration data still exists on the worker.',
title: `Remove ${unit} from inventory?`,
confirmationText: "Confirm",
confirmationButtonProps: {color: "primary"},
Expand Down
42 changes: 34 additions & 8 deletions src/Pioreactor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import ActionManualDosingForm from "./components/ActionManualDosingForm"
import ActionCirculatingForm from "./components/ActionCirculatingForm"
import ActionLEDForm from "./components/ActionLEDForm"
import PioreactorIcon from "./components/PioreactorIcon"
import PioreactorIconWithModel from "./components/PioreactorIconWithModel"
import UnderlineSpan from "./components/UnderlineSpan";
import Bioreactor40Diagram from "./components/Bioreactor40";
import Bioreactor20Diagram from "./components/Bioreactor20";
Expand Down Expand Up @@ -135,7 +136,6 @@ function UnitSettingDisplaySubtext(props){


function UnitSettingDisplay(props) {
console.log(props)
const value = props.value === null ? "" : props.value

function prettyPrint(x){
Expand Down Expand Up @@ -294,10 +294,19 @@ function ButtonStopProcess({experiment, unit}) {
}


function modelStringFromModelNameAndModelVersion(modelName, modelVersion){
if (modelName === "pioreactor_20ml"){
return `Pioreactor 20ml, v${modelVersion}`
} else if (modelName === "pioreactor_40ml"){
return `Pioreactor 40ml, v${modelVersion}`
} else {
return "Unknown model"
}
}



function PioreactorHeader({unit, assignedExperiment, isActive, selectExperiment}) {
function PioreactorHeader({unit, assignedExperiment, isActive, selectExperiment, modelName, modelVersion}) {
const navigate = useNavigate()

const onExperimentClick = () => {
Expand Down Expand Up @@ -341,6 +350,14 @@ function PioreactorHeader({unit, assignedExperiment, isActive, selectExperiment}
{isActive ? "Active" : "Inactive"}
</Box>
</Box>
<Box sx={{display:"inline"}}>
<Box fontWeight="fontWeightBold" sx={{display:"inline-block"}}>
<PioreactorIcon sx={{ fontSize: 14, verticalAlign: "-2px" }}/> Model:&nbsp;
</Box>
<Box fontWeight="fontWeightRegular" sx={{mr: "1%", display:"inline-block"}}>
{modelStringFromModelNameAndModelVersion(modelName, modelVersion)}
</Box>
</Box>

</Typography>
</Box>
Expand Down Expand Up @@ -1495,9 +1512,8 @@ function SettingNumericField(props) {



function UnitCard({unit, experiment, config, isAssignedToExperiment, isActive}){
function UnitCard({unit, experiment, config, isAssignedToExperiment, isActive, modelName}){
const [relabelMap, setRelabelMap] = useState({})

useEffect(() => {

if (experiment){
Expand All @@ -1508,7 +1524,7 @@ function UnitCard({unit, experiment, config, isAssignedToExperiment, isActive}){
return (
<React.Fragment>
<div>
<PioreactorCard isUnitActive={isAssignedToExperiment && isActive} unit={unit} config={config} experiment={experiment} label={relabelMap[unit]}/>
<PioreactorCard modelName={modelName} isUnitActive={isAssignedToExperiment && isActive} unit={unit} config={config} experiment={experiment} label={relabelMap[unit]}/>
</div>
</React.Fragment>
)}
Expand All @@ -1532,6 +1548,7 @@ function FlashLEDButton(props){

function PioreactorCard(props){
const unit = props.unit
const modelName = props.modelName
const isUnitActive = props.isUnitActive
const experiment = props.experiment
const config = props.config
Expand Down Expand Up @@ -1721,7 +1738,7 @@ function PioreactorCard(props){
...(isUnitActive ? {} : { color: disabledColor }),
}}
gutterBottom>
<PioreactorIcon color={isUnitActive ? "inherit" : "disabled"} sx={{verticalAlign: "middle", marginRight: "3px", display: {xs: 'none', sm: 'none', md: 'inline' } }}/>
<PioreactorIconWithModel model={modelName} />
{(label ) ? label : unit }
</Typography>
<Tooltip title={indicatorLabel} placement="right">
Expand Down Expand Up @@ -1925,6 +1942,8 @@ function Pioreactor({title}) {
const {unit} = useParams();
const [assignedExperiment, setAssignedExperiment] = useState(null)
const [isActive, setIsActive] = useState(true)
const [modelName, setModelName] = useState("")
const [modelVersion, setModelVersion] = useState("")
const [error, setError] = useState(null)
const navigate = useNavigate()

Expand Down Expand Up @@ -1953,6 +1972,8 @@ function Pioreactor({title}) {
.then((json) => {
setAssignedExperiment(json['experiment'])
setIsActive(json['is_active'])
setModelName(json['model_name'])
setModelVersion(json['model_version'])
})
.catch((error) => {
setError(error.message);
Expand All @@ -1978,18 +1999,23 @@ function Pioreactor({title}) {
<MQTTProvider name={unit} config={config} experiment={experimentMetadata.experiment}>
<Grid container rowSpacing={1} columnSpacing={2} justifyContent="space-between">
<Grid item md={12} xs={12}>
<PioreactorHeader unit={unit} assignedExperiment={assignedExperiment} isActive={isActive} selectExperiment={selectExperiment}/>
<PioreactorHeader unit={unit} assignedExperiment={assignedExperiment} isActive={isActive} selectExperiment={selectExperiment} modelName={modelName} modelVersion={modelVersion}/>
{experimentMetadata.experiment && assignedExperiment && experimentMetadata.experiment !== assignedExperiment &&
<Box>
<Alert severity="info" style={{marginBottom: '10px', marginTop: '10px'}}>This worker is part of different experiment. Switch to experiment <Chip icon=<PlayCircleOutlinedIcon/> size="small" label={assignedExperiment} clickable onClick={onExperimentClick}/> to control this worker.</Alert>
</Box>
}
</Grid>
<Grid item lg={8} md={12} xs={12}>
<UnitCard isActive={isActive} isAssignedToExperiment={experimentMetadata.experiment === assignedExperiment} unit={unit} experiment={experimentMetadata.experiment} config={config}/>
<UnitCard modelName={modelName} isActive={isActive} isAssignedToExperiment={experimentMetadata.experiment === assignedExperiment} unit={unit} experiment={experimentMetadata.experiment} config={config}/>
</Grid>
<Grid item lg={4} md={12} xs={12}>
{modelName === "pioreactor_20ml" &&
<Bioreactor20Diagram experiment={experimentMetadata.experiment} unit={unit} config={config}/>
}
{modelName === "pioreactor_40ml" &&
<Bioreactor40Diagram experiment={experimentMetadata.experiment} unit={unit} config={config}/>
}
</Grid>

<Grid item xs={12} md={7} container spacing={2} justifyContent="flex-start" style={{height: "100%"}}>
Expand Down
Loading

0 comments on commit 7b07d5e

Please sign in to comment.