From 6c6843df49507f6201a89ea6425b522ba85a91f5 Mon Sep 17 00:00:00 2001 From: Enrico Fasoli Date: Thu, 9 Apr 2015 13:06:31 +0200 Subject: [PATCH] WIP async transformation of the library --- bower.json | 3 +- gulpfile.js | 1 + src/PBSlib.coffee | 173 +++++++++++++++++++++++++---------------- src/app.coffee | 18 +++-- src/controllers.coffee | 56 +++++++------ src/index.html | 1 + 6 files changed, 147 insertions(+), 105 deletions(-) diff --git a/bower.json b/bower.json index f0c2326..9b7fe82 100644 --- a/bower.json +++ b/bower.json @@ -22,6 +22,7 @@ "bootstrap": "~3.3.4", "sweetalert": "~0.5.0", "angular": "~1.3.15", - "angular-ui-router": "~0.2.13" + "angular-ui-router": "~0.2.13", + "async": "~0.9.2" } } diff --git a/gulpfile.js b/gulpfile.js index b41d89e..7c5d339 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -35,6 +35,7 @@ gulp.task('js',function(){ jsFiles = ["src/*.js", "bower_components/jquery/dist/jquery.js", "bower_components/moment/moment.js", + "bower_components/async/lib/async.js", "bower_components/bootstrap/dist/js/bootstrap.js", "bower_components/bootstrap/js/collapse.js", "bower_components/angular/angular.js", diff --git a/src/PBSlib.coffee b/src/PBSlib.coffee index fc5b89e..31388b9 100644 --- a/src/PBSlib.coffee +++ b/src/PBSlib.coffee @@ -1,6 +1,7 @@ class PBS constructor: (@list, @verbose) -> @days = [] + @verbose = yes @criticalPaths = [] log: (x...) -> @@ -25,68 +26,89 @@ class PBS return item # Compute the item's end day - calculateEndDay: (item) => - if !item.startDay? + calculateEndDay: (item,cb) => + if item.endDay? then cb null, item.endDay + else if !item.startDay? @log "calculating start day of",item.id - item.startDay = @calculateStartDay item - @log "start day of",item.id,"is",item.startDay - item.endDay = item.startDay + item.duration - @log "end day of",item.id,"is",item.endDay - @insertDay item.endDay - return item.endDay + @calculateStartDay item, => @calculateEndDay item, cb + else + @log "start day of",item.id,"is",item.startDay + item.endDay = item.startDay + item.duration + @log "end day of",item.id,"is",item.endDay + @insertDay item.endDay + cb null, item.endDay # Find out which day the activity starts - calculateStartDay: (item) => - if !item.depends? or item.depends.length is 0 + calculateStartDay: (item,cb) => + if item.startDay? then cb null, item.startDay + else if !item.depends? or item.depends.length is 0 @insertDay 0 - return item.startDay = 0 - item.startDay = @maxa item.depends.map(@toActivity).map @calculateEndDay - @log "start day of",item.id,"is",item.startDay - # write max delay time to each depend - for j,x of item.depends - @log "checking permittedDelay to dependency", x, "of", item - i = @toActivity x - if !i.dependant? then i.dependant = [item.id] - else i.dependant.push item.id - if !i.permittedDelay? - i.permittedDelay = item.startDay - @calculateEndDay i - @log "written permittedDelay to dependency", x, "of", item, "as", i.permittedDelay - else @log "aborting permittedDelay: already calculated" - @log "permitted delay of",x,"is",i.permittedDelay - @insertDay item.startDay - return item.startDay + cb null, item.startDay = 0 + else + async.map item.depends.map(@toActivity), @calculateEndDay.bind(@), (er, r) => + item.startDay = @maxa r + @log "start day of",item.id,"is",item.startDay + # write max delay time to each depend + checkPermittedDelay = (x,c) => + @log "checking permittedDelay to dependency", x, "of", item + i = @toActivity x + if !i.dependant? then i.dependant = [item.id] + else i.dependant.push item.id + if !i.permittedDelay? + @calculateEndDay i, (e,d) => + i.permittedDelay = item.startDay - d + @log "written permittedDelay to dependency", x, "of", item, "as", i.permittedDelay + @log "permitted delay of",x,"is",i.permittedDelay + c() + else + @log "aborting permittedDelay: already calculated" + @log "permitted delay of",x,"is",i.permittedDelay + c() + async.each item.depends, checkPermittedDelay.bind(@), => + @insertDay item.startDay + cb null, item.startDay - calculateDelays: (item) => - if !item.dependant? or item.dependant.length is 0 then return no - lowestFDelay = 0; fDelay = no; cDelay = 0 - for j,i of item.dependant - x = @toActivity i - if !isNaN(x.permittedDelay) or x.permittedDelay < lowestFDelay or fDelay is no - @log "activity", i, "dependant on", item.id, "has the lowest delay for now ("+(x.permittedDelay or 0)+")" - lowestFDelay = x.permittedDelay or 0 - cDelay = x.chainedDelay or 0 - fDelay = yes - olDelay = item.chainedDelay - item.chainedDelay = lowestFDelay + cDelay - @log "chained delay of", item.id, "is", item.chainedDelay - return item.chainedDelay isnt olDelay + calculateDelays: (item,cb) => + if !item.dependant? or item.dependant.length is 0 then cb null, no + else + lowestFDelay = 0; fDelay = no; cDelay = 0 + checkDependant = (i,c) => + x = @toActivity i + if !isNaN(x.permittedDelay) or x.permittedDelay < lowestFDelay or fDelay is no + @log "activity", i, "dependant on", item.id, "has the lowest delay for now ("+(x.permittedDelay or 0)+")" + lowestFDelay = x.permittedDelay or 0 + cDelay = x.chainedDelay or 0 + fDelay = yes + c() + async.each item.dependant, checkDependant.bind(@), => + olDelay = item.chainedDelay + item.chainedDelay = lowestFDelay + cDelay + @log "chained delay of", item.id, "is", item.chainedDelay + cb null, item.chainedDelay isnt olDelay - calculateCriticalPaths: (path) -> + calculateCriticalPaths: (path,cb) -> @log "calculating path from",path lastID = path[path.length - 1] last = @toActivity lastID if last.dependant? and last.dependant.length > 0 - last.dependant.forEach (x) => + checkDependant = (x,cb2) => ii = @toActivity x delay = ii.permittedDelay or 0 if delay is 0 - @calculateCriticalPaths path.concat x + cb2 null, yes + @calculateCriticalPaths path.concat(x), cb else @log "dead end at", lastID, "-->", x, "because delay is", delay + cb2 null, no + async.each last.dependant, checkDependant.bind(@), (a,b) -> return else - path.forEach (x) => @toActivity(x).critical = yes - @log "calculated path", path - @criticalPaths.push path + setCritical = (x,c) => + @toActivity(x).critical = yes + c null, yes + async.each path, setCritical.bind(@), => + @log "calculated path", path + @criticalPaths.push path + cb null, path # Find out which activity has the highest id highestID: => return @maxa(@list.map (x) -> x.id) @@ -100,26 +122,41 @@ class PBS setData: (data) -> @list = data return @ - + calculate: (options,cb) -> - h = @highestID() - for x,i in @list - @log '('+x.id+'/'+h+')' - @calculateEndDay x - finished = no; i = 0 - while !finished - i++; finished = yes - for x,i in @list - if @calculateDelays x - finished = no - @log "Done calculating delays. Took", i, "iterations" - for x,i in @list - if !x.depends? or x.depends.length is 0 - @calculateCriticalPaths [x.id] - results = activities: @list, days: @days, criticalPaths: @criticalPaths - if options?.json - if cb? then cb(JSON.stringify results) - JSON.stringify results - else - if cb? then cb(results) - results + @log "(Step 1) calculating startDay, endDay, freeDelay" + async.each @list, @calculateEndDay.bind(@), => + @log "(Step 2) Starting chained delay calculations." + cont = yes; i = 0 + notDone = -> + console.log cont + cont + chainedDelayChanged = (x,res) => + i++ + cont = no + @calculateDelays x, (e,r) => + @log "cDelay calc result:",r + res null, r + calculateAllDelays = (cb2) => + async.map @list, chainedDelayChanged, (err,r) -> + iterator = (acc,x,cb3) -> + if x + cb3 null, yes + else cb3 null, acc + async.reduce r, no, iterator, (err,res) -> + cont = res + cb2 null + #TODO: check WHILST + async.whilst notDone, calculateAllDelays, => + @log "Done calculating delays. Took", i, "iterations" + #TODO: check THIS vvvvv + calculateCriticalPathIfApplicable = (x, c) => + if !x.depends? or x.depends.length is 0 + @calculateCriticalPaths [x.id], c + async.each @list, calculateCriticalPathIfApplicable.bind(@), => + results = activities: @list, days: @days, criticalPaths: @criticalPaths + @log "DONE:", results + if options?.json + cb JSON.stringify results + else + cb results diff --git a/src/app.coffee b/src/app.coffee index 46db041..eaedbf9 100644 --- a/src/app.coffee +++ b/src/app.coffee @@ -46,13 +46,15 @@ pertController = ($scope) -> localStorage.setItem 'ganttpert', sdata unless options.silent swal 'Ok', 'Data updated', 'success' - $scope.pbs = new PBS(data).calculate() - $scope.$broadcast 'dataChanged' + $scope.pbs = new PBS(data).calculate {}, -> + $scope.$broadcast 'dataChanged' catch e swal 'Error', e, 'error' - $scope.fromLocalStorage = (options) -> + $scope.fromLocalStorage = (options,cb) -> options = options || {} + if options.call? then cb = options + else unless cb?.call? then return console.log "fromLocalStorage called without callback" data = localStorage.getItem options.name || 'ganttpert' if data is null then data = "[]" try @@ -63,13 +65,15 @@ pertController = ($scope) -> swal 'JSON Error', e, 'error' if options.raw #console.log 'Loading: []' - return [] + cb [] else #console.log 'Loading: {list: [], days: []}' - return list: [], days: [] + cb list: [], days: [] if options.raw #console.log 'Loading: '+jdata - return jdata + cb jdata else #console.log 'Loading: '+$scope.pbs - return $scope.pbs ?= new PBS(jdata).calculate() + new PBS(jdata).calculate (x) -> + $scope.pbs = x + cb x diff --git a/src/controllers.coffee b/src/controllers.coffee index f50b620..1697c9d 100644 --- a/src/controllers.coffee +++ b/src/controllers.coffee @@ -1,9 +1,8 @@ pertApp.controller 'tableController', ($scope) -> $scope.list = [] $scope.refreshTable = -> - ls = $scope.fromLocalStorage() - if ls? - $scope.list = ls.activities + $scope.fromLocalStorage (ls) -> + if ls? then $scope.list = ls.activities $scope.$on 'dataChanged', $scope.refreshTable $scope.refreshTable() @@ -32,7 +31,7 @@ pertApp.controller 'pertDiagController', ($scope) -> network = new vis.Network (document.getElementById 'pertDiagram'), { nodes: nodes, edges: connections }, options $scope.rebuild = -> console.log 'rebuild' - $scope.buildGraph $scope.fromLocalStorage() + $scope.fromLocalStorage (r) -> $scope.buildGraph r $scope.$on 'dataChanged', $scope.rebuild $scope.rebuild() @@ -47,8 +46,8 @@ pertApp.controller 'ganttDiagController', ($scope) -> if !data? then return timeline = new vis.Timeline (document.getElementById 'timeline'), ($scope.toDates data.activities), {} $scope.$on 'dataChanged', -> - $scope.buildTimeline $scope.fromLocalStorage() - $scope.buildTimeline $scope.fromLocalStorage() + $scope.fromLocalStorage (r) -> $scope.buildTimeline r + $scope.fromLocalStorage (r) -> $scope.buildTimeline r pertApp.controller 'rawEditorController', ($scope) -> $scope.reset = -> @@ -60,10 +59,9 @@ pertApp.controller 'rawEditorController', ($scope) -> swal 'Invalid Data', e, 'error' $scope.toLocalStorage data $scope.reloadData = -> - $scope.taData = JSON.stringify $scope.fromLocalStorage silent: yes, raw: yes - $scope.$on 'dataChanged', -> - $scope.reloadData() - #$('#ta').val JSON.stringify $scope.fromLocalStorage silent: yes, raw: yes + $scope.fromLocalStorage { silent: yes, raw: yes }, (x) -> + $scope.taData = JSON.stringify x + $scope.$on 'dataChanged', -> $scope.reloadData() $scope.reloadData() pertApp.controller 'editorController', ($scope) -> @@ -77,18 +75,18 @@ pertApp.controller 'editorController', ($scope) -> swal 'Ops', 'could not find '+id, 'warning' $scope.delete = (index,id) -> - newdata = $scope.fromLocalStorage raw: yes - l = [] - if id? then for i,j of newdata - if id isnt j.id - l.push j - else for i,j of newdata - if parseInt(i) isnt index - l.push j - diff = newdata.length - l.length - $scope.toLocalStorage l, silent: yes - if diff isnt 1 - swal 'Done', diff+' item(s) deleted', 'warning' + $scope.fromLocalStorage { raw: yes }, (newdata) -> + l = [] + if id? then for i,j of newdata + if id isnt j.id + l.push j + else for i,j of newdata + if parseInt(i) isnt index + l.push j + diff = newdata.length - l.length + $scope.toLocalStorage l, silent: yes + if diff isnt 1 + swal 'Done', diff+' item(s) deleted', 'warning' $scope.addNew = (id, dur, deps) -> dur ?= $('#new-duration').val().trim() @@ -109,14 +107,14 @@ pertApp.controller 'editorController', ($scope) -> unless isNaN dep deps[i] = parseInt dep catch e - newdata = $scope.fromLocalStorage silent: yes, raw: yes - if !newdata? or newdata is null or !newdata.push? - newdata = [] - newdata.push { id: id, duration: dur, depends: deps } - $scope.toLocalStorage newdata, silent: yes + $scope.fromLocalStorage { silent: yes, raw: yes }, (newdata) -> + if !newdata? or newdata is null or !newdata.push? + newdata = [] + newdata.push { id: id, duration: dur, depends: deps } + $scope.toLocalStorage newdata, silent: yes $scope.refreshEditor = -> - data = $scope.fromLocalStorage { silent: yes, raw: yes } - $scope.list = data || [] + $scope.fromLocalStorage { silent: yes, raw: yes }, (data) -> + $scope.list = data || [] $scope.$on 'dataChanged', $scope.refreshEditor $scope.refreshEditor() diff --git a/src/index.html b/src/index.html index 47df66a..18f044d 100644 --- a/src/index.html +++ b/src/index.html @@ -9,6 +9,7 @@ +