fable 3.0.116 → 3.0.118
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/debug/Example-ProgressTime.js +89 -0
- package/debug/Harness.js +14 -60
- package/dist/fable.compatible.js +185 -178
- package/dist/fable.compatible.min.js +2 -2
- package/dist/fable.compatible.min.js.map +1 -1
- package/dist/fable.js +131 -124
- package/dist/fable.min.js +2 -2
- package/dist/fable.min.js.map +1 -1
- package/package.json +1 -1
- package/source/services/Fable-Service-Operation-DefaultSettings.js +16 -21
- package/source/services/Fable-Service-Operation.js +82 -61
- package/source/services/Fable-Service-ProgressTime.js +5 -0
- package/source/services/Fable-Service-ProgressTracker.js +129 -15
- package/test/FableOperation_tests.js +61 -11
package/package.json
CHANGED
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
module.exports = (
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
{
|
|
3
|
+
"Metadata": {
|
|
4
|
+
"UUID": false,
|
|
5
|
+
"Hash": false,
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
"Name": "",
|
|
8
|
+
"Summary": "",
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
},
|
|
21
|
-
"Steps": [],
|
|
22
|
-
"Errors": [],
|
|
23
|
-
"Log": []
|
|
24
|
-
}
|
|
10
|
+
"Version": 0
|
|
11
|
+
},
|
|
12
|
+
"Status": {
|
|
13
|
+
"Completed": false,
|
|
14
|
+
"StepCount": 0
|
|
15
|
+
},
|
|
16
|
+
"Steps": [],
|
|
17
|
+
"Errors": [],
|
|
18
|
+
"Log": []
|
|
19
|
+
}
|
|
25
20
|
);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { PE } = require('big.js');
|
|
1
2
|
const libFableServiceBase = require('fable-serviceproviderbase');
|
|
2
3
|
|
|
3
4
|
const _OperationStatePrototypeString = JSON.stringify(require('./Fable-Service-Operation-DefaultSettings.js'));
|
|
@@ -12,9 +13,6 @@ class FableOperation extends libFableServiceBase
|
|
|
12
13
|
// Timestamps will just be the long ints
|
|
13
14
|
this.timeStamps = {};
|
|
14
15
|
|
|
15
|
-
// ProgressTrackers have an object format of: {Hash:'SomeHash',EndTime:UINT,CurrentTime:UINT,TotalCount:INT,CurrentCount:INT}
|
|
16
|
-
this.progressTrackers = {};
|
|
17
|
-
|
|
18
16
|
this.serviceType = 'PhasedOperation';
|
|
19
17
|
|
|
20
18
|
this.state = JSON.parse(_OperationStatePrototypeString);
|
|
@@ -29,113 +27,136 @@ class FableOperation extends libFableServiceBase
|
|
|
29
27
|
this.state.Metadata.Name = (typeof(this.options.Name) == 'string') ? this.options.Name : `Unnamed Operation ${this.state.Metadata.UUID}`;
|
|
30
28
|
this.name = this.state.Metadata.Name;
|
|
31
29
|
|
|
30
|
+
this.progressTrackers = this.fable.instantiateServiceProviderWithoutRegistration('ProgressTracker');
|
|
31
|
+
|
|
32
|
+
this.state.OverallProgressTracker = this.progressTrackers.createProgressTracker(`Overall-${this.state.Metadata.UUID}`);
|
|
33
|
+
|
|
34
|
+
// This is here to use the pass-through logging functions in the operation itself.
|
|
32
35
|
this.log = this;
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
execute(fExecutionCompleteCallback)
|
|
36
39
|
{
|
|
37
40
|
// TODO: Should the same operation be allowed to execute more than one time?
|
|
38
|
-
if (this.state.
|
|
41
|
+
if (this.state.OverallProgressTracker.StartTimeStamp > 0)
|
|
39
42
|
{
|
|
40
43
|
return fExecutionCompleteCallback(new Error(`Operation [${this.state.Metadata.UUID}] ${this.state.Metadata.Name} has already been executed!`));
|
|
41
44
|
}
|
|
42
45
|
|
|
43
|
-
this.state.Status.TimeStart = +new Date();
|
|
44
|
-
|
|
45
46
|
let tmpAnticipate = this.fable.instantiateServiceProviderWithoutRegistration('Anticipate');
|
|
47
|
+
|
|
48
|
+
this.progressTrackers.setProgressTrackerTotalOperations(this.state.OverallProgressTracker.Hash, this.state.Status.StepCount);
|
|
49
|
+
this.progressTrackers.startProgressTracker(this.state.OverallProgressTracker.Hash);
|
|
50
|
+
this.info(`Operation [${this.state.Metadata.UUID}] ${this.state.Metadata.Name} starting...`);
|
|
46
51
|
|
|
47
|
-
for (let i = 0; i < this.state.Steps; i++)
|
|
52
|
+
for (let i = 0; i < this.state.Steps.length; i++)
|
|
48
53
|
{
|
|
49
|
-
tmpAnticipate.anticipate(
|
|
54
|
+
tmpAnticipate.anticipate(
|
|
55
|
+
function(fNext)
|
|
56
|
+
{
|
|
57
|
+
this.fable.log.info(`Step #${i} [${this.state.Steps[i].GUIDStep}] ${this.state.Steps[i].Name} starting...`);
|
|
58
|
+
this.progressTrackers.startProgressTracker(this.state.Steps[i].ProgressTracker.Hash);
|
|
59
|
+
return fNext();
|
|
60
|
+
}.bind(this));
|
|
61
|
+
// Steps are executed in a custom context with
|
|
62
|
+
tmpAnticipate.anticipate(this.stepFunctions[this.state.Steps[i].GUIDStep].bind(
|
|
63
|
+
{
|
|
64
|
+
log:this,
|
|
65
|
+
fable:this.fable,
|
|
66
|
+
options:this.state.Steps[i].Metadata,
|
|
67
|
+
metadata:this.state.Steps[i].Metadata,
|
|
68
|
+
ProgressTracker:this.progressTrackers.getProgressTracker(this.state.Steps[i].ProgressTracker.Hash),
|
|
69
|
+
updateProgressTracker: function(pProgressAmount)
|
|
70
|
+
{
|
|
71
|
+
return this.progressTrackers.updateProgressTracker(this.state.Steps[i].ProgressTracker.Hash, pProgressAmount);
|
|
72
|
+
}.bind(this),
|
|
73
|
+
incrementProgressTracker: function(pProgressIncrementAmount)
|
|
74
|
+
{
|
|
75
|
+
return this.progressTrackers.incrementProgressTracker(this.state.Steps[i].ProgressTracker.Hash, pProgressIncrementAmount);
|
|
76
|
+
}.bind(this),
|
|
77
|
+
setProgressTrackerTotalOperations: function(pTotalOperationCount)
|
|
78
|
+
{
|
|
79
|
+
return this.progressTrackers.setProgressTrackerTotalOperations(this.state.Steps[i].ProgressTracker.Hash, pTotalOperationCount);
|
|
80
|
+
}.bind(this),
|
|
81
|
+
getProgressTrackerStatusString: function()
|
|
82
|
+
{
|
|
83
|
+
return this.progressTrackers.getProgressTrackerStatusString(this.state.Steps[i].ProgressTracker.Hash);
|
|
84
|
+
}.bind(this),
|
|
85
|
+
logProgressTrackerStatus: function()
|
|
86
|
+
{
|
|
87
|
+
return this.log.info(`Step #${i} [${this.state.Steps[i].GUIDStep}]: ${this.progressTrackers.getProgressTrackerStatusString(this.state.Steps[i].ProgressTracker.Hash)}`);
|
|
88
|
+
}.bind(this),
|
|
89
|
+
OperationState:this.state,
|
|
90
|
+
StepState:this.state.Steps[i]
|
|
91
|
+
}));
|
|
92
|
+
tmpAnticipate.anticipate(
|
|
93
|
+
function(fNext)
|
|
94
|
+
{
|
|
95
|
+
this.progressTrackers.endProgressTracker(this.state.Steps[i].ProgressTracker.Hash);
|
|
96
|
+
let tmpStepTimingMessage = this.progressTrackers.getProgressTrackerStatusString(this.state.Steps[i].ProgressTracker.Hash);
|
|
97
|
+
this.fable.log.info(`Step #${i} [${this.state.Steps[i].GUIDStep}] ${this.state.Steps[i].Name} complete.`);
|
|
98
|
+
this.fable.log.info(`Step #${i} [${this.state.Steps[i].GUIDStep}] ${this.state.Steps[i].Name} ${tmpStepTimingMessage}.`);
|
|
99
|
+
|
|
100
|
+
this.progressTrackers.incrementProgressTracker(this.state.OverallProgressTracker.Hash, 1);
|
|
101
|
+
let tmpOperationTimingMessage = this.progressTrackers.getProgressTrackerStatusString(this.state.OverallProgressTracker.Hash);
|
|
102
|
+
this.fable.log.info(`Operation [${this.state.Metadata.UUID}] ${tmpOperationTimingMessage}.`);
|
|
103
|
+
return fNext();
|
|
104
|
+
}.bind(this));
|
|
50
105
|
}
|
|
51
106
|
|
|
52
107
|
// Wait for the anticipation to complete
|
|
53
108
|
tmpAnticipate.wait(
|
|
54
109
|
(pError) =>
|
|
55
110
|
{
|
|
56
|
-
|
|
111
|
+
if (pError)
|
|
112
|
+
{
|
|
113
|
+
this.fable.log.error(`Operation [${this.state.Metadata.UUID}] ${this.state.Metadata.Name} had an error: ${pError}`, pError);
|
|
114
|
+
return fExecutionCompleteCallback(pError);
|
|
115
|
+
}
|
|
116
|
+
this.info(`Operation [${this.state.Metadata.UUID}] ${this.state.Metadata.Name} complete.`);
|
|
117
|
+
let tmpOperationTimingMessage = this.progressTrackers.getProgressTrackerStatusString(this.state.OverallProgressTracker.Hash);
|
|
118
|
+
this.progressTrackers.endProgressTracker(this.state.OverallProgressTracker.Hash);
|
|
119
|
+
this.fable.log.info(`Operation [${this.state.Metadata.UUID}] ${tmpOperationTimingMessage}.`);
|
|
57
120
|
return fExecutionCompleteCallback();
|
|
58
121
|
});
|
|
59
122
|
}
|
|
60
123
|
|
|
61
|
-
|
|
62
|
-
TODO: I've gone back and forth on whether this should be an object, JSON
|
|
63
|
-
object prototype, or set of functions here. Discuss with colleagues!
|
|
64
|
-
*/
|
|
65
|
-
addStep(fStepFunction, pStepName, pStepDescription, pStepMetadata, pGUIDStep)
|
|
124
|
+
addStep(fStepFunction, pStepMetadata, pStepName, pStepDescription, pGUIDStep)
|
|
66
125
|
{
|
|
67
126
|
let tmpStep = {};
|
|
68
127
|
|
|
69
128
|
// GUID is optional
|
|
70
129
|
tmpStep.GUIDStep = (typeof(pGUIDStep) !== 'undefined') ? pGUIDStep : `STEP-${this.state.Steps.length}-${this.fable.DataGeneration.randomNumericString()}`;
|
|
71
130
|
|
|
131
|
+
|
|
72
132
|
// Name is optional
|
|
73
133
|
tmpStep.Name = (typeof(pStepName) !== 'undefined') ? pStepName : `Step [${tmpStep.GUIDStep}]`;
|
|
74
134
|
tmpStep.Description = (typeof(pStepDescription) !== 'undefined') ? pStepDescription : `Step execution of ${tmpStep.Name}.`;
|
|
75
135
|
|
|
76
|
-
tmpStep.
|
|
136
|
+
tmpStep.ProgressTracker = this.progressTrackers.createProgressTracker(tmpStep.GUIDStep);
|
|
77
137
|
|
|
78
|
-
tmpStep.
|
|
79
|
-
tmpStep.TimeEnd = false;
|
|
138
|
+
tmpStep.Metadata = (typeof(pStepMetadata) === 'object') ? pStepMetadata : {};
|
|
80
139
|
|
|
81
140
|
// There is an array of steps, in the Operation State itself ... push a step there
|
|
82
141
|
this.state.Steps.push(tmpStep);
|
|
83
142
|
|
|
84
|
-
this.
|
|
143
|
+
this.stepMap[tmpStep.GUIDStep] = tmpStep;
|
|
85
144
|
|
|
86
|
-
this.
|
|
145
|
+
this.stepFunctions[tmpStep.GUIDStep] = typeof(fStepFunction) == 'function' ? fStepFunction : function (fDone) { return fDone(); };
|
|
87
146
|
|
|
88
147
|
this.state.Status.StepCount++;
|
|
89
148
|
|
|
90
149
|
return tmpStep;
|
|
91
150
|
}
|
|
92
151
|
|
|
93
|
-
|
|
94
|
-
* Retrieves a step from the step map based on the provided GUID.
|
|
95
|
-
* @param {string} pGUIDStep - The GUID of the step to retrieve.
|
|
96
|
-
* @returns {object|boolean} - The step object if found, otherwise false.
|
|
97
|
-
*/
|
|
98
|
-
getStep(pGUIDStep)
|
|
152
|
+
setStepTotalOperations(pGUIDStep, pTotalOperationCount)
|
|
99
153
|
{
|
|
100
|
-
if (this.stepMap.hasOwnProperty(pGUIDStep))
|
|
154
|
+
if (!this.stepMap.hasOwnProperty(pGUIDStep))
|
|
101
155
|
{
|
|
102
|
-
return
|
|
156
|
+
return new Error(`Step [${pGUIDStep}] does not exist in operation [${this.state.Metadata.UUID}] ${this.state.Metadata.Name} when attempting to set total operations to ${pTotalOperationCount}.`);
|
|
103
157
|
}
|
|
104
158
|
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Begins a step in the Fable service operation.
|
|
110
|
-
* @param {string} pGUIDStep - The GUID of the step to begin.
|
|
111
|
-
* @returns {object|boolean} - The step object if found, or `false` if not found.
|
|
112
|
-
*/
|
|
113
|
-
beginStep(pGUIDStep)
|
|
114
|
-
{
|
|
115
|
-
let tmpStep = this.getStep(pGUIDStep);
|
|
116
|
-
|
|
117
|
-
if (tmpStep === false)
|
|
118
|
-
{
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
tmpStep.TimeStart = +new Date();
|
|
123
|
-
|
|
124
|
-
return tmpStep;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
endStep(pGUIDStep)
|
|
128
|
-
{
|
|
129
|
-
let tmpStep = this.getStep(pGUIDStep);
|
|
130
|
-
|
|
131
|
-
if (tmpStep === false)
|
|
132
|
-
{
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
tmpStep.TimeEnd = +new Date();
|
|
137
|
-
|
|
138
|
-
return tmpStep;
|
|
159
|
+
this.progressTrackers.setProgressTrackerTotalOperations(this.stepMap[pGUIDStep].ProgressTracker.Hash, pTotalOperationCount);
|
|
139
160
|
}
|
|
140
161
|
|
|
141
162
|
writeOperationLog(pLogLevel, pLogText, pLogObject)
|
|
@@ -15,6 +15,11 @@ class FableServiceProgressTime extends libFableServiceBase
|
|
|
15
15
|
{
|
|
16
16
|
let tmpTimeDuration = typeof(pTimeDurationInMilliseconds) == 'number' ? pTimeDurationInMilliseconds : 0;
|
|
17
17
|
|
|
18
|
+
if (pTimeDurationInMilliseconds < 0)
|
|
19
|
+
{
|
|
20
|
+
return 'unknown';
|
|
21
|
+
}
|
|
22
|
+
|
|
18
23
|
let tmpTimeDurationString = '';
|
|
19
24
|
if (tmpTimeDuration > 3600000)
|
|
20
25
|
{
|
|
@@ -23,7 +23,7 @@ class FableServiceProgressTracker extends libFableServiceBase
|
|
|
23
23
|
|
|
24
24
|
if (!this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash))
|
|
25
25
|
{
|
|
26
|
-
this.fable.log.warn(`
|
|
26
|
+
this.fable.log.warn(`ProgressTracker ${tmpProgressTrackerHash} does not exist! Creating a new tracker...`);
|
|
27
27
|
this.createProgressTracker(tmpProgressTrackerHash, 100);
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -46,6 +46,8 @@ class FableServiceProgressTracker extends libFableServiceBase
|
|
|
46
46
|
EndTimeStamp: -1,
|
|
47
47
|
|
|
48
48
|
PercentComplete: -1,
|
|
49
|
+
// If this is set to true, PercentComplete will be calculated as CurrentCount / TotalCount even if it goes over 100%
|
|
50
|
+
AllowTruePercentComplete: false,
|
|
49
51
|
|
|
50
52
|
ElapsedTime: -1,
|
|
51
53
|
AverageOperationTime: -1,
|
|
@@ -58,7 +60,7 @@ class FableServiceProgressTracker extends libFableServiceBase
|
|
|
58
60
|
|
|
59
61
|
if (this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash))
|
|
60
62
|
{
|
|
61
|
-
this.fable.log.warn(`
|
|
63
|
+
this.fable.log.warn(`ProgressTracker ${tmpProgressTrackerHash} already exists! Overwriting with a new tracker...`);
|
|
62
64
|
this.progressTimes.removeTimeStamp(tmpProgressTracker.StartTimeHash);
|
|
63
65
|
this.progressTimes.removeTimeStamp(tmpProgressTracker.EndTimeHash);
|
|
64
66
|
}
|
|
@@ -68,11 +70,27 @@ class FableServiceProgressTracker extends libFableServiceBase
|
|
|
68
70
|
return tmpProgressTracker;
|
|
69
71
|
}
|
|
70
72
|
|
|
73
|
+
setProgressTrackerTotalOperations(pProgressTrackerHash, pTotalOperations)
|
|
74
|
+
{
|
|
75
|
+
let tmpProgressTrackerHash = (typeof(pProgressTrackerHash) == 'string') ? pProgressTrackerHash : 'Default';
|
|
76
|
+
let tmpTotalOperations = (typeof(pTotalOperations) == 'number') ? pTotalOperations : 100;
|
|
77
|
+
|
|
78
|
+
if (!this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash))
|
|
79
|
+
{
|
|
80
|
+
this.fable.log.warn(`Attempted to set the total operations of ProgressTracker ${tmpProgressTrackerHash} but it does not exist! Creating a new tracker...`);
|
|
81
|
+
this.createProgressTracker(tmpProgressTrackerHash, tmpTotalOperations);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
this.progressTrackers[tmpProgressTrackerHash].TotalCount = tmpTotalOperations;
|
|
85
|
+
|
|
86
|
+
return this.progressTrackers[tmpProgressTrackerHash];
|
|
87
|
+
}
|
|
88
|
+
|
|
71
89
|
startProgressTracker(pProgressTrackerHash)
|
|
72
90
|
{
|
|
73
91
|
let tmpProgressTrackerHash = (typeof(pProgressTrackerHash) == 'string') ? pProgressTrackerHash : 'Default';
|
|
74
92
|
|
|
75
|
-
// This is the only method to lazily create
|
|
93
|
+
// This is the only method to lazily create ProgressTrackers now
|
|
76
94
|
if (!this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash))
|
|
77
95
|
{
|
|
78
96
|
this.createProgressTracker(tmpProgressTrackerHash, 100);
|
|
@@ -82,6 +100,10 @@ class FableServiceProgressTracker extends libFableServiceBase
|
|
|
82
100
|
|
|
83
101
|
this.progressTimes.createTimeStamp(this.progressTrackers[tmpProgressTrackerHash].StartTimeHash);
|
|
84
102
|
tmpProgressTracker.StartTimeStamp = this.progressTimes.getTimeStampValue(this.progressTrackers[tmpProgressTrackerHash].StartTimeHash);
|
|
103
|
+
if (tmpProgressTracker.CurrentCount < 0)
|
|
104
|
+
{
|
|
105
|
+
tmpProgressTracker.CurrentCount = 0;
|
|
106
|
+
}
|
|
85
107
|
|
|
86
108
|
return this.solveProgressTrackerStatus(tmpProgressTrackerHash);
|
|
87
109
|
}
|
|
@@ -92,10 +114,12 @@ class FableServiceProgressTracker extends libFableServiceBase
|
|
|
92
114
|
|
|
93
115
|
if (!this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash))
|
|
94
116
|
{
|
|
95
|
-
this.fable.log.error(`Attempted to end
|
|
117
|
+
this.fable.log.error(`Attempted to end ProgressTracker ${tmpProgressTrackerHash} that does not exist!`);
|
|
96
118
|
return false;
|
|
97
119
|
}
|
|
98
120
|
|
|
121
|
+
let tmpProgressTracker = this.progressTrackers[tmpProgressTrackerHash];
|
|
122
|
+
|
|
99
123
|
this.progressTimes.createTimeStamp(this.progressTrackers[tmpProgressTrackerHash].EndTimeHash);
|
|
100
124
|
tmpProgressTracker.EndTimeStamp = this.progressTimes.getTimeStampValue(this.progressTrackers[tmpProgressTrackerHash].EndTimeHash);
|
|
101
125
|
|
|
@@ -108,7 +132,7 @@ class FableServiceProgressTracker extends libFableServiceBase
|
|
|
108
132
|
|
|
109
133
|
if (!this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash))
|
|
110
134
|
{
|
|
111
|
-
this.fable.log.error(`Attempted to solve
|
|
135
|
+
this.fable.log.error(`Attempted to solve ProgressTracker ${tmpProgressTrackerHash} that does not exist!`);
|
|
112
136
|
return false;
|
|
113
137
|
}
|
|
114
138
|
|
|
@@ -116,7 +140,7 @@ class FableServiceProgressTracker extends libFableServiceBase
|
|
|
116
140
|
|
|
117
141
|
if ((tmpProgressTracker.TotalCount < 1) || isNaN(tmpProgressTracker.TotalCount))
|
|
118
142
|
{
|
|
119
|
-
this.fable.log.error(`
|
|
143
|
+
this.fable.log.error(`ProgressTracker ${tmpProgressTracker.Hash} has an invalid total count of operations (${tmpProgressTracker.TotalCount}! Setting it to the default of 100...`);
|
|
120
144
|
tmpProgressTracker.TotalCount = 100;
|
|
121
145
|
}
|
|
122
146
|
|
|
@@ -124,18 +148,27 @@ class FableServiceProgressTracker extends libFableServiceBase
|
|
|
124
148
|
if (tmpProgressTracker.CurrentCount < 1)
|
|
125
149
|
{
|
|
126
150
|
tmpProgressTracker.PercentComplete = 0;
|
|
127
|
-
return tmpProgressTracker;
|
|
128
151
|
}
|
|
129
152
|
else
|
|
130
153
|
{
|
|
131
154
|
tmpProgressTracker.PercentComplete = (tmpProgressTracker.CurrentCount / tmpProgressTracker.TotalCount) * 100.0;
|
|
132
155
|
}
|
|
133
156
|
|
|
157
|
+
if (!tmpProgressTracker.AllowTruePercentComplete && (tmpProgressTracker.PercentComplete > 100))
|
|
158
|
+
{
|
|
159
|
+
tmpProgressTracker.PercentComplete = 100;
|
|
160
|
+
}
|
|
161
|
+
|
|
134
162
|
// Compute the average time per operation
|
|
135
163
|
this.progressTimes.updateTimeStampValue('CurrentTime');
|
|
136
164
|
tmpProgressTracker.CurrentTimeStamp = this.progressTimes.getTimeStampValue('CurrentTime');
|
|
137
165
|
tmpProgressTracker.ElapsedTime = tmpProgressTracker.CurrentTimeStamp - tmpProgressTracker.StartTimeStamp;
|
|
138
166
|
|
|
167
|
+
if (tmpProgressTracker.EndTimeStamp > 0)
|
|
168
|
+
{
|
|
169
|
+
tmpProgressTracker.ElapsedTime = tmpProgressTracker.EndTimeStamp - tmpProgressTracker.StartTimeStamp;
|
|
170
|
+
}
|
|
171
|
+
|
|
139
172
|
if (tmpProgressTracker.CurrentCount > 0)
|
|
140
173
|
{
|
|
141
174
|
tmpProgressTracker.AverageOperationTime = (tmpProgressTracker.CurrentTimeStamp-tmpProgressTracker.StartTimeStamp) / tmpProgressTracker.CurrentCount;
|
|
@@ -165,7 +198,7 @@ class FableServiceProgressTracker extends libFableServiceBase
|
|
|
165
198
|
|
|
166
199
|
if (isNaN(tmpCurrentOperations))
|
|
167
200
|
{
|
|
168
|
-
this.fable.log.warn(`Attempted to update
|
|
201
|
+
this.fable.log.warn(`Attempted to update ProgressTracker ${tmpProgressTrackerHash} with an invalid number of operations!`)
|
|
169
202
|
return false;
|
|
170
203
|
}
|
|
171
204
|
|
|
@@ -191,13 +224,13 @@ class FableServiceProgressTracker extends libFableServiceBase
|
|
|
191
224
|
|
|
192
225
|
if (!this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash))
|
|
193
226
|
{
|
|
194
|
-
this.fable.log.warn(`Attempted to increment
|
|
227
|
+
this.fable.log.warn(`Attempted to increment ProgressTracker ${tmpProgressTrackerHash} but it did not exist.`);
|
|
195
228
|
return false;
|
|
196
229
|
}
|
|
197
230
|
|
|
198
231
|
if (this.progressTrackers[tmpProgressTrackerHash].StartTimeStamp < 1)
|
|
199
232
|
{
|
|
200
|
-
this.fable.log.warn(`Attempted to increment
|
|
233
|
+
this.fable.log.warn(`Attempted to increment ProgressTracker ${tmpProgressTrackerHash} but it was not started.. starting now.`);
|
|
201
234
|
this.startProgressTracker(tmpProgressTrackerHash);
|
|
202
235
|
}
|
|
203
236
|
|
|
@@ -206,32 +239,113 @@ class FableServiceProgressTracker extends libFableServiceBase
|
|
|
206
239
|
return this.solveProgressTrackerStatus(tmpProgressTrackerHash);
|
|
207
240
|
}
|
|
208
241
|
|
|
209
|
-
|
|
242
|
+
getProgressTrackerCompletedOperationCountString(pProgressTrackerHash)
|
|
243
|
+
{
|
|
244
|
+
let tmpProgressTrackerHash = (typeof(pProgressTrackerHash) == 'string') ? pProgressTrackerHash : 'Default';
|
|
245
|
+
|
|
246
|
+
// This call here can mean if we add operations and then immediately get the string, this function runs twice.
|
|
247
|
+
const tmpProgressTracker = this.progressTrackers[tmpProgressTrackerHash];
|
|
248
|
+
|
|
249
|
+
// The states of a progress tracker:
|
|
250
|
+
if (tmpProgressTracker.CurrentCount < 0)
|
|
251
|
+
{
|
|
252
|
+
return `none`;
|
|
253
|
+
}
|
|
254
|
+
else if (tmpProgressTracker.CurrentCount < 1)
|
|
255
|
+
{
|
|
256
|
+
return `0`;
|
|
257
|
+
}
|
|
258
|
+
else
|
|
259
|
+
{
|
|
260
|
+
return `${tmpProgressTracker.CurrentCount}`;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
getProgressTrackerPercentCompleteString(pProgressTrackerHash)
|
|
210
265
|
{
|
|
211
266
|
let tmpProgressTrackerHash = (typeof(pProgressTrackerHash) == 'string') ? pProgressTrackerHash : 'Default';
|
|
212
267
|
|
|
268
|
+
// This call here can mean if we add operations and then immediately get the string, this function runs twice.
|
|
269
|
+
// TODO: Is there a pattern to avoid this double call that's worth putting in?
|
|
270
|
+
this.solveProgressTrackerStatus(tmpProgressTrackerHash);
|
|
271
|
+
|
|
213
272
|
if (!this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash))
|
|
214
273
|
{
|
|
215
|
-
|
|
274
|
+
return `ProgressTracker ${tmpProgressTrackerHash} does not exist! No stats to display.`;
|
|
216
275
|
}
|
|
217
276
|
else
|
|
218
277
|
{
|
|
219
278
|
const tmpProgressTracker = this.progressTrackers[tmpProgressTrackerHash];
|
|
220
279
|
|
|
280
|
+
// The states of a progress tracker:
|
|
281
|
+
// 1. Not started
|
|
282
|
+
if (tmpProgressTracker.StartTimeStamp < 1)
|
|
283
|
+
{
|
|
284
|
+
return `0%`;
|
|
285
|
+
}
|
|
286
|
+
// 2. Started, but no operations completed
|
|
287
|
+
|
|
221
288
|
if (tmpProgressTracker.CurrentCount < 1)
|
|
222
289
|
{
|
|
223
|
-
|
|
290
|
+
return `0%`;
|
|
224
291
|
}
|
|
292
|
+
// 3. Started, some operations completed
|
|
225
293
|
else if (tmpProgressTracker.EndTimeStamp < 1)
|
|
226
294
|
{
|
|
227
|
-
|
|
295
|
+
return `${tmpProgressTracker.PercentComplete.toFixed(3)}%`;
|
|
296
|
+
}
|
|
297
|
+
// 4. Done
|
|
298
|
+
else
|
|
299
|
+
{
|
|
300
|
+
return `${tmpProgressTracker.PercentComplete.toFixed(3)}%`;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
getProgressTrackerStatusString(pProgressTrackerHash)
|
|
306
|
+
{
|
|
307
|
+
let tmpProgressTrackerHash = (typeof(pProgressTrackerHash) == 'string') ? pProgressTrackerHash : 'Default';
|
|
308
|
+
|
|
309
|
+
// This call here can mean if we add operations and then immediately get the string, this function runs twice.
|
|
310
|
+
// TODO: Is there a pattern to avoid this double call that's worth putting in?
|
|
311
|
+
this.solveProgressTrackerStatus(tmpProgressTrackerHash);
|
|
312
|
+
|
|
313
|
+
if (!this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash))
|
|
314
|
+
{
|
|
315
|
+
return `ProgressTracker ${tmpProgressTrackerHash} does not exist! No stats to display.`;
|
|
316
|
+
}
|
|
317
|
+
else
|
|
318
|
+
{
|
|
319
|
+
const tmpProgressTracker = this.progressTrackers[tmpProgressTrackerHash];
|
|
320
|
+
|
|
321
|
+
// The states of a progress tracker:
|
|
322
|
+
// 1. Not started
|
|
323
|
+
if (tmpProgressTracker.StartTimeStamp < 1)
|
|
324
|
+
{
|
|
325
|
+
return `ProgressTracker ${tmpProgressTracker.Hash} has not been started yet.`;
|
|
228
326
|
}
|
|
327
|
+
// 2. Started, but no operations completed
|
|
328
|
+
|
|
329
|
+
if ((tmpProgressTracker.CurrentCount < 1) && (tmpProgressTracker.EndTimeStamp < 1))
|
|
330
|
+
{
|
|
331
|
+
return `ProgressTracker ${tmpProgressTracker.Hash} has no completed operations. ${this.progressTimes.formatTimeDuration(tmpProgressTracker.ElapsedTime)} have elapsed since it was started.`;
|
|
332
|
+
}
|
|
333
|
+
// 3. Started, some operations completed
|
|
334
|
+
else if (tmpProgressTracker.EndTimeStamp < 1)
|
|
335
|
+
{
|
|
336
|
+
return `ProgressTracker ${tmpProgressTracker.Hash} is ${tmpProgressTracker.PercentComplete.toFixed(3)}% completed - ${tmpProgressTracker.CurrentCount} / ${tmpProgressTracker.TotalCount} operations over ${this.progressTimes.formatTimeDuration(tmpProgressTracker.ElapsedTime)} (median ${this.progressTimes.formatTimeDuration(tmpProgressTracker.AverageOperationTime)} per). Estimated completion: ${this.progressTimes.formatTimeDuration(tmpProgressTracker.EstimatedCompletionTime)}`; }
|
|
337
|
+
// 4. Done
|
|
229
338
|
else
|
|
230
339
|
{
|
|
231
|
-
|
|
340
|
+
return `ProgressTracker ${tmpProgressTracker.Hash} is done. It completed ${tmpProgressTracker.CurrentCount} / ${tmpProgressTracker.TotalCount} operations in ${this.progressTimes.formatTimeDuration(tmpProgressTracker.ElapsedTime)} (median ${this.progressTimes.formatTimeDuration(tmpProgressTracker.AverageOperationTime)} per).`;
|
|
232
341
|
}
|
|
233
342
|
}
|
|
234
343
|
}
|
|
344
|
+
|
|
345
|
+
logProgressTrackerStatus(pProgressTrackerHash)
|
|
346
|
+
{
|
|
347
|
+
this.fable.log.info(this.getProgressTrackerStatusString(pProgressTrackerHash));
|
|
348
|
+
}
|
|
235
349
|
}
|
|
236
350
|
|
|
237
351
|
module.exports = FableServiceProgressTracker;
|
|
@@ -86,26 +86,76 @@ suite
|
|
|
86
86
|
function(fDone)
|
|
87
87
|
{
|
|
88
88
|
let testFable = new libFable();
|
|
89
|
-
let tmpOperation = testFable.instantiateServiceProvider('Operation', {Name:'The
|
|
89
|
+
let tmpOperation = testFable.instantiateServiceProvider('Operation', {Name:'MTO - The Mega Test Operation'});
|
|
90
90
|
Expect(tmpOperation).to.be.an('object');
|
|
91
91
|
Expect(testFable.servicesMap.Operation.hasOwnProperty(tmpOperation.Hash)).to.equal(true);
|
|
92
92
|
Expect(tmpOperation.state.Log.length).to.equal(0);
|
|
93
|
-
|
|
93
|
+
|
|
94
|
+
let tmpText = `Created operation ${tmpOperation.Hash}; ready to add a step and start execution...`;
|
|
94
95
|
tmpOperation.log.info(tmpText);
|
|
95
96
|
Expect(tmpOperation.state.Log.length).to.equal(1);
|
|
96
97
|
Expect(tmpOperation.state.Log[0]).to.contain(tmpText);
|
|
97
98
|
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
let tmpOperationCount = 10;
|
|
100
|
+
|
|
101
|
+
tmpOperation.addStep(
|
|
102
|
+
function (fStepComplete)
|
|
100
103
|
{
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
this.logProgressTrackerStatus();
|
|
105
|
+
|
|
106
|
+
let tmpAnticipate = testFable.newAnticipate();
|
|
107
|
+
|
|
108
|
+
for (let i = 1; i <= tmpOperationCount; i++)
|
|
109
|
+
{
|
|
110
|
+
tmpAnticipate.anticipate((fWorkComplete)=>
|
|
111
|
+
{
|
|
112
|
+
let tmpDelay = Math.floor(Math.random() * 100) + 100;
|
|
113
|
+
this.log.info(`Doing some big work for ${tmpDelay}ms on iteration ${i}...`);
|
|
114
|
+
setTimeout(
|
|
115
|
+
() =>
|
|
116
|
+
{
|
|
117
|
+
this.log.info(`Work done for iteration ${i}.`);
|
|
118
|
+
this.incrementProgressTracker(1);
|
|
119
|
+
this.logProgressTrackerStatus();
|
|
120
|
+
return fWorkComplete();
|
|
121
|
+
}, tmpDelay);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
tmpAnticipate.wait(fStepComplete);
|
|
126
|
+
}, {}, 'Example Step 1', 'This is the first step of the mega test.', 'STEP1');
|
|
127
|
+
tmpOperation.setStepTotalOperations('STEP1', tmpOperationCount);
|
|
128
|
+
|
|
129
|
+
tmpOperation.addStep(
|
|
130
|
+
function (fStepComplete)
|
|
131
|
+
{
|
|
132
|
+
let tmpShortOperationCount = 300;
|
|
133
|
+
|
|
134
|
+
this.setProgressTrackerTotalOperations(tmpShortOperationCount);
|
|
135
|
+
this.logProgressTrackerStatus();
|
|
136
|
+
|
|
137
|
+
let tmpAnticipate = testFable.newAnticipate();
|
|
138
|
+
|
|
139
|
+
for (let i = 1; i <= tmpShortOperationCount; i++)
|
|
140
|
+
{
|
|
141
|
+
tmpAnticipate.anticipate((fWorkComplete)=>
|
|
142
|
+
{
|
|
143
|
+
let tmpDelay = Math.floor(Math.random() * 3) + 3;
|
|
144
|
+
this.log.info(`Doing a little work for ${tmpDelay}ms on iteration ${i}...`);
|
|
145
|
+
setTimeout(
|
|
146
|
+
() =>
|
|
147
|
+
{
|
|
148
|
+
this.log.info(`Leetle work done for iteration ${i}.`);
|
|
149
|
+
this.incrementProgressTracker(1);
|
|
150
|
+
this.logProgressTrackerStatus();
|
|
151
|
+
return fWorkComplete();
|
|
152
|
+
}, tmpDelay);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
108
155
|
|
|
156
|
+
tmpAnticipate.wait(fStepComplete);
|
|
157
|
+
}, {}, 'Example Step 2', 'This is the second step of the mega test.', 'STEP2');
|
|
158
|
+
|
|
109
159
|
tmpOperation.execute(fDone);
|
|
110
160
|
}
|
|
111
161
|
);
|