Skip to content

Commit

Permalink
[#281] Support failure rate switch
Browse files Browse the repository at this point in the history
E.g. between operationa/predicted/manual failure rate of SNS nodes
  • Loading branch information
Kasmadei authored and blcham committed Jun 7, 2024
1 parent 9e20b4e commit 3561122
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ const useStyles = makeStyles()((theme: Theme) => ({
marginTop: 8,
marginBottom: 8,
},
numberInput: {
"& .MuiInputBase-input": {
color: "black",
padding: "8px 12px",
},
},
}));

export default useStyles;
127 changes: 126 additions & 1 deletion src/components/editor/faultTree/menu/faultEvent/FaultEventMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { useTranslation } from "react-i18next";
import { asArray } from "@utils/utils";
import { ReusableFaultEventsProvider } from "@hooks/useReusableFaultEvents";
import { useSelectedSystem } from "@hooks/useSelectedSystem";
import { Radio, RadioGroup, FormControlLabel, FormControl, TextField } from "@mui/material";

interface Props {
shapeToolData?: FaultEvent;
Expand All @@ -21,6 +22,12 @@ interface Props {
rootIri?: string;
}

enum RadioButtonType {
Predicted = "Predicted",
Manual = "Manual",
Operational = "Operational",
}

const FaultEventMenu = ({ shapeToolData, onEventUpdated, refreshTree, rootIri }: Props) => {
const { t } = useTranslation();
const { classes } = useStyles();
Expand All @@ -38,12 +45,48 @@ const FaultEventMenu = ({ shapeToolData, onEventUpdated, refreshTree, rootIri }:
const [schematicDesignation, setSchematicDesignation] = useState<string | undefined>(undefined);
const [selectedSystem] = useSelectedSystem();

const [snsPredictedFailureRate, setSnsPredictedFailureRate] = useState<number | undefined>(undefined);
const [snsManuallyDefinedFailureRate, setSnsManuallyDefinedFailureRate] = useState<number | undefined>(undefined);
const [snsOperationalFailureRate, setSnsOperationalFailureRate] = useState<number | undefined>(undefined);
const [selectedRadioButton, setSelectedRadioButton] = useState<string>(RadioButtonType.Predicted);

const handleManuallyDefinedFailureRateChange = (event) => {
const inputValue = event.target.value;
const regex = /^[0-9]*\.?[0-9]*$/;
if (regex.test(inputValue)) {
setSnsManuallyDefinedFailureRate(inputValue);
}
};

const handleSnsBasicSelectedFailureRateChange = (event: React.ChangeEvent<HTMLInputElement>) => {
// TODO: Add handler for update with error
setSelectedRadioButton(event.target.value as RadioButtonType);
if (event.target.value === RadioButtonType.Predicted) {
// Updated when we switch to Pred. rate:
onEventUpdated({ ...shapeToolData, probability: snsPredictedFailureRate });
}
if (event.target.value === RadioButtonType.Operational) {
// Updated when we switch to Oper. rate:
onEventUpdated({ ...shapeToolData, probability: snsOperationalFailureRate });
}
};

const handleManualFailureRateUpdate = () => {
onEventUpdated({ ...shapeToolData, probability: snsManuallyDefinedFailureRate });
};

const handleFailureModeClicked = (failureMode: FailureMode) => {
setFailureModeOverview(failureMode);
setFailureModeOverviewDialogOpen(true);
};

useEffect(() => {
// Clear values, after node was changed
setSnsPredictedFailureRate(undefined);
setSnsOperationalFailureRate(undefined);
setSnsOperationalFailureRate(undefined);
setSelectedRadioButton(RadioButtonType.Predicted);

if (shapeToolData?.supertypes?.criticality) {
setCriticality(shapeToolData.supertypes.criticality);
} else {
Expand Down Expand Up @@ -99,6 +142,31 @@ const FaultEventMenu = ({ shapeToolData, onEventUpdated, refreshTree, rootIri }:
} else {
setSchematicDesignation(undefined);
}

if (shapeToolData?.selectedEstimate) {
// SELECTED ESTIMATE => PREDICTED IS SELECTED
setSnsPredictedFailureRate(shapeToolData?.selectedEstimate.value);
setSelectedRadioButton(RadioButtonType.Predicted);
setSnsManuallyDefinedFailureRate(undefined);
} else {
// NO SELECTED ESTIMATE => MANUAL IS SELECTED
setSelectedRadioButton(RadioButtonType.Manual);
if (shapeToolData?.probability) {
setSnsManuallyDefinedFailureRate(shapeToolData.probability);
}
}
let supertypes: any = shapeToolData?.supertypes?.supertypes;
if (supertypes) {
const objectWithHasFailureRate = supertypes.find((obj) => obj.hasOwnProperty("hasFailureRate"));

if (objectWithHasFailureRate?.hasFailureRate?.prediction?.value) {
setSnsPredictedFailureRate(objectWithHasFailureRate?.hasFailureRate?.prediction?.value);
}
if (objectWithHasFailureRate?.hasFailureRate?.estimate?.value) {
setSelectedRadioButton(RadioButtonType.Operational);
setSnsOperationalFailureRate(objectWithHasFailureRate?.hasFailureRate?.estimate?.value);
}
}
}, [shapeToolData]);

const basedFailureRate = shapeToolData?.supertypes?.supertypes?.hasFailureRate?.estimate?.value;
Expand Down Expand Up @@ -169,7 +237,64 @@ const FaultEventMenu = ({ shapeToolData, onEventUpdated, refreshTree, rootIri }:

{/* INTERMEDIATE NODE */}
{shapeToolData && shapeToolData.eventType === EventType.INTERMEDIATE && (
<Box style={{ display: "flex", flexDirection: "row" }}></Box>
<>
{shapeToolData?.probability && (
<Box className={classes.labelRow}>
<Typography>
<span className={classes.label}>Calculated failure rate</span>
{shapeToolData?.probability.toExponential(2)}
</Typography>
</Box>
)}
</>
)}

{shapeToolData && shapeToolData.eventType === EventType.BASIC && (
<>
<Box className={classes.labelRow}>
<FormControl>
<RadioGroup value={selectedRadioButton} onChange={handleSnsBasicSelectedFailureRateChange}>
{snsPredictedFailureRate && (
<Box display={"flex"} flexDirection={"row"} alignItems="center">
<FormControlLabel
value={RadioButtonType.Predicted}
control={<Radio />}
label="Predicted failure rate:"
/>
<Typography>{`${snsPredictedFailureRate}`}</Typography>
</Box>
)}
{snsOperationalFailureRate && (
<Box display={"flex"} flexDirection={"row"} alignItems="center">
<FormControlLabel
value={RadioButtonType.Operational}
control={<Radio />}
label="Operational failure rate:"
/>
<Typography>{`${snsOperationalFailureRate}`}</Typography>
</Box>
)}
<Box display={"flex"} flexDirection={"row"} alignItems="center">
<FormControlLabel
value={RadioButtonType.Manual}
control={<Radio />}
label="Manually defined failure rate:"
/>
<TextField
className={classes.numberInput}
InputLabelProps={{ shrink: false }}
variant="outlined"
value={snsManuallyDefinedFailureRate}
onChange={handleManuallyDefinedFailureRateChange}
inputProps={{ inputMode: "decimal" }}
disabled={selectedRadioButton !== RadioButtonType.Manual}
onBlur={handleManualFailureRateUpdate}
/>
</Box>
</RadioGroup>
</FormControl>
</Box>
</>
)}

{/* EXTERNAL NODE */}
Expand Down
9 changes: 8 additions & 1 deletion src/components/editor/faultTree/shapes/RenderTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,15 @@ const renderTree = async (container, node, parentShape = null, pathsToHighlight)
if (width > DEFAULT_NODE_SHAPE_SIZE) nodeShape.prop("size", { width: width });

nodeShape.attr(["label", "text"], node.name);

// For now it seems impossible to detect when operational failure rate was selected.
// "has" function from lodash can only check property existence with direct path. So we can't show "(o)" in front of "probability"

if (has(node, "probability")) {
nodeShape.attr(["probabilityLabel", "text"], node.probability.toExponential(2));
nodeShape.attr(
["probabilityLabel", "text"],
`${has(node, "selectedEstimate") ? "(p)" : "(m)"}${node.probability.toExponential(2)}`,
);
}
if (has(node, "probabilityRequirement")) {
nodeShape.attr(["probabilityRequirementLabel", "text"], node.probabilityRequirement.toExponential(2));
Expand Down
6 changes: 6 additions & 0 deletions src/models/eventModel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const ctx = {
username: VocabularyUtils.PREFIX + "username",
estimate: VocabularyUtils.PREFIX + "has-estimate",
schematicDesignation: VocabularyUtils.PREFIX + "schematic-designation",
selectedEstimate: VocabularyUtils.PREFIX + "has-selected-estimation",
};

export const CONTEXT = Object.assign({}, ctx, ABSTRACT_CONTEXT, FAILURE_MODE_CONTEXT, RECTANGLE_CONTEXT);
Expand Down Expand Up @@ -81,6 +82,11 @@ export interface FaultEvent extends AbstractModel {
references?: {
isPartOf?: string;
};
selectedEstimate?: {
iri?: string;
types?: string[];
value?: number;
};
supertypes?: {
criticality?: number;
supertypes?: {
Expand Down

0 comments on commit 3561122

Please sign in to comment.