@hpcc-js/comms
Version:
hpcc-js - Communications
197 lines (181 loc) • 8.01 kB
text/typescript
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!;
}