From 2219643e975ed5ac02b0255b9649763c55813f7d Mon Sep 17 00:00:00 2001 From: Jette Petzold Date: Thu, 21 Sep 2023 17:07:46 +0200 Subject: [PATCH] revision (diagram generation) --- .../fta/analysis/fta-cutSet-calculator.ts | 4 +- .../fta/diagram/fta-diagram-generator.ts | 205 ++++++++---------- .../fta/diagram/fta-interfaces.ts | 1 + .../src-language-server/fta/diagram/utils.ts | 45 +--- extension/src-webview/fta-model.ts | 1 + extension/src-webview/fta-views.tsx | 4 +- .../src-webview/options/cut-set-registry.ts | 2 +- extension/webpack.config.js | 2 +- 8 files changed, 107 insertions(+), 157 deletions(-) diff --git a/extension/src-language-server/fta/analysis/fta-cutSet-calculator.ts b/extension/src-language-server/fta/analysis/fta-cutSet-calculator.ts index a2926b72..15816679 100644 --- a/extension/src-language-server/fta/analysis/fta-cutSet-calculator.ts +++ b/extension/src-language-server/fta/analysis/fta-cutSet-calculator.ts @@ -40,7 +40,7 @@ export function determineMinimalCutSet(allNodes: AstNode[]): Set checkIfMinimalCutSet(cutSet, allCutSets)); + const minimalCutSet = allCutSets.filter((cutSet) => checkMinimalCutSet(cutSet, allCutSets)); return minimalCutSet; } @@ -51,7 +51,7 @@ export function determineMinimalCutSet(allNodes: AstNode[]): Set, allCutSets: Set[]): boolean { +function checkMinimalCutSet(cutSet: Set, allCutSets: Set[]): boolean { for (const otherCutSet of allCutSets) { let contained = true; otherCutSet.forEach((element) => { diff --git a/extension/src-language-server/fta/diagram/fta-diagram-generator.ts b/extension/src-language-server/fta/diagram/fta-diagram-generator.ts index 5b8b2e37..6bb2536a 100644 --- a/extension/src-language-server/fta/diagram/fta-diagram-generator.ts +++ b/extension/src-language-server/fta/diagram/fta-diagram-generator.ts @@ -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); } @@ -46,73 +38,55 @@ export class FtaDiagramGenerator extends LangiumDiagramGenerator { */ protected generateRoot(args: GeneratorContext): 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 = 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): SModelElement[] { - const idCache = args.idCache; + private generateEdges(node: AstNode, idCache: IdCache): 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); } } @@ -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 + idCache: IdCache, + label?: string ): FTAEdge { - let children: SModelElement[] = []; - if (label !== "") { - children = [ - { - 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, @@ -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): FTANode { - const idCache = args.idCache; - + private generateFTNode(node: namedFtaElement, idCache: IdCache): 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): SLabel[] { + return [ { 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): SLabel[] { + return [ + { + type: "label:xref", + id: idCache.uniqueId(id + ".label"), + text: label, + }, + ]; } } diff --git a/extension/src-language-server/fta/diagram/fta-interfaces.ts b/extension/src-language-server/fta/diagram/fta-interfaces.ts index eaaa9d6d..39492e56 100644 --- a/extension/src-language-server/fta/diagram/fta-interfaces.ts +++ b/extension/src-language-server/fta/diagram/fta-interfaces.ts @@ -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; diff --git a/extension/src-language-server/fta/diagram/utils.ts b/extension/src-language-server/fta/diagram/utils.ts index 857db595..21976c5d 100644 --- a/extension/src-language-server/fta/diagram/utils.ts +++ b/extension/src-language-server/fta/diagram/utils.ts @@ -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"; /** @@ -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. @@ -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 { - const allGates: Map = 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; -} \ No newline at end of file diff --git a/extension/src-webview/fta-model.ts b/extension/src-webview/fta-model.ts index 631e01b0..2746ea2b 100644 --- a/extension/src-webview/fta-model.ts +++ b/extension/src-webview/fta-model.ts @@ -43,6 +43,7 @@ export class FTANode extends SNode { popupFeature, ]; + name: string; nodeType: FTNodeType = FTNodeType.UNDEFINED; description: string = ""; highlight?: boolean; diff --git a/extension/src-webview/fta-views.tsx b/extension/src-webview/fta-views.tsx index 64e2b435..5e831c02 100644 --- a/extension/src-webview/fta-views.tsx +++ b/extension/src-webview/fta-views.tsx @@ -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; @@ -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 { diff --git a/extension/src-webview/options/cut-set-registry.ts b/extension/src-webview/options/cut-set-registry.ts index 94d7d42e..74a37936 100644 --- a/extension/src-webview/options/cut-set-registry.ts +++ b/extension/src-webview/options/cut-set-registry.ts @@ -69,7 +69,7 @@ export class CutSetsRegistry extends Registry { if (selected === "-") { return "-"; } else { - return selected.split(","); + return selected.split(", "); } } } diff --git a/extension/webpack.config.js b/extension/webpack.config.js index 652892b9..1e9f82a0 100644 --- a/extension/webpack.config.js +++ b/extension/webpack.config.js @@ -58,7 +58,7 @@ const lsConfig = { const commonWebConfig = { target: 'web', mode: "none", // Leave source code as close as possible. Only set to production during distribution. - devtool: 'nosources-source-map', + devtool: 'eval-source-map', resolve: { extensions: ['.ts', '.tsx', '.js'] },