Skip to content

Commit

Permalink
revision (diagram generation)
Browse files Browse the repository at this point in the history
  • Loading branch information
Drakae committed Sep 21, 2023
1 parent 204db0e commit 2219643
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 157 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export function determineMinimalCutSet(allNodes: AstNode[]): Set<namedFtaElement

// Cut sets are minimal if removing one element destroys the cut set
// If cut set contains another cut set from the array, remove it since it is not minimal
const minimalCutSet = allCutSets.filter((cutSet) => checkIfMinimalCutSet(cutSet, allCutSets));
const minimalCutSet = allCutSets.filter((cutSet) => checkMinimalCutSet(cutSet, allCutSets));

return minimalCutSet;
}
Expand All @@ -51,7 +51,7 @@ export function determineMinimalCutSet(allNodes: AstNode[]): Set<namedFtaElement
* @param allCutSets All Cut Sets of the Fault Tree.
* @returns True if the given set is a minimal cut set.
*/
function checkIfMinimalCutSet(cutSet: Set<namedFtaElement>, allCutSets: Set<namedFtaElement>[]): boolean {
function checkMinimalCutSet(cutSet: Set<namedFtaElement>, allCutSets: Set<namedFtaElement>[]): boolean {
for (const otherCutSet of allCutSets) {
let contained = true;
otherCutSet.forEach((element) => {
Expand Down
205 changes: 89 additions & 116 deletions extension/src-language-server/fta/diagram/fta-diagram-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,17 @@
*/

import { AstNode } from "langium";
import { GeneratorContext, LangiumDiagramGenerator } from "langium-sprotty";
import { GeneratorContext, IdCache, LangiumDiagramGenerator } from "langium-sprotty";
import { SLabel, SModelElement, SModelRoot } from "sprotty-protocol";
import {
Component,
Condition,
Gate,
ModelFTA,
TopEvent,
isComponent,
isCondition,
isKNGate
} from "../../generated/ast";
import { ModelFTA, isComponent, isCondition, isKNGate } from "../../generated/ast";
import { FtaServices } from "../fta-module";
import { namedFtaElement } from "../utils";
import { FTAEdge, FTANode } from "./fta-interfaces";
import { FTA_EDGE_TYPE, FTA_NODE_TYPE } from "./fta-model";
import { getAllGateTypes, getFTNodeType, getTargets } from "./utils";
import { getFTNodeType, getTargets } from "./utils";

export class FtaDiagramGenerator extends LangiumDiagramGenerator {
allNodes: AstNode[];
protected allNodes: AstNode[];
constructor(services: FtaServices) {
super(services);
}
Expand All @@ -46,73 +38,55 @@ export class FtaDiagramGenerator extends LangiumDiagramGenerator {
*/
protected generateRoot(args: GeneratorContext<ModelFTA>): SModelRoot {
const { document } = args;
const model: ModelFTA = document.parseResult.value;
//set filter for later maybe

let ftaChildren: SModelElement[] = model.components?.map((comps) => this.generateFTANode(comps, args));

//returns a Map with the gate types as the key and all instances of that type as the value.
const allGates: Map<string, AstNode[]> = getAllGateTypes(model.gates);

//first create the ftaNode for the topevent, conditions and all gates
ftaChildren = ftaChildren.concat([
this.generateFTANode(model.topEvent, args),
...model.conditions?.map((cond) => this.generateFTANode(cond, args)).flat(1),
]);

allGates.forEach((value: AstNode[]) => {
ftaChildren = ftaChildren.concat([
...value?.map((gates) => this.generateFTANode(gates as Gate, args)).flat(1),
]);
});

//after that create the edges of the gates and the top event
allGates.forEach((value: AstNode[]) => {
ftaChildren = ftaChildren.concat([
...value?.map((gates) => this.generateEdgesForFTANode(gates, args)).flat(1),
]);
});
const model = document.parseResult.value;
const idCache = args.idCache;

ftaChildren = ftaChildren.concat([...this.generateEdgesForFTANode(model.topEvent, args)]);
const ftaChildren: SModelElement[] = [
// create nodes for top event, components, conditions, and gates
this.generateFTNode(model.topEvent, idCache),
...model.components.map((component) => this.generateFTNode(component, idCache)),
...model.conditions.map((condition) => this.generateFTNode(condition, idCache)),
...model.gates.map((gate) => this.generateFTNode(gate, idCache)),
// create edges for the gates and the top event
...model.gates.map((gate) => this.generateEdges(gate, idCache)).flat(1),
...this.generateEdges(model.topEvent, idCache),
];

this.allNodes = model.components;
this.allNodes = this.allNodes.concat(model.topEvent, ...model.conditions);
allGates.forEach((value: AstNode[]) => {
this.allNodes = this.allNodes.concat(...value);
});
// TODO: needed? -> look for a better way to access them for the cut set calculator
// save all nodes
this.allNodes = [model.topEvent, ...model.components, ...model.conditions, ...model.gates];

return {
type: "graph",
id: "root",
children: ftaChildren
children: ftaChildren,
};
}

/**
* Getter method for every FTANode in the Fault Tree.
* @returns every FTANode in the Fault Tree.
* Getterfor all nodes of the fault tree.
* @returns all nodes in the fault tree.
*/
public getNodes(): AstNode[] {
return this.allNodes;
}

/**
* Generates the edges for {@code node}.
* Generates the edges for the given {@code node}.
* @param node FTA component for which the edges should be created.
* @param args GeneratorContext of the FTA model.
* @returns edges representing the references {@code node} contains.
* @param idCache The ID cache of the FTA model.
* @returns edges representing the references the given {@code node} contains.
*/
private generateEdgesForFTANode(node: AstNode, args: GeneratorContext<ModelFTA>): SModelElement[] {
const idCache = args.idCache;
private generateEdges(node: AstNode, idCache: IdCache<AstNode>): SModelElement[] {
const elements: SModelElement[] = [];
const sourceId = idCache.getId(node);
// for every reference an edge is created
const targets = getTargets(node);
for (const target of targets) {
const targetId = idCache.getId(target);
const edgeId = idCache.uniqueId(`${sourceId}:-:${targetId}`, undefined);
const edgeId = idCache.uniqueId(`${sourceId}_${targetId}`, undefined);
if (sourceId && targetId) {
const e = this.generateFTAEdge(edgeId, sourceId, targetId, "", args);
const e = this.generateFTEdge(edgeId, sourceId, targetId, idCache);
elements.push(e);
}
}
Expand All @@ -124,27 +98,18 @@ export class FtaDiagramGenerator extends LangiumDiagramGenerator {
* @param edgeId The ID of the edge that should be created.
* @param sourceId The ID of the source of the edge.
* @param targetId The ID of the target of the edge.
* @param idCache The ID cache of the FTA model.
* @param label The label of the edge.
* @param param4 GeneratorContext of the FTA model.
* @returns an FTAEdge.
*/
private generateFTAEdge(
private generateFTEdge(
edgeId: string,
sourceId: string,
targetId: string,
label: string,
{ idCache }: GeneratorContext<ModelFTA>
idCache: IdCache<AstNode>,
label?: string
): FTAEdge {
let children: SModelElement[] = [];
if (label !== "") {
children = [
<SLabel>{
type: "label:xref",
id: idCache.uniqueId(edgeId + ".label"),
text: label,
},
];
}
const children = label ? this.createEdgeLabel(label, edgeId, idCache): [];
return {
type: FTA_EDGE_TYPE,
id: edgeId,
Expand All @@ -158,61 +123,69 @@ export class FtaDiagramGenerator extends LangiumDiagramGenerator {
/**
* Generates a single FTANode for the given {@code node}.
* @param node The FTA component the node should be created for.
* @param args GeneratorContext of the FTA model.
* @param idCache The ID cache of the FTA model.
* @returns a FTANode representing {@code node}.
*/
private generateFTANode(node: TopEvent | Gate | Component | Condition, args: GeneratorContext<ModelFTA>): FTANode {
const idCache = args.idCache;

private generateFTNode(node: namedFtaElement, idCache: IdCache<AstNode>): FTANode {
const nodeId = idCache.uniqueId(node.name, node);
const children: SModelElement[] = this.createNodeLabel(node.name, nodeId, idCache);
const description = isComponent(node) || isCondition(node) ? node.description : "";

const ftNode = {
type: FTA_NODE_TYPE,
id: nodeId,
name: node.name,
nodeType: getFTNodeType(node),
description: description,
children: children,
highlight: true,
layout: "stack",
layoutOptions: {
paddingTop: 10.0,
paddingBottom: 10.0,
paddngLeft: 10.0,
paddingRight: 10.0,
},
} as FTANode;

if (isKNGate(node)) {
ftNode.k = node.k;
ftNode.n = node.children.length;
}
return ftNode;
}

const children: SModelElement[] = [
/**
* Generates SLabel element for the given {@code label} of a node.
* @param label Label to translate to SLabel element.
* @param id The ID of the element for which the label should be generated.
* @param idCache The ID cache of the FTA model.
* @returns SLabel element representing {@code label}.
*/
protected createNodeLabel(label: string, id: string, idCache: IdCache<AstNode>): SLabel[] {
return [
<SLabel>{
type: "label",
id: idCache.uniqueId(nodeId + ".label"),
text: node.name,
id: idCache.uniqueId(id + ".label"),
text: label,
},
];
}

let desc = "";
if (isComponent(node) || isCondition(node)) {
desc = node.description;
}

if (isKNGate(node)) {
return {
type: FTA_NODE_TYPE,
id: nodeId,
nodeType: getFTNodeType(node),
description: desc,
children: children,
highlight: true,
k: node.k,
n: node.children.length,
layout: "stack",
layoutOptions: {
paddingTop: 10.0,
paddingBottom: 10.0,
paddngLeft: 10.0,
paddingRight: 10.0,
},
};
} else {
return {
type: FTA_NODE_TYPE,
id: nodeId,
nodeType: getFTNodeType(node),
description: desc,
children: children,
highlight: true,
layout: "stack",
layoutOptions: {
paddingTop: 10.0,
paddingBottom: 10.0,
paddngLeft: 10.0,
paddingRight: 10.0,
},
};
}
/**
* Generates SLabel element for the given {@code label} of an edge.
* @param label Label to translate to SLabel element.
* @param id The ID of the element for which the label should be generated.
* @param idCache The ID cache of the FTA model.
* @returns SLabel element representing {@code label}.
*/
protected createEdgeLabel(label: string, id: string, idCache: IdCache<AstNode>): SLabel[] {
return [
<SLabel>{
type: "label:xref",
id: idCache.uniqueId(id + ".label"),
text: label,
},
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { FTNodeType } from "./fta-model";
* Node of a fault tree.
*/
export interface FTANode extends SNode {
name: string;
nodeType: FTNodeType;
description: string;
highlight?: boolean;
Expand Down
45 changes: 10 additions & 35 deletions extension/src-language-server/fta/diagram/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,16 @@
*/

import { AstNode } from "langium";
import { isTopEvent, isComponent, isCondition, isGate, isAND, isOR, isKNGate, isInhibitGate, Gate } from "../../generated/ast";
import {
isAND,
isComponent,
isCondition,
isGate,
isInhibitGate,
isKNGate,
isOR,
isTopEvent,
} from "../../generated/ast";
import { FTNodeType } from "./fta-model";

/**
Expand All @@ -43,7 +52,6 @@ export function getFTNodeType(node: AstNode): FTNodeType {
return FTNodeType.UNDEFINED;
}


/**
* Getter for the references contained in {@code node}.
* @param node The AstNode we want the children of.
Expand Down Expand Up @@ -73,36 +81,3 @@ export function getTargets(node: AstNode): AstNode[] {
}
return targets;
}



/** Sorts every gate with its type and puts them into a two dimensional array
* @param gates Every gate within the FTAModel
* @returns A two dimensional array with every gate sorted into the respective category of And, Or, KN, Inhibit-Gate
*/
export function getAllGateTypes(gates: Gate[]): Map<string, AstNode[]> {
const allGates: Map<string, AstNode[]> = new Map();

const andGates: AstNode[] = [];
const orGates: AstNode[] = [];
const kNGates: AstNode[] = [];
const inhibGates: AstNode[] = [];

for (const gate of gates) {
if (isAND(gate)) {
andGates.push(gate);
} else if (isOR(gate)) {
orGates.push(gate);
} else if (isKNGate(gate)) {
kNGates.push(gate);
} else if (isInhibitGate(gate)) {
inhibGates.push(gate);
}
}

allGates.set("AND", andGates);
allGates.set("OR", orGates);
allGates.set("KNGate", kNGates);
allGates.set("InhibitGate", inhibGates);
return allGates;
}
1 change: 1 addition & 0 deletions extension/src-webview/fta-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export class FTANode extends SNode {
popupFeature,
];

name: string;
nodeType: FTNodeType = FTNodeType.UNDEFINED;
description: string = "";
highlight?: boolean;
Expand Down
4 changes: 2 additions & 2 deletions extension/src-webview/fta-views.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export class FTANodeView extends RectangularNodeView {
node.highlight = false;
if (node.nodeType === FTNodeType.COMPONENT || node.nodeType === FTNodeType.CONDITION) {
//node is component or condition and in the selected cut set.
if (set.includes(node.id)) {
if (set.includes(node.name)) {
node.highlight = true;
onlyInCutSet = true;

Expand Down Expand Up @@ -140,7 +140,7 @@ export class FTANodeView extends RectangularNodeView {
for (const edge of node.outgoingEdges) {
let target = (edge.target as FTANode);
if ((target.nodeType === FTNodeType.COMPONENT || target.nodeType === FTNodeType.CONDITION)) {
if (set.includes(target.id)) {
if (set.includes(target.name)) {
return true;
}
} else {
Expand Down
Loading

0 comments on commit 2219643

Please sign in to comment.