fable 3.0.115 → 3.0.117
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 +0 -18
- package/dist/fable.compatible.js +202 -179
- package/dist/fable.compatible.min.js +2 -2
- package/dist/fable.compatible.min.js.map +1 -1
- package/dist/fable.js +140 -117
- package/dist/fable.min.js +2 -2
- package/dist/fable.min.js.map +1 -1
- package/package.json +1 -1
- package/source/Fable.js +4 -2
- package/source/services/Fable-Service-DataGeneration.js +1 -1
- package/source/services/Fable-Service-Math.js +7 -7
- package/source/services/Fable-Service-Operation-DefaultSettings.js +16 -21
- package/source/services/Fable-Service-Operation.js +89 -236
- package/source/services/Fable-Service-ProgressTime.js +159 -0
- package/source/services/Fable-Service-ProgressTracker.js +351 -0
- package/test/FableOperation_tests.js +61 -11
- package/test/PregressTime_tests.js +110 -0
- package/test/ProgressTracker_tests.js +42 -0
package/package.json
CHANGED
package/source/Fable.js
CHANGED
|
@@ -72,11 +72,13 @@ class Fable extends libFableServiceBase.CoreServiceProviderBase
|
|
|
72
72
|
this.addAndInstantiateServiceType('DataGeneration', require('./services/Fable-Service-DataGeneration.js'));
|
|
73
73
|
this.addAndInstantiateServiceType('Utility', require('./services/Fable-Service-Utility.js'));
|
|
74
74
|
this.addAndInstantiateServiceType('Math', require('./services/Fable-Service-Math.js'));
|
|
75
|
-
this.addServiceType('Operation', require('./services/Fable-Service-Operation.js'));
|
|
76
75
|
this.addServiceType('RestClient', require('./services/Fable-Service-RestClient.js'));
|
|
77
|
-
this.addServiceType('CSVParser', require('./services/Fable-Service-CSVParser.js'));
|
|
78
76
|
this.addServiceType('Manifest', require('manyfest'));
|
|
79
77
|
this.addServiceType('ObjectCache', require('cachetrax'));
|
|
78
|
+
this.addServiceType('Operation', require('./services/Fable-Service-Operation.js'));
|
|
79
|
+
this.addServiceType('ProgressTracker', require('./services/Fable-Service-ProgressTracker.js'));
|
|
80
|
+
this.addAndInstantiateServiceType('ProgressTime', require('./services/Fable-Service-ProgressTime.js'));
|
|
81
|
+
this.addServiceType('CSVParser', require('./services/Fable-Service-CSVParser.js'));
|
|
80
82
|
this.addServiceType('FilePersistence', require('./services/Fable-Service-FilePersistence.js'));
|
|
81
83
|
}
|
|
82
84
|
|
|
@@ -31,7 +31,7 @@ class FableServiceDataGeneration extends libFableServiceBase
|
|
|
31
31
|
randomNumericString(pLength, pMaxNumber)
|
|
32
32
|
{
|
|
33
33
|
let tmpLength = (typeof(pLength) === 'undefined') ? 10 : pLength;
|
|
34
|
-
let tmpMaxNumber = (typeof(pMaxNumber) === 'undefined') ?
|
|
34
|
+
let tmpMaxNumber = (typeof(pMaxNumber) === 'undefined') ? 9999999999 : pMaxNumber;
|
|
35
35
|
|
|
36
36
|
return this.services.DataFormat.stringPadStart(this.randomIntegerUpTo(tmpMaxNumber), pLength, '0');
|
|
37
37
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
const libFableServiceBase = require('fable-serviceproviderbase');
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Arbitrary Precision Math Operations
|
|
5
|
-
* @author Steven Velozo <steven@velozo.com>
|
|
6
|
-
* @description Simple functions that perform arbitrary precision math operations and return string resultant values. Wraps big.js
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
* Arbitrary Precision Math Operations
|
|
5
|
+
* @author Steven Velozo <steven@velozo.com>
|
|
6
|
+
* @description Simple functions that perform arbitrary precision math operations and return string resultant values. Wraps big.js
|
|
7
|
+
* @class FableServiceMath
|
|
8
|
+
* @extends libFableServiceBase
|
|
9
|
+
*/
|
|
9
10
|
class FableServiceMath extends libFableServiceBase
|
|
10
11
|
{
|
|
11
12
|
constructor(pFable, pOptions, pServiceHash)
|
|
@@ -15,9 +16,8 @@ class FableServiceMath extends libFableServiceBase
|
|
|
15
16
|
this.serviceType = 'Math';
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
|
|
19
19
|
/*
|
|
20
|
-
Rounding
|
|
20
|
+
Pass-through Rounding Method Constants
|
|
21
21
|
|
|
22
22
|
Property Value BigDecimal Equiv Description
|
|
23
23
|
---------- ----- ---------------- -----------
|
|
@@ -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,96 +27,134 @@ 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?
|
|
41
|
+
if (this.state.OverallProgressTracker.StartTimeStamp > 0)
|
|
38
42
|
{
|
|
39
43
|
return fExecutionCompleteCallback(new Error(`Operation [${this.state.Metadata.UUID}] ${this.state.Metadata.Name} has already been executed!`));
|
|
40
44
|
}
|
|
41
45
|
|
|
42
|
-
this.state.Status.TimeStart = +new Date();
|
|
43
|
-
|
|
44
46
|
let tmpAnticipate = this.fable.instantiateServiceProviderWithoutRegistration('Anticipate');
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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...`);
|
|
51
|
+
|
|
52
|
+
for (let i = 0; i < this.state.Steps.length; i++)
|
|
53
|
+
{
|
|
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
|
+
ProgressTracker:this.progressTrackers.getProgressTracker(this.state.Steps[i].ProgressTracker.Hash),
|
|
67
|
+
updateProgressTracker: function(pProgressAmount)
|
|
68
|
+
{
|
|
69
|
+
return this.progressTrackers.updateProgressTracker(this.state.Steps[i].ProgressTracker.Hash, pProgressAmount);
|
|
70
|
+
}.bind(this),
|
|
71
|
+
incrementProgressTracker: function(pProgressIncrementAmount)
|
|
72
|
+
{
|
|
73
|
+
return this.progressTrackers.incrementProgressTracker(this.state.Steps[i].ProgressTracker.Hash, pProgressIncrementAmount);
|
|
74
|
+
}.bind(this),
|
|
75
|
+
setProgressTrackerTotalOperations: function(pTotalOperationCount)
|
|
76
|
+
{
|
|
77
|
+
return this.progressTrackers.setProgressTrackerTotalOperations(this.state.Steps[i].ProgressTracker.Hash, pTotalOperationCount);
|
|
78
|
+
}.bind(this),
|
|
79
|
+
getProgressTrackerStatusString: function()
|
|
80
|
+
{
|
|
81
|
+
return this.progressTrackers.getProgressTrackerStatusString(this.state.Steps[i].ProgressTracker.Hash);
|
|
82
|
+
}.bind(this),
|
|
83
|
+
logProgressTrackerStatus: function()
|
|
84
|
+
{
|
|
85
|
+
return this.log.info(`Step #${i} [${this.state.Steps[i].GUIDStep}]: ${this.progressTrackers.getProgressTrackerStatusString(this.state.Steps[i].ProgressTracker.Hash)}`);
|
|
86
|
+
}.bind(this),
|
|
87
|
+
OperationState:this.state,
|
|
88
|
+
StepState:this.state.Steps[i]
|
|
89
|
+
}));
|
|
90
|
+
tmpAnticipate.anticipate(
|
|
91
|
+
function(fNext)
|
|
92
|
+
{
|
|
93
|
+
this.progressTrackers.endProgressTracker(this.state.Steps[i].ProgressTracker.Hash);
|
|
94
|
+
let tmpStepTimingMessage = this.progressTrackers.getProgressTrackerStatusString(this.state.Steps[i].ProgressTracker.Hash);
|
|
95
|
+
this.fable.log.info(`Step #${i} [${this.state.Steps[i].GUIDStep}] ${this.state.Steps[i].Name} complete.`);
|
|
96
|
+
this.fable.log.info(`Step #${i} [${this.state.Steps[i].GUIDStep}] ${this.state.Steps[i].Name} ${tmpStepTimingMessage}.`);
|
|
97
|
+
|
|
98
|
+
this.progressTrackers.incrementProgressTracker(this.state.OverallProgressTracker.Hash, 1);
|
|
99
|
+
let tmpOperationTimingMessage = this.progressTrackers.getProgressTrackerStatusString(this.state.OverallProgressTracker.Hash);
|
|
100
|
+
this.fable.log.info(`Operation [${this.state.Metadata.UUID}] ${tmpOperationTimingMessage}.`);
|
|
101
|
+
return fNext();
|
|
102
|
+
}.bind(this));
|
|
49
103
|
}
|
|
50
104
|
|
|
51
105
|
// Wait for the anticipation to complete
|
|
52
106
|
tmpAnticipate.wait(
|
|
53
107
|
(pError) =>
|
|
54
108
|
{
|
|
55
|
-
|
|
109
|
+
if (pError)
|
|
110
|
+
{
|
|
111
|
+
this.fable.log.error(`Operation [${this.state.Metadata.UUID}] ${this.state.Metadata.Name} had an error: ${pError}`, pError);
|
|
112
|
+
return fExecutionCompleteCallback(pError);
|
|
113
|
+
}
|
|
114
|
+
this.info(`Operation [${this.state.Metadata.UUID}] ${this.state.Metadata.Name} complete.`);
|
|
115
|
+
let tmpOperationTimingMessage = this.progressTrackers.getProgressTrackerStatusString(this.state.OverallProgressTracker.Hash);
|
|
116
|
+
this.progressTrackers.endProgressTracker(this.state.OverallProgressTracker.Hash);
|
|
117
|
+
this.fable.log.info(`Operation [${this.state.Metadata.UUID}] ${tmpOperationTimingMessage}.`);
|
|
56
118
|
return fExecutionCompleteCallback();
|
|
57
119
|
});
|
|
58
120
|
}
|
|
59
121
|
|
|
60
|
-
|
|
61
|
-
TODO: I've gone back and forth on whether this should be an object, JSON
|
|
62
|
-
object prototype, or set of functions here. Discuss with colleagues!
|
|
63
|
-
*/
|
|
64
|
-
addStep(pGUIDStep, fStepFunction, pStepName, pStepDescription, pStepMetadata)
|
|
122
|
+
addStep(fStepFunction, pStepMetadata, pStepName, pStepDescription, pGUIDStep)
|
|
65
123
|
{
|
|
66
124
|
let tmpStep = {};
|
|
125
|
+
|
|
126
|
+
// GUID is optional
|
|
67
127
|
tmpStep.GUIDStep = (typeof(pGUIDStep) !== 'undefined') ? pGUIDStep : `STEP-${this.state.Steps.length}-${this.fable.DataGeneration.randomNumericString()}`;
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
// Name is optional
|
|
68
131
|
tmpStep.Name = (typeof(pStepName) !== 'undefined') ? pStepName : `Step [${tmpStep.GUIDStep}]`;
|
|
69
132
|
tmpStep.Description = (typeof(pStepDescription) !== 'undefined') ? pStepDescription : `Step execution of ${tmpStep.Name}.`;
|
|
70
|
-
// TODO: Right now this allows an Array... do we want to block that?
|
|
71
|
-
tmpStep.Metadata = (typeof(pStepMetadata) === 'object') ? pStepMetadata : {};
|
|
72
133
|
|
|
73
|
-
tmpStep.
|
|
74
|
-
|
|
134
|
+
tmpStep.ProgressTracker = this.progressTrackers.createProgressTracker(tmpStep.GUIDStep);
|
|
135
|
+
|
|
136
|
+
tmpStep.Metadata = (typeof(pStepMetadata) === 'object') ? pStepMetadata : {};
|
|
75
137
|
|
|
76
138
|
// There is an array of steps, in the Operation State itself ... push a step there
|
|
77
139
|
this.state.Steps.push(tmpStep);
|
|
78
140
|
|
|
79
|
-
this.stepMap[tmpStep.GUIDStep]
|
|
80
|
-
this.stepFunctions[tmpStep.GUIDStep] = fStepFunction;
|
|
141
|
+
this.stepMap[tmpStep.GUIDStep] = tmpStep;
|
|
81
142
|
|
|
82
|
-
this.
|
|
83
|
-
return tmpStep;
|
|
84
|
-
}
|
|
143
|
+
this.stepFunctions[tmpStep.GUIDStep] = typeof(fStepFunction) == 'function' ? fStepFunction : function (fDone) { return fDone(); };
|
|
85
144
|
|
|
86
|
-
|
|
87
|
-
{
|
|
88
|
-
if (this.stepMap.hasOwnProperty(pGUIDStep))
|
|
89
|
-
{
|
|
90
|
-
return this.stepMap[pGUIDStep];
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return false;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
startStep(pGUIDStep)
|
|
97
|
-
{
|
|
98
|
-
let tmpStep = this.getStep(pGUIDStep);
|
|
99
|
-
|
|
100
|
-
if (tmpStep === false)
|
|
101
|
-
{
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
tmpStep.TimeStart = +new Date();
|
|
145
|
+
this.state.Status.StepCount++;
|
|
106
146
|
|
|
107
147
|
return tmpStep;
|
|
108
148
|
}
|
|
109
149
|
|
|
110
|
-
|
|
150
|
+
setStepTotalOperations(pGUIDStep, pTotalOperationCount)
|
|
111
151
|
{
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if (tmpStep === false)
|
|
152
|
+
if (!this.stepMap.hasOwnProperty(pGUIDStep))
|
|
115
153
|
{
|
|
116
|
-
return
|
|
154
|
+
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}.`);
|
|
117
155
|
}
|
|
118
156
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
return tmpStep;
|
|
157
|
+
this.progressTrackers.setProgressTrackerTotalOperations(this.stepMap[pGUIDStep].ProgressTracker.Hash, pTotalOperationCount);
|
|
122
158
|
}
|
|
123
159
|
|
|
124
160
|
writeOperationLog(pLogLevel, pLogText, pLogObject)
|
|
@@ -178,189 +214,6 @@ class FableOperation extends libFableServiceBase
|
|
|
178
214
|
this.writeOperationErrors(pLogText, pLogObject);
|
|
179
215
|
this.fable.log.fatal(pLogText, pLogObject);
|
|
180
216
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
/************************************************************************
|
|
184
|
-
* BEGINNING OF --> Telemetry Helpers
|
|
185
|
-
*/
|
|
186
|
-
createTimeStamp(pTimeStampHash)
|
|
187
|
-
{
|
|
188
|
-
let tmpTimeStampHash = (typeof(pTimeStampHash) == 'string') ? pTimeStampHash : 'Default';
|
|
189
|
-
this.timeStamps[tmpTimeStampHash] = +new Date();
|
|
190
|
-
return this.timeStamps[tmpTimeStampHash];
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
getTimeDelta(pTimeStampHash)
|
|
194
|
-
{
|
|
195
|
-
let tmpTimeStampHash = (typeof(pTimeStampHash) == 'string') ? pTimeStampHash : 'Default';
|
|
196
|
-
if (this.timeStamps.hasOwnProperty(tmpTimeStampHash))
|
|
197
|
-
{
|
|
198
|
-
let tmpEndTime = +new Date();
|
|
199
|
-
return tmpEndTime-this.timeStamps[tmpTimeStampHash];
|
|
200
|
-
}
|
|
201
|
-
else
|
|
202
|
-
{
|
|
203
|
-
return -1;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
logTimeDelta(pTimeStampHash, pMessage)
|
|
208
|
-
{
|
|
209
|
-
let tmpTimeStampHash = (typeof(pTimeStampHash) == 'string') ? pTimeStampHash : 'Default';
|
|
210
|
-
let tmpMessage = (typeof(pMessage) !== 'undefined') ? pMessage : `Elapsed for ${tmpTimeStampHash}: `;
|
|
211
|
-
let tmpOperationTime = this.getTimeDelta(pTimeStampHash);
|
|
212
|
-
this.info(tmpMessage +' ('+tmpOperationTime+'ms)');
|
|
213
|
-
return tmpOperationTime;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
createProgressTracker(pTotalOperations, pProgressTrackerHash)
|
|
217
|
-
{
|
|
218
|
-
let tmpProgressTrackerHash = (typeof(pProgressTrackerHash) == 'string') ? pProgressTrackerHash : 'DefaultProgressTracker';
|
|
219
|
-
let tmpTotalOperations = (typeof(pTotalOperations) == 'number') ? pTotalOperations : 100;
|
|
220
|
-
|
|
221
|
-
let tmpProgressTracker = (
|
|
222
|
-
{
|
|
223
|
-
Hash: tmpProgressTrackerHash,
|
|
224
|
-
StartTime: this.createTimeStamp(tmpProgressTrackerHash),
|
|
225
|
-
EndTime: 0,
|
|
226
|
-
CurrentTime: 0,
|
|
227
|
-
PercentComplete: -1,
|
|
228
|
-
AverageOperationTime: -1,
|
|
229
|
-
EstimatedCompletionTime: -1,
|
|
230
|
-
TotalCount: tmpTotalOperations,
|
|
231
|
-
CurrentCount:-1
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
this.progressTrackers[tmpProgressTrackerHash] = tmpProgressTracker;
|
|
235
|
-
|
|
236
|
-
return tmpProgressTracker;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
solveProgressTrackerStatus(pProgressTrackerHash)
|
|
240
|
-
{
|
|
241
|
-
let tmpProgressTrackerHash = (typeof(pProgressTrackerHash) == 'string') ? pProgressTrackerHash : 'DefaultProgressTracker';
|
|
242
|
-
|
|
243
|
-
if (!this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash))
|
|
244
|
-
{
|
|
245
|
-
this.createProgressTracker(100, tmpProgressTrackerHash);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
let tmpProgressTracker = this.progressTrackers[tmpProgressTrackerHash];
|
|
249
|
-
|
|
250
|
-
tmpProgressTracker.CurrentTime = this.getTimeDelta(tmpProgressTracker.Hash);
|
|
251
|
-
|
|
252
|
-
if ((tmpProgressTracker.CurrentCount > 0) && (tmpProgressTracker.TotalCount > 0))
|
|
253
|
-
{
|
|
254
|
-
tmpProgressTracker.PercentComplete = (tmpProgressTracker.CurrentCount / tmpProgressTracker.TotalCount) * 100.0;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
if ((tmpProgressTracker.CurrentCount > 0) && (tmpProgressTracker.CurrentTime > 0))
|
|
258
|
-
{
|
|
259
|
-
tmpProgressTracker.AverageOperationTime = tmpProgressTracker.CurrentTime / tmpProgressTracker.CurrentCount;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
if ((tmpProgressTracker.CurrentCount < tmpProgressTracker.TotalCount) && (tmpProgressTracker.AverageOperationTime > 0))
|
|
263
|
-
{
|
|
264
|
-
tmpProgressTracker.EstimatedCompletionTime = (tmpProgressTracker.TotalCount - tmpProgressTracker.CurrentCount) * tmpProgressTracker.AverageOperationTime;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
updateProgressTrackerStatus(pProgressTrackerHash, pCurrentOperations)
|
|
269
|
-
{
|
|
270
|
-
let tmpProgressTrackerHash = (typeof(pProgressTrackerHash) == 'string') ? pProgressTrackerHash : 'DefaultProgressTracker';
|
|
271
|
-
let tmpCurrentOperations = parseInt(pCurrentOperations);
|
|
272
|
-
|
|
273
|
-
if (isNaN(tmpCurrentOperations))
|
|
274
|
-
{
|
|
275
|
-
return false;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
if (!this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash))
|
|
279
|
-
{
|
|
280
|
-
this.createProgressTracker(100, tmpProgressTrackerHash);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
this.progressTrackers[tmpProgressTrackerHash].CurrentCount = tmpCurrentOperations;
|
|
284
|
-
this.progressTrackers[tmpProgressTrackerHash].CurrentTime = this.getTimeDelta(tmpProgressTrackerHash);
|
|
285
|
-
|
|
286
|
-
this.solveProgressTrackerStatus(tmpProgressTrackerHash);
|
|
287
|
-
|
|
288
|
-
return this.progressTrackers[tmpProgressTrackerHash];
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
incrementProgressTrackerStatus(pProgressTrackerHash, pIncrementSize)
|
|
292
|
-
{
|
|
293
|
-
let tmpProgressTrackerHash = (typeof(pProgressTrackerHash) == 'string') ? pProgressTrackerHash : 'DefaultProgressTracker';
|
|
294
|
-
let tmpIncrementSize = parseInt(pIncrementSize);
|
|
295
|
-
|
|
296
|
-
if (isNaN(tmpIncrementSize))
|
|
297
|
-
{
|
|
298
|
-
return false;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
if (!this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash))
|
|
302
|
-
{
|
|
303
|
-
this.createProgressTracker(100, tmpProgressTrackerHash);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
this.progressTrackers[tmpProgressTrackerHash].CurrentCount = this.progressTrackers[tmpProgressTrackerHash].CurrentCount + tmpIncrementSize;
|
|
307
|
-
this.progressTrackers[tmpProgressTrackerHash].CurrentTime = this.getTimeDelta(tmpProgressTrackerHash);
|
|
308
|
-
|
|
309
|
-
this.solveProgressTrackerStatus(tmpProgressTrackerHash);
|
|
310
|
-
|
|
311
|
-
return this.progressTrackers[tmpProgressTrackerHash];
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
setProgressTrackerEndTime(pProgressTrackerHash, pCurrentOperations)
|
|
315
|
-
{
|
|
316
|
-
let tmpProgressTrackerHash = (typeof(pProgressTrackerHash) == 'string') ? pProgressTrackerHash : 'DefaultProgressTracker';
|
|
317
|
-
let tmpCurrentOperations = parseInt(pCurrentOperations);
|
|
318
|
-
|
|
319
|
-
if (!this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash))
|
|
320
|
-
{
|
|
321
|
-
return false;
|
|
322
|
-
}
|
|
323
|
-
if (!isNaN(tmpCurrentOperations))
|
|
324
|
-
{
|
|
325
|
-
this.updateProgressTrackerStatus(tmpProgressTrackerHash, tmpCurrentOperations);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
this.progressTrackers[tmpProgressTrackerHash].EndTime = this.getTimeDelta(tmpProgressTrackerHash);
|
|
329
|
-
|
|
330
|
-
this.solveProgressTrackerStatus(tmpProgressTrackerHash);
|
|
331
|
-
|
|
332
|
-
return this.progressTrackers[tmpProgressTrackerHash];
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
printProgressTrackerStatus(pProgressTrackerHash)
|
|
336
|
-
{
|
|
337
|
-
let tmpProgressTrackerHash = (typeof(pProgressTrackerHash) == 'string') ? pProgressTrackerHash : 'DefaultProgressTracker';
|
|
338
|
-
|
|
339
|
-
if (!this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash))
|
|
340
|
-
{
|
|
341
|
-
this.info(`>> Progress Tracker ${tmpProgressTrackerHash} does not exist! No stats to display.`);
|
|
342
|
-
}
|
|
343
|
-
else
|
|
344
|
-
{
|
|
345
|
-
const tmpProgressTracker = this.progressTrackers[tmpProgressTrackerHash];
|
|
346
|
-
|
|
347
|
-
if (tmpProgressTracker.CurrentCount < 1)
|
|
348
|
-
{
|
|
349
|
-
this.info(`>> Progress Tracker ${tmpProgressTracker.Hash} has no completed operations. ${tmpProgressTracker.CurrentTime}ms have elapsed since it was started.`);
|
|
350
|
-
}
|
|
351
|
-
else if (tmpProgressTracker.EndTime < 1)
|
|
352
|
-
{
|
|
353
|
-
this.info(`>> Progress Tracker ${tmpProgressTracker.Hash} is ${tmpProgressTracker.PercentComplete.toFixed(3)}% completed - ${tmpProgressTracker.CurrentCount} / ${tmpProgressTracker.TotalCount} operations over ${tmpProgressTracker.CurrentTime}ms (median ${tmpProgressTracker.AverageOperationTime.toFixed(3)} per). Estimated completion in ${tmpProgressTracker.EstimatedCompletionTime.toFixed(0)}ms or ${(tmpProgressTracker.EstimatedCompletionTime / 1000 / 60).toFixed(2)}minutes`)
|
|
354
|
-
}
|
|
355
|
-
else
|
|
356
|
-
{
|
|
357
|
-
this.info(`>> Progress Tracker ${tmpProgressTracker.Hash} is done and completed ${tmpProgressTracker.CurrentCount} / ${tmpProgressTracker.TotalCount} operations in ${tmpProgressTracker.EndTime}ms.`)
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
/*
|
|
362
|
-
* END OF --> Logging and Telemetry Helpers
|
|
363
|
-
************************************************************************/
|
|
364
217
|
}
|
|
365
218
|
|
|
366
219
|
module.exports = FableOperation;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
const libFableServiceBase = require('fable-serviceproviderbase');
|
|
2
|
+
|
|
3
|
+
class FableServiceProgressTime extends libFableServiceBase
|
|
4
|
+
{
|
|
5
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
6
|
+
{
|
|
7
|
+
super(pFable, pOptions, pServiceHash);
|
|
8
|
+
|
|
9
|
+
this.serviceType = 'ProgressTime';
|
|
10
|
+
|
|
11
|
+
this.timeStamps = {};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
formatTimeDuration(pTimeDurationInMilliseconds)
|
|
15
|
+
{
|
|
16
|
+
let tmpTimeDuration = typeof(pTimeDurationInMilliseconds) == 'number' ? pTimeDurationInMilliseconds : 0;
|
|
17
|
+
|
|
18
|
+
if (pTimeDurationInMilliseconds < 0)
|
|
19
|
+
{
|
|
20
|
+
return 'unknown';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let tmpTimeDurationString = '';
|
|
24
|
+
if (tmpTimeDuration > 3600000)
|
|
25
|
+
{
|
|
26
|
+
tmpTimeDurationString += Math.floor(tmpTimeDuration/3600000)+'h ';
|
|
27
|
+
tmpTimeDuration = tmpTimeDuration % 3600000;
|
|
28
|
+
}
|
|
29
|
+
if (tmpTimeDuration > 60000)
|
|
30
|
+
{
|
|
31
|
+
tmpTimeDurationString += Math.floor(tmpTimeDuration/60000)+'m ';
|
|
32
|
+
tmpTimeDuration = tmpTimeDuration % 60000;
|
|
33
|
+
}
|
|
34
|
+
if (tmpTimeDuration > 1000)
|
|
35
|
+
{
|
|
36
|
+
tmpTimeDurationString += Math.floor(tmpTimeDuration/1000)+'s ';
|
|
37
|
+
tmpTimeDuration = tmpTimeDuration % 1000;
|
|
38
|
+
}
|
|
39
|
+
tmpTimeDurationString += Math.round(tmpTimeDuration)+'ms';
|
|
40
|
+
|
|
41
|
+
return tmpTimeDurationString;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
createTimeStamp(pTimeStampHash)
|
|
45
|
+
{
|
|
46
|
+
let tmpTimeStampHash = (typeof(pTimeStampHash) == 'string') ? pTimeStampHash : 'Default';
|
|
47
|
+
this.timeStamps[tmpTimeStampHash] = +new Date();
|
|
48
|
+
return this.timeStamps[tmpTimeStampHash];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
getTimeStampValue(pTimeStampHash)
|
|
52
|
+
{
|
|
53
|
+
let tmpTimeStampHash = (typeof(pTimeStampHash) == 'string') ? pTimeStampHash : 'Default';
|
|
54
|
+
return this.timeStamps.hasOwnProperty(tmpTimeStampHash) ? this.timeStamps[tmpTimeStampHash] : -1;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
updateTimeStampValue(pTimeStampHash, pReferenceTime)
|
|
58
|
+
{
|
|
59
|
+
let tmpTimeStampHash = (typeof(pTimeStampHash) == 'string') ? pTimeStampHash : 'Default';
|
|
60
|
+
let tmpReferenceTime = false;
|
|
61
|
+
|
|
62
|
+
// This function allows the user to pass in either a reference time in ms, or, a hash of a timestamp.
|
|
63
|
+
if (typeof(pReferenceTime) == 'string')
|
|
64
|
+
{
|
|
65
|
+
tmpReferenceTime = this.timeStamps.hasOwnProperty(tmpReference) ? this.timeStamps[tmpReference] : false;
|
|
66
|
+
}
|
|
67
|
+
else if (typeof(pReferenceTime) == 'number')
|
|
68
|
+
{
|
|
69
|
+
tmpReferenceTime = pReferenceTime;
|
|
70
|
+
}
|
|
71
|
+
else
|
|
72
|
+
{
|
|
73
|
+
tmpReferenceTime = +new Date();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (this.timeStamps.hasOwnProperty(tmpTimeStampHash) && tmpReferenceTime)
|
|
77
|
+
{
|
|
78
|
+
this.timeStamps[tmpTimeStampHash] = tmpReferenceTime;
|
|
79
|
+
return this.timeStamps[tmpTimeStampHash];
|
|
80
|
+
}
|
|
81
|
+
else
|
|
82
|
+
{
|
|
83
|
+
return -1;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
removeTimeStamp(pTimeStampHash)
|
|
88
|
+
{
|
|
89
|
+
let tmpTimeStampHash = (typeof(pTimeStampHash) == 'string') ? pTimeStampHash : 'Default';
|
|
90
|
+
if (this.timeStamps.hasOwnProperty(tmpTimeStampHash))
|
|
91
|
+
{
|
|
92
|
+
delete this.timeStamps[tmpTimeStampHash];
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
else
|
|
96
|
+
{
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
getTimeStampDelta(pTimeStampHash, pReferenceTime)
|
|
102
|
+
{
|
|
103
|
+
let tmpTimeStampHash = (typeof(pTimeStampHash) == 'string') ? pTimeStampHash : 'Default';
|
|
104
|
+
let tmpReferenceTime = false;
|
|
105
|
+
|
|
106
|
+
// This function allows the user to pass in either a reference time in ms, or, a hash of a timestamp.
|
|
107
|
+
if (typeof(pReferenceTime) == 'string')
|
|
108
|
+
{
|
|
109
|
+
tmpReferenceTime = this.timeStamps.hasOwnProperty(tmpReference) ? this.timeStamps[tmpReference] : false;
|
|
110
|
+
}
|
|
111
|
+
else if (typeof(pReferenceTime) == 'number')
|
|
112
|
+
{
|
|
113
|
+
tmpReferenceTime = pReferenceTime;
|
|
114
|
+
}
|
|
115
|
+
else
|
|
116
|
+
{
|
|
117
|
+
tmpReferenceTime = +new Date();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (this.timeStamps.hasOwnProperty(tmpTimeStampHash) && tmpReferenceTime)
|
|
121
|
+
{
|
|
122
|
+
return tmpReferenceTime-this.timeStamps[tmpTimeStampHash];
|
|
123
|
+
}
|
|
124
|
+
else
|
|
125
|
+
{
|
|
126
|
+
return -1;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
getDurationBetweenTimestamps(pTimeStampHashStart, pTimeStampHashEnd)
|
|
131
|
+
{
|
|
132
|
+
let tmpTimeStampHashStart = (typeof(pTimeStampHashStart) == 'string') ? pTimeStampHashStart : 'Default';
|
|
133
|
+
let tmpTimeStampHashEnd = (typeof(pTimeStampHashEnd) == 'string') ? pTimeStampHashEnd : 'Default';
|
|
134
|
+
if (this.timeStamps.hasOwnProperty(tmpTimeStampHashStart) && this.timeStamps.hasOwnProperty(tmpTimeStampHashEnd))
|
|
135
|
+
{
|
|
136
|
+
return this.timeStamps[tmpTimeStampHashEnd]-this.timeStamps[tmpTimeStampHashStart];
|
|
137
|
+
}
|
|
138
|
+
else
|
|
139
|
+
{
|
|
140
|
+
return -1;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
getTimeStampDeltaMessage(pTimeStampHash, pMessage, pReferenceTime)
|
|
145
|
+
{
|
|
146
|
+
let tmpTimeStampHash = (typeof(pTimeStampHash) == 'string') ? pTimeStampHash : 'Default';
|
|
147
|
+
let tmpMessage = (typeof(pMessage) !== 'undefined') ? pMessage : `Elapsed for ${tmpTimeStampHash}: `;
|
|
148
|
+
let tmpOperationTime = this.getTimeStampDelta(tmpTimeStampHash, pReferenceTime);
|
|
149
|
+
|
|
150
|
+
return `${tmpMessage} ${this.formatTimeDuration(tmpOperationTime)}`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
logTimeStampDelta(pTimeStampHash, pMessage, pReferenceTime)
|
|
154
|
+
{
|
|
155
|
+
this.fable.log.info(this.getTimeStampDeltaMessage(pTimeStampHash, pMessage, pReferenceTime));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
module.exports = FableServiceProgressTime;
|