precedence-diagram-method
Version:
An API to calculate the precedence diagram method
209 lines (208 loc) • 9.28 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const DetectLoopInGraph_1 = __importDefault(require("./DetectLoopInGraph"));
class PrecedenceGraph {
constructor(graph, startNodeLabel) {
let graphCopy = JSON.parse(JSON.stringify(graph));
this.startNodeLabel = startNodeLabel;
let hasLoop = PrecedenceGraph.hasLoop(graphCopy);
if (!hasLoop) {
let resettedGraph = PrecedenceGraph.resetCalcedTimesGraph(graphCopy);
let forwardGraph = PrecedenceGraph.calcForwardGraph(resettedGraph, startNodeLabel);
let backwardGraph = PrecedenceGraph.calcBackwardGraph(forwardGraph);
this.graph = backwardGraph;
}
else {
throw new Error("Graph has a directed loop");
}
}
getGraph() {
return this.graph;
}
getStartNode() {
return this.startNodeLabel;
}
static hasLoop(graphJSON) {
let loopDetector = new DetectLoopInGraph_1.default(graphJSON);
return loopDetector.isCyclic();
}
static resetCalcedTimesGraph(graphJSON) {
let copy = JSON.parse(JSON.stringify(graphJSON));
let allNodeLabels = Object.keys(copy);
for (let i = 0; i < allNodeLabels.length; i++) {
let nodeLabel = allNodeLabels[i];
let node = copy[nodeLabel];
delete node.earliestStart;
delete node.earliestEnd;
delete node.latestStart;
delete node.latestEnd;
delete node.buffer;
copy[nodeLabel] = node;
}
return JSON.parse(JSON.stringify(copy));
}
static calcForwardGraph(graph, startNodeLabel) {
let graphJSON = JSON.parse(JSON.stringify(graph));
let startNode = graphJSON[startNodeLabel];
startNode.earliestStart = 0;
startNode.earliestEnd = startNode.duration;
startNode.latestStart = 0;
startNode.latestEnd = startNode.duration;
graphJSON[startNodeLabel] = startNode;
let listOfImpoveableNodes = [startNodeLabel];
while (listOfImpoveableNodes.length > 0) {
let liftOfNextImproveableNodes = [];
for (let i = 0; i < listOfImpoveableNodes.length; i++) {
let parentLabel = listOfImpoveableNodes[i];
let parent = graphJSON[parentLabel];
let children = parent.children || [];
parent.children = children;
graphJSON[parentLabel] = parent;
for (let j = 0; j < children.length; j++) {
let childLabel = children[j];
let child = graphJSON[childLabel];
let parents = child.parents || [];
parents.push(parentLabel);
child.parents = parents;
let childsEarliestStart = child.earliestStart;
if (childsEarliestStart == null || childsEarliestStart < parent.earliestEnd) {
childsEarliestStart = parent.earliestEnd;
liftOfNextImproveableNodes.push(childLabel);
}
child.earliestStart = childsEarliestStart;
child.earliestEnd = childsEarliestStart + child.duration;
graphJSON[childLabel] = child;
}
}
listOfImpoveableNodes = liftOfNextImproveableNodes;
}
return JSON.parse(JSON.stringify(graphJSON));
}
static getFromAllEarliestEndsTheLatest(graph) {
let graphJSON = JSON.parse(JSON.stringify(graph));
let listOfLeafes = PrecedenceGraph.getAllLeafes(graphJSON);
let highestEarliestEnd = 0;
for (let leafLabel of listOfLeafes) {
let node = graphJSON[leafLabel];
let earliestEnd = node.earliestEnd;
if (earliestEnd > highestEarliestEnd) {
highestEarliestEnd = earliestEnd;
}
}
return highestEarliestEnd;
}
static setLatestStartToAllLeafes(graph) {
let graphJSON = JSON.parse(JSON.stringify(graph));
let highestEarliestEnd = PrecedenceGraph.getFromAllEarliestEndsTheLatest(graphJSON);
let listOfLeafes = PrecedenceGraph.getAllLeafes(graphJSON);
for (let leafLabel of listOfLeafes) {
let node = graphJSON[leafLabel];
node.latestEnd = highestEarliestEnd;
graphJSON[leafLabel] = node;
}
return graphJSON;
}
static calcBackwardGraph(graph) {
let graphJSON = PrecedenceGraph.setLatestStartToAllLeafes(graph);
let listOfLeafes = PrecedenceGraph.getAllLeafes(graphJSON);
for (let i = 0; i < listOfLeafes.length; i++) {
let endNodeLabel = listOfLeafes[i];
graphJSON = PrecedenceGraph.calcBackwardGraphForEndlabel(graphJSON, endNodeLabel);
}
return JSON.parse(JSON.stringify(graphJSON));
}
static calcBackwardGraphForEndlabel(graphJSON, endNodeLabel) {
let endNode = graphJSON[endNodeLabel];
if (endNode.latestStart == null) {
endNode.latestStart = endNode.latestEnd - endNode.duration;
endNode.buffer = endNode.latestEnd - endNode.earliestEnd;
}
graphJSON[endNodeLabel] = JSON.parse(JSON.stringify(endNode));
let listOfImpoveableNodes = [endNodeLabel];
while (listOfImpoveableNodes.length > 0) {
let liftOfNextImproveableNodes = [];
for (let i = 0; i < listOfImpoveableNodes.length; i++) {
let childLabel = listOfImpoveableNodes[i];
let child = graphJSON[childLabel];
let parents = child.parents;
if (!!parents) {
for (let j = 0; j < parents.length; j++) {
let parentLabel = parents[j];
let parent = JSON.parse(JSON.stringify(graphJSON[parentLabel]));
let parentsLatestEnd = parent.latestEnd;
if (parentsLatestEnd == null || parentsLatestEnd > child.latestStart) {
parent.latestEnd = child.latestStart;
liftOfNextImproveableNodes.push(parentLabel);
}
parent.latestStart = parent.latestEnd - parent.duration;
parent.buffer = parent.latestEnd - parent.earliestEnd;
graphJSON[parentLabel] = JSON.parse(JSON.stringify(parent));
}
}
}
listOfImpoveableNodes = liftOfNextImproveableNodes;
}
return graphJSON;
}
isCriticalPath(parentId, childId) {
let parent = this.graph[parentId];
let child = this.graph[childId];
return PrecedenceGraph.isCriticalPath(parent, child);
}
static isCriticalPath(parent, child) {
// buffer 0 is not enough
//A --> B && B --> C && A --> C
//crititcal path would be A-->B-->C but we dont want A-->C as critical path
if (child.latestStart === parent.latestEnd && child.buffer === 0 && parent.buffer === 0) {
return true;
}
return false;
}
getCriticalPaths() {
return PrecedenceGraph.getCriticalPaths(this.graph, this.startNodeLabel);
}
static getCriticalPaths(graphJSON, startNodeLabel) {
let criticalPaths = [];
let parent = graphJSON[startNodeLabel];
if (parent.buffer === 0) {
let children = parent.children;
if (children.length > 0) {
for (let i = 0; i < children.length; i++) {
let childLabel = children[i];
let child = graphJSON[childLabel];
if (PrecedenceGraph.isCriticalPath(parent, child)) {
//A --> B && B --> C && A --> C
//crititcal path would be A-->B-->C but we dont want A-->C as critical path
let childsCriticalPaths = PrecedenceGraph.getCriticalPaths(graphJSON, childLabel);
for (let j = 0; j < childsCriticalPaths.length; j++) {
let childsCriticalPath = childsCriticalPaths[j];
let completedChildsCriticalPath = [startNodeLabel].concat(childsCriticalPath);
criticalPaths.push(completedChildsCriticalPath);
}
}
}
return criticalPaths;
}
else {
return [startNodeLabel];
}
}
return criticalPaths;
}
static getAllLeafes(graphJSON) {
let listOfLeafes = [];
let allNodeLabels = Object.keys(graphJSON);
for (let i = 0; i < allNodeLabels.length; i++) {
let nodeLabel = allNodeLabels[i];
let node = graphJSON[nodeLabel];
if (node.children.length === 0) {
listOfLeafes.push(nodeLabel);
}
}
return listOfLeafes;
}
}
exports.default = PrecedenceGraph;