UNPKG

@hpcc-js/comms

Version:
197 lines (181 loc) 8.01 kB
import { Cache, Edge, Graph, StateObject, StringAnyMap, Subgraph, Vertex, XMLNode } from "@hpcc-js/util"; import { WsWorkunits } from "../services/wsWorkunits.ts"; import { BaseScope } from "./scope.ts"; import { Timer } from "./timer.ts"; import { Workunit } from "./workunit.ts"; export interface ECLGraphEx extends WsWorkunits.ECLGraph { Time: number; } export class ECLGraph extends StateObject<ECLGraphEx, ECLGraphEx> implements ECLGraphEx { protected wu: Workunit; get properties(): ECLGraphEx { return this.get(); } get Name(): string { return this.get("Name"); } get Label(): string { return this.get("Label"); } get Type(): string { return this.get("Type"); } get Complete(): boolean { return this.get("Complete"); } get WhenStarted(): string { return this.get("WhenStarted"); } get WhenFinished(): string { return this.get("WhenFinished"); } get Time(): number { return this.get("Time"); } get Running(): boolean { return this.get("Running"); } get RunningId(): number { return this.get("RunningId"); } get Failed(): boolean { return this.get("Failed"); } constructor(wu: Workunit, eclGraph: WsWorkunits.ECLGraph, eclTimers: Timer[]) { super(); this.wu = wu; let duration = 0; for (const eclTimer of eclTimers) { if (eclTimer.GraphName === eclGraph.Name && !eclTimer.HasSubGraphId) { duration = Math.round(eclTimer.Seconds * 1000) / 1000; break; } } this.set({ Time: duration, ...eclGraph }); } fetchScopeGraph(subgraphID?: string): Promise<ScopeGraph> { if (subgraphID) { return this.wu.fetchGraphDetails([subgraphID], ["subgraph"]).then((scopes) => { return createGraph(scopes); }); } return this.wu.fetchGraphDetails([this.Name], ["graph"]).then((scopes) => { return createGraph(scopes); }); } } export class GraphCache extends Cache<WsWorkunits.ECLGraph, ECLGraph> { constructor() { super((obj) => { return Cache.hash([obj.Name]); }); } } type Callback = (tag: string, attributes: StringAnyMap, children: XMLNode[], _stack: XMLNode[]) => void; function walkXmlJson(node: XMLNode, callback: Callback, stack?: XMLNode[]) { stack = stack || []; stack.push(node); callback(node.name, node.$, node.children(), stack); node.children().forEach((childNode) => { walkXmlJson(childNode, callback, stack); }); stack.pop(); } function flattenAtt(nodes: XMLNode[]): StringAnyMap { const retVal: StringAnyMap = {}; nodes.forEach((node: XMLNode) => { if (node.name === "att") { retVal[node.$["name"]] = node.$["value"]; } }); return retVal; } export class XGMMLGraph extends Graph<StringAnyMap, StringAnyMap, StringAnyMap> { } export class XGMMLSubgraph extends Subgraph<StringAnyMap, StringAnyMap, StringAnyMap> { } export class XGMMLVertex extends Vertex<StringAnyMap, StringAnyMap, StringAnyMap> { } export class XGMMLEdge extends Edge<StringAnyMap, StringAnyMap, StringAnyMap> { } export function createXGMMLGraph(id: string, graphs: XMLNode): XGMMLGraph { const subgraphs: { [id: string]: XGMMLSubgraph } = {}; const vertices: { [id: string]: XGMMLVertex } = {}; const edges: { [id: string]: XGMMLEdge } = {}; const graph = new XGMMLGraph((item) => { return item._!["id"]; }); const stack: XGMMLSubgraph[] = [graph.root]; walkXmlJson(graphs, (tag: string, attributes: StringAnyMap, childNodes: XMLNode[], _stack) => { const top = stack[stack.length - 1]; switch (tag) { case "graph": break; case "node": if (childNodes.length && childNodes[0].children().length && childNodes[0].children()[0].name === "graph") { const subgraph = top.createSubgraph(flattenAtt(childNodes)); stack.push(subgraph); subgraphs[attributes["id"]] = subgraph; } else { } // TODO: Is this really a node when its also a subgraph? const vertex = top.createVertex(flattenAtt(childNodes)); vertices[attributes["id"]] = vertex; break; case "edge": const edge = top.createEdge(vertices[attributes["source"]], vertices[attributes["target"]], flattenAtt(childNodes)); edges[attributes["id"]] = edge; break; default: } }); return graph; } export class ScopeGraph extends Graph<BaseScope, BaseScope, BaseScope> { } export class ScopeSubgraph extends Subgraph<BaseScope, BaseScope, BaseScope> { } export class ScopeVertex extends Vertex<BaseScope, BaseScope, BaseScope> { } export class ScopeEdge extends Edge<BaseScope, BaseScope, BaseScope> { } export function createGraph(scopes: BaseScope[]): ScopeGraph { const subgraphs: { [scopeName: string]: ScopeSubgraph } = {}; const edges: { [scopeName: string]: BaseScope } = {}; const vertices: { [scopeName: string]: ScopeVertex } = {}; let graph: ScopeGraph | undefined; for (const scope of scopes) { switch (scope.ScopeType) { case "graph": graph = new ScopeGraph(item => item._!.Id, scope); subgraphs[scope.ScopeName] = graph.root; break; case "subgraph": if (!graph) { graph = new ScopeGraph(item => item._!.Id, scope); subgraphs[scope.ScopeName] = graph.root; } const scopeStack = scope.parentScope().split(":"); let scopeParent1 = subgraphs[scope.parentScope()]; while (scopeStack.length && !scopeParent1) { scopeParent1 = subgraphs[scopeStack.join(":")]; scopeStack.pop(); } if (!scopeParent1) { console.warn(`Missing SG:Parent (${scope.Id}): ${scope.parentScope()}`); } else { const parent1: ScopeSubgraph = scopeParent1; subgraphs[scope.ScopeName] = parent1.createSubgraph(scope); } break; case "activity": const scopeParent2 = subgraphs[scope.parentScope()]; if (!scopeParent2) { console.warn(`Missing A:Parent (${scope.Id}): ${scope.parentScope()}`); } else { vertices[scope.ScopeName] = scopeParent2.createVertex(scope); } break; case "edge": edges[scope.ScopeName] = scope; break; case "function": const scopeParent3 = vertices[scope.parentScope()]; if (!scopeParent3) { console.warn(`Missing F:Parent (${scope.Id}): ${scope.parentScope()}`); } else { scopeParent3._.children().push(scope); } break; } } for (const id in edges) { const scope = edges[id]; const scopeParent3 = subgraphs[scope.parentScope()]; if (!scopeParent3) { console.warn(`Missing E:Parent (${scope.Id}): ${scope.parentScope()}`); } else { const parent3: ScopeSubgraph = scopeParent3; try { const source = graph!.vertex(scope.attr("IdSource").RawValue); const target = graph!.vertex(scope.attr("IdTarget").RawValue); parent3.createEdge(source, target, scope); } catch (e) { // const sourceIndex = scope.attr("SourceIndex").RawValue; // const targetIndex = scope.attr("TargetIndex").RawValue; console.warn(`Invalid Edge: ${id}`); } } } return graph!; }