ultravisor 1.0.2 → 1.0.4
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/.claude/launch.json +11 -0
- package/.claude/ultravisor-dev-config.json +3 -0
- package/.ultravisor.json +426 -0
- package/docs/README.md +63 -0
- package/package.json +12 -8
- package/source/Ultravisor.cjs +22 -3
- package/source/cli/Ultravisor-CLIProgram.cjs +35 -23
- package/source/cli/commands/Ultravisor-Command-SingleOperation.cjs +29 -18
- package/source/cli/commands/Ultravisor-Command-SingleTask.cjs +62 -19
- package/source/cli/commands/Ultravisor-Command-UpdateTask.cjs +27 -15
- package/source/config/Ultravisor-Default-Command-Configuration.cjs +5 -3
- package/source/services/Ultravisor-ExecutionEngine.cjs +1039 -0
- package/source/services/Ultravisor-ExecutionManifest.cjs +399 -0
- package/source/services/Ultravisor-Hypervisor-State.cjs +270 -97
- package/source/services/Ultravisor-Hypervisor.cjs +38 -83
- package/source/services/Ultravisor-StateManager.cjs +241 -0
- package/source/services/Ultravisor-TaskTypeRegistry.cjs +143 -0
- package/source/services/tasks/Ultravisor-TaskType-Base.cjs +105 -0
- package/source/services/tasks/control/Ultravisor-TaskType-IfConditional.cjs +148 -0
- package/source/services/tasks/control/Ultravisor-TaskType-LaunchOperation.cjs +187 -0
- package/source/services/tasks/control/Ultravisor-TaskType-SplitExecute.cjs +184 -0
- package/source/services/tasks/data/Ultravisor-TaskType-ReplaceString.cjs +82 -0
- package/source/services/tasks/data/Ultravisor-TaskType-SetValues.cjs +81 -0
- package/source/services/tasks/data/Ultravisor-TaskType-StringAppender.cjs +101 -0
- package/source/services/tasks/file-io/Ultravisor-TaskType-ReadFile.cjs +103 -0
- package/source/services/tasks/file-io/Ultravisor-TaskType-WriteFile.cjs +117 -0
- package/source/services/tasks/interaction/Ultravisor-TaskType-ErrorMessage.cjs +54 -0
- package/source/services/tasks/interaction/Ultravisor-TaskType-ValueInput.cjs +62 -0
- package/source/web_server/Ultravisor-API-Server.cjs +237 -124
- package/test/Ultravisor_browser_tests.js +2226 -0
- package/test/Ultravisor_tests.js +1143 -5830
- package/webinterface/css/ultravisor.css +23 -0
- package/webinterface/package.json +6 -3
- package/webinterface/source/Pict-Application-Ultravisor.js +93 -73
- package/webinterface/source/cards/FlowCard-CSVTransform.js +43 -0
- package/webinterface/source/cards/FlowCard-Command.js +86 -0
- package/webinterface/source/cards/FlowCard-ComprehensionIntersect.js +40 -0
- package/webinterface/source/cards/FlowCard-Conditional.js +87 -0
- package/webinterface/source/cards/FlowCard-CopyFile.js +55 -0
- package/webinterface/source/cards/FlowCard-End.js +29 -0
- package/webinterface/source/cards/FlowCard-GetJSON.js +55 -0
- package/webinterface/source/cards/FlowCard-GetText.js +54 -0
- package/webinterface/source/cards/FlowCard-Histogram.js +176 -0
- package/webinterface/source/cards/FlowCard-LaunchOperation.js +82 -0
- package/webinterface/source/cards/FlowCard-ListFiles.js +55 -0
- package/webinterface/source/cards/FlowCard-MeadowCount.js +44 -0
- package/webinterface/source/cards/FlowCard-MeadowCreate.js +44 -0
- package/webinterface/source/cards/FlowCard-MeadowDelete.js +45 -0
- package/webinterface/source/cards/FlowCard-MeadowRead.js +46 -0
- package/webinterface/source/cards/FlowCard-MeadowReads.js +46 -0
- package/webinterface/source/cards/FlowCard-MeadowUpdate.js +44 -0
- package/webinterface/source/cards/FlowCard-ParseCSV.js +85 -0
- package/webinterface/source/cards/FlowCard-ReadJSON.js +54 -0
- package/webinterface/source/cards/FlowCard-ReadText.js +54 -0
- package/webinterface/source/cards/FlowCard-RestRequest.js +59 -0
- package/webinterface/source/cards/FlowCard-SendJSON.js +57 -0
- package/webinterface/source/cards/FlowCard-Solver.js +77 -0
- package/webinterface/source/cards/FlowCard-Start.js +29 -0
- package/webinterface/source/cards/FlowCard-TemplateString.js +77 -0
- package/webinterface/source/cards/FlowCard-WriteJSON.js +54 -0
- package/webinterface/source/cards/FlowCard-WriteText.js +54 -0
- package/webinterface/source/data/ExampleFlow-CSVPipeline.js +231 -0
- package/webinterface/source/data/ExampleFlow-FileProcessor.js +315 -0
- package/webinterface/source/data/ExampleFlow-MeadowPipeline.js +328 -0
- package/webinterface/source/providers/PictRouter-Ultravisor-Configuration.json +8 -8
- package/webinterface/source/views/PictView-Ultravisor-Dashboard.js +6 -6
- package/webinterface/source/views/PictView-Ultravisor-FlowEditor.js +436 -0
- package/webinterface/source/views/PictView-Ultravisor-ManifestList.js +45 -43
- package/webinterface/source/views/PictView-Ultravisor-OperationEdit.js +34 -89
- package/webinterface/source/views/PictView-Ultravisor-OperationList.js +128 -13
- package/webinterface/source/views/PictView-Ultravisor-PendingInput.js +314 -0
- package/webinterface/source/views/PictView-Ultravisor-Schedule.js +18 -53
- package/webinterface/source/views/PictView-Ultravisor-TimingView.js +27 -14
- package/webinterface/source/views/PictView-Ultravisor-TopBar.js +2 -1
- package/.babelrc +0 -6
- package/.browserslistrc +0 -1
- package/.browserslistrc-BACKUP +0 -1
- package/.gulpfile-quackage-config.json +0 -7
- package/.gulpfile-quackage.js +0 -2
- package/debug/Harness.js +0 -5
- package/source/services/Ultravisor-Operation-Manifest.cjs +0 -160
- package/source/services/Ultravisor-Operation.cjs +0 -200
- package/source/services/Ultravisor-Task.cjs +0 -349
- package/source/services/events/Ultravisor-Hypervisor-Event-Solver.cjs +0 -11
- package/source/services/tasks/Ultravisor-Task-Base.cjs +0 -264
- package/source/services/tasks/Ultravisor-Task-CollectValues.cjs +0 -188
- package/source/services/tasks/Ultravisor-Task-Command.cjs +0 -65
- package/source/services/tasks/Ultravisor-Task-CommandEach.cjs +0 -190
- package/source/services/tasks/Ultravisor-Task-Conditional.cjs +0 -104
- package/source/services/tasks/Ultravisor-Task-DateWindow.cjs +0 -72
- package/source/services/tasks/Ultravisor-Task-GeneratePagedOperation.cjs +0 -336
- package/source/services/tasks/Ultravisor-Task-LaunchOperation.cjs +0 -143
- package/source/services/tasks/Ultravisor-Task-LaunchTask.cjs +0 -146
- package/source/services/tasks/Ultravisor-Task-LineMatch.cjs +0 -158
- package/source/services/tasks/Ultravisor-Task-Request.cjs +0 -56
- package/source/services/tasks/Ultravisor-Task-Solver.cjs +0 -89
- package/source/services/tasks/Ultravisor-Task-TemplateString.cjs +0 -93
- package/source/services/tasks/rest/Ultravisor-Task-GetBinary.cjs +0 -127
- package/source/services/tasks/rest/Ultravisor-Task-GetJSON.cjs +0 -119
- package/source/services/tasks/rest/Ultravisor-Task-GetText.cjs +0 -109
- package/source/services/tasks/rest/Ultravisor-Task-GetXML.cjs +0 -112
- package/source/services/tasks/rest/Ultravisor-Task-RestRequest.cjs +0 -499
- package/source/services/tasks/rest/Ultravisor-Task-SendJSON.cjs +0 -150
- package/source/services/tasks/stagingfiles/Ultravisor-Task-CopyFile.cjs +0 -110
- package/source/services/tasks/stagingfiles/Ultravisor-Task-ListFiles.cjs +0 -89
- package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadBinary.cjs +0 -87
- package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadJSON.cjs +0 -67
- package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadText.cjs +0 -66
- package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadXML.cjs +0 -69
- package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteBinary.cjs +0 -95
- package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteJSON.cjs +0 -96
- package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteText.cjs +0 -99
- package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteXML.cjs +0 -102
- package/webinterface/.babelrc +0 -6
- package/webinterface/.browserslistrc +0 -1
- package/webinterface/.browserslistrc-BACKUP +0 -1
- package/webinterface/.gulpfile-quackage-config.json +0 -7
- package/webinterface/.gulpfile-quackage.js +0 -2
- package/webinterface/source/views/PictView-Ultravisor-TaskEdit.js +0 -220
- package/webinterface/source/views/PictView-Ultravisor-TaskList.js +0 -248
- /package/docs/{cover.md → _cover.md} +0 -0
|
@@ -1,45 +1,137 @@
|
|
|
1
|
-
const libPictService = require(
|
|
1
|
+
const libPictService = require('pict-serviceproviderbase');
|
|
2
2
|
|
|
3
3
|
const libFS = require('fs');
|
|
4
4
|
const libPath = require('path');
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Persistent state store for Ultravisor.
|
|
8
|
+
*
|
|
9
|
+
* Stores Node Templates (reusable pre-configured task type instances)
|
|
10
|
+
* and Operation Definitions (graphs) in memory and persists to
|
|
11
|
+
* `.ultravisor.json` via fable's gatherProgramConfiguration system.
|
|
12
|
+
*
|
|
13
|
+
* Also manages GlobalState that persists across operation runs.
|
|
14
|
+
*
|
|
15
|
+
* Auto-generates meaningful hashes for new entities:
|
|
16
|
+
* Templates: TMPL-{TYPE}-{NNN} (e.g. TMPL-READFILE-001)
|
|
17
|
+
* Operations: OPR-{NNNN} (e.g. OPR-0001)
|
|
18
|
+
*/
|
|
6
19
|
class UltravisorHypervisorState extends libPictService
|
|
7
20
|
{
|
|
8
21
|
constructor(pPict, pOptions, pServiceHash)
|
|
9
22
|
{
|
|
10
23
|
super(pPict, pOptions, pServiceHash);
|
|
11
24
|
|
|
12
|
-
this.
|
|
25
|
+
this.serviceType = 'UltravisorHypervisorState';
|
|
26
|
+
|
|
27
|
+
// Node Templates (reusable pre-configured task type instances) keyed by Hash
|
|
28
|
+
this._NodeTemplates = {};
|
|
29
|
+
|
|
30
|
+
// Operation Definitions (with Graph) keyed by Hash
|
|
13
31
|
this._Operations = {};
|
|
14
32
|
|
|
33
|
+
// Global state (persists across runs)
|
|
34
|
+
this._GlobalState = {};
|
|
35
|
+
|
|
36
|
+
// Auto-hash counters
|
|
37
|
+
this._TemplateCounters = {};
|
|
38
|
+
this._OperationCounter = 0;
|
|
39
|
+
|
|
40
|
+
// Gather configuration
|
|
15
41
|
this._ConfigurationOutcome = this.fable.gatherProgramConfiguration(false);
|
|
16
42
|
|
|
17
|
-
// Load
|
|
43
|
+
// Load from configuration
|
|
18
44
|
let tmpConfig = this.fable.ProgramConfiguration || {};
|
|
19
45
|
|
|
20
|
-
|
|
46
|
+
// Load Node Templates (with backward compat from old TaskDefinitions key)
|
|
47
|
+
let tmpTemplateSource = tmpConfig.NodeTemplates || tmpConfig.TaskDefinitions;
|
|
48
|
+
if (tmpTemplateSource && typeof(tmpTemplateSource) === 'object')
|
|
21
49
|
{
|
|
22
|
-
let
|
|
23
|
-
for (let i = 0; i <
|
|
50
|
+
let tmpKeys = Object.keys(tmpTemplateSource);
|
|
51
|
+
for (let i = 0; i < tmpKeys.length; i++)
|
|
24
52
|
{
|
|
25
|
-
this.
|
|
53
|
+
this._NodeTemplates[tmpKeys[i]] = tmpTemplateSource[tmpKeys[i]];
|
|
26
54
|
}
|
|
27
55
|
}
|
|
28
56
|
|
|
29
57
|
if (tmpConfig.Operations && typeof(tmpConfig.Operations) === 'object')
|
|
30
58
|
{
|
|
31
|
-
let
|
|
32
|
-
for (let i = 0; i <
|
|
59
|
+
let tmpKeys = Object.keys(tmpConfig.Operations);
|
|
60
|
+
for (let i = 0; i < tmpKeys.length; i++)
|
|
33
61
|
{
|
|
34
|
-
this._Operations[
|
|
62
|
+
this._Operations[tmpKeys[i]] = tmpConfig.Operations[tmpKeys[i]];
|
|
35
63
|
}
|
|
36
64
|
}
|
|
65
|
+
|
|
66
|
+
if (tmpConfig.GlobalState && typeof(tmpConfig.GlobalState) === 'object')
|
|
67
|
+
{
|
|
68
|
+
this._GlobalState = tmpConfig.GlobalState;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (typeof(tmpConfig.OperationCounter) === 'number')
|
|
72
|
+
{
|
|
73
|
+
this._OperationCounter = tmpConfig.OperationCounter;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Load template counters (with backward compat from old TaskCounters key)
|
|
77
|
+
let tmpCounterSource = tmpConfig.TemplateCounters || tmpConfig.TaskCounters;
|
|
78
|
+
if (tmpCounterSource && typeof(tmpCounterSource) === 'object')
|
|
79
|
+
{
|
|
80
|
+
this._TemplateCounters = tmpCounterSource;
|
|
81
|
+
}
|
|
37
82
|
}
|
|
38
83
|
|
|
84
|
+
// ====================================================================
|
|
85
|
+
// Auto-Hash Generation
|
|
86
|
+
// ====================================================================
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Generate a meaningful hash for a new node template.
|
|
90
|
+
*
|
|
91
|
+
* @param {string} pType - The task type (e.g. 'read-file').
|
|
92
|
+
* @returns {string} e.g. 'TMPL-READFILE-001'
|
|
93
|
+
*/
|
|
94
|
+
generateTemplateHash(pType)
|
|
95
|
+
{
|
|
96
|
+
let tmpTypeKey = (pType || 'TEMPLATE').toUpperCase().replace(/[^A-Z0-9]/g, '');
|
|
97
|
+
|
|
98
|
+
if (!this._TemplateCounters[tmpTypeKey])
|
|
99
|
+
{
|
|
100
|
+
this._TemplateCounters[tmpTypeKey] = 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
this._TemplateCounters[tmpTypeKey]++;
|
|
104
|
+
|
|
105
|
+
let tmpCounter = String(this._TemplateCounters[tmpTypeKey]).padStart(3, '0');
|
|
106
|
+
|
|
107
|
+
return `TMPL-${tmpTypeKey}-${tmpCounter}`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Generate a meaningful hash for a new operation.
|
|
112
|
+
*
|
|
113
|
+
* @returns {string} e.g. 'OPR-0001'
|
|
114
|
+
*/
|
|
115
|
+
generateOperationHash()
|
|
116
|
+
{
|
|
117
|
+
this._OperationCounter++;
|
|
118
|
+
|
|
119
|
+
return `OPR-${String(this._OperationCounter).padStart(4, '0')}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ====================================================================
|
|
123
|
+
// Persistence
|
|
124
|
+
// ====================================================================
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Persist current state to disk via gatherProgramConfiguration path.
|
|
128
|
+
*
|
|
129
|
+
* @returns {boolean} True if state was persisted successfully.
|
|
130
|
+
*/
|
|
39
131
|
persistState()
|
|
40
132
|
{
|
|
41
|
-
// Check the _ConfigurationOutcome to see where we should be persisting state
|
|
42
133
|
let tmpFinalGatherPhasePath = false;
|
|
134
|
+
|
|
43
135
|
for (let i = 0; i < this._ConfigurationOutcome.GatherPhases.length; i++)
|
|
44
136
|
{
|
|
45
137
|
let tmpGatherPhase = this._ConfigurationOutcome.GatherPhases[i];
|
|
@@ -52,162 +144,243 @@ class UltravisorHypervisorState extends libPictService
|
|
|
52
144
|
if (!tmpFinalGatherPhasePath && this.fable.settings.ProgramConfigurationFileName)
|
|
53
145
|
{
|
|
54
146
|
tmpFinalGatherPhasePath = libPath.resolve(process.cwd(), this.fable.settings.ProgramConfigurationFileName);
|
|
55
|
-
this.
|
|
147
|
+
this.log.warn('UltravisorHypervisorState: could not determine config path; using ProgramConfigurationFileName.');
|
|
56
148
|
}
|
|
57
149
|
else if (!tmpFinalGatherPhasePath)
|
|
58
150
|
{
|
|
59
|
-
this.
|
|
60
|
-
return;
|
|
151
|
+
this.log.error('UltravisorHypervisorState: no config path available; state will not be saved.');
|
|
152
|
+
return false;
|
|
61
153
|
}
|
|
62
154
|
|
|
63
|
-
|
|
64
|
-
const tmpStateToPersist = this._ConfigurationOutcome.ConfigurationOutcome;
|
|
155
|
+
let tmpStateToPersist = this._ConfigurationOutcome.ConfigurationOutcome || {};
|
|
65
156
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const tmpTaskKeys = Object.keys(this._Tasks);
|
|
72
|
-
for (let i=0; i<tmpTaskKeys.length; i++)
|
|
73
|
-
{
|
|
74
|
-
if (tmpStateToPersist.Tasks.hasOwnProperty(tmpTaskKeys[i]))
|
|
75
|
-
{
|
|
76
|
-
tmpStateToPersist.Tasks[tmpTaskKeys[i]] = Object.assign({}, tmpStateToPersist.Tasks[tmpTaskKeys[i]], this._Tasks[tmpTaskKeys[i]]);
|
|
77
|
-
}
|
|
78
|
-
else
|
|
79
|
-
{
|
|
80
|
-
tmpStateToPersist.Tasks[tmpTaskKeys[i]] = this._Tasks[tmpTaskKeys[i]];
|
|
81
|
-
}
|
|
82
|
-
}
|
|
157
|
+
tmpStateToPersist.NodeTemplates = this._NodeTemplates;
|
|
158
|
+
tmpStateToPersist.Operations = this._Operations;
|
|
159
|
+
tmpStateToPersist.GlobalState = this._GlobalState;
|
|
160
|
+
tmpStateToPersist.OperationCounter = this._OperationCounter;
|
|
161
|
+
tmpStateToPersist.TemplateCounters = this._TemplateCounters;
|
|
83
162
|
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
const tmpOperationKeys = Object.keys(this._Operations);
|
|
89
|
-
for (let i=0; i<tmpOperationKeys.length; i++)
|
|
90
|
-
{
|
|
91
|
-
if (tmpStateToPersist.Operations.hasOwnProperty(tmpOperationKeys[i]))
|
|
92
|
-
{
|
|
93
|
-
tmpStateToPersist.Operations[tmpOperationKeys[i]] = Object.assign({}, tmpStateToPersist.Operations[tmpOperationKeys[i]], this._Operations[tmpOperationKeys[i]]);
|
|
94
|
-
}
|
|
95
|
-
else
|
|
96
|
-
{
|
|
97
|
-
tmpStateToPersist.Operations[tmpOperationKeys[i]] = this._Operations[tmpOperationKeys[i]];
|
|
98
|
-
}
|
|
99
|
-
}
|
|
163
|
+
// Remove old keys if present (migration)
|
|
164
|
+
delete tmpStateToPersist.TaskDefinitions;
|
|
165
|
+
delete tmpStateToPersist.TaskCounters;
|
|
100
166
|
|
|
101
|
-
this.
|
|
167
|
+
this.log.info(`UltravisorHypervisorState: persisting state to ${tmpFinalGatherPhasePath}`);
|
|
102
168
|
|
|
103
169
|
try
|
|
104
170
|
{
|
|
105
|
-
libFS.writeFileSync(tmpFinalGatherPhasePath, JSON.stringify(tmpStateToPersist, null,
|
|
171
|
+
libFS.writeFileSync(tmpFinalGatherPhasePath, JSON.stringify(tmpStateToPersist, null, '\t'), 'utf8');
|
|
106
172
|
}
|
|
107
|
-
catch(pError)
|
|
173
|
+
catch (pError)
|
|
108
174
|
{
|
|
109
|
-
this.
|
|
175
|
+
this.log.error(`UltravisorHypervisorState: persist error: ${pError.message}`);
|
|
110
176
|
return false;
|
|
111
177
|
}
|
|
112
178
|
|
|
113
|
-
return true
|
|
179
|
+
return true;
|
|
114
180
|
}
|
|
115
181
|
|
|
182
|
+
// ====================================================================
|
|
183
|
+
// Node Template CRUD
|
|
184
|
+
// ====================================================================
|
|
116
185
|
|
|
117
|
-
|
|
186
|
+
/**
|
|
187
|
+
* Create or update a node template.
|
|
188
|
+
*
|
|
189
|
+
* @param {object} pTemplate - The node template object.
|
|
190
|
+
* Must have a Hash (or one will be auto-generated from Type).
|
|
191
|
+
* @param {function} fCallback - function(pError, pTemplate)
|
|
192
|
+
*/
|
|
193
|
+
updateNodeTemplate(pTemplate, fCallback)
|
|
118
194
|
{
|
|
119
|
-
if (typeof(
|
|
195
|
+
if (typeof(pTemplate) !== 'object' || pTemplate === null)
|
|
120
196
|
{
|
|
121
|
-
return fCallback(new Error(
|
|
197
|
+
return fCallback(new Error('updateNodeTemplate requires a valid object.'));
|
|
122
198
|
}
|
|
123
|
-
|
|
199
|
+
|
|
200
|
+
// Auto-generate hash if not provided
|
|
201
|
+
if (!pTemplate.Hash || typeof(pTemplate.Hash) !== 'string' || pTemplate.Hash.length === 0)
|
|
124
202
|
{
|
|
125
|
-
|
|
203
|
+
pTemplate.Hash = this.generateTemplateHash(pTemplate.Type || 'TEMPLATE');
|
|
126
204
|
}
|
|
127
205
|
|
|
128
|
-
if (this.
|
|
206
|
+
if (this._NodeTemplates.hasOwnProperty(pTemplate.Hash))
|
|
129
207
|
{
|
|
130
|
-
|
|
131
|
-
|
|
208
|
+
this._NodeTemplates[pTemplate.Hash] = Object.assign(
|
|
209
|
+
this._NodeTemplates[pTemplate.Hash], pTemplate);
|
|
132
210
|
}
|
|
133
211
|
else
|
|
134
212
|
{
|
|
135
|
-
|
|
136
|
-
this._Operations[pOperation.GUIDOperation] = pOperation;
|
|
213
|
+
this._NodeTemplates[pTemplate.Hash] = pTemplate;
|
|
137
214
|
}
|
|
138
215
|
|
|
139
216
|
this.persistState();
|
|
140
217
|
|
|
141
|
-
return fCallback(null, this.
|
|
218
|
+
return fCallback(null, this._NodeTemplates[pTemplate.Hash]);
|
|
142
219
|
}
|
|
143
220
|
|
|
144
|
-
|
|
221
|
+
/**
|
|
222
|
+
* Get a node template by hash.
|
|
223
|
+
*/
|
|
224
|
+
getNodeTemplate(pHash, fCallback)
|
|
145
225
|
{
|
|
146
|
-
|
|
147
|
-
const tmpOperations = [];
|
|
148
|
-
for (let i=0; i<tmpOperationKeys.length; i++)
|
|
226
|
+
if (!this._NodeTemplates.hasOwnProperty(pHash))
|
|
149
227
|
{
|
|
150
|
-
|
|
228
|
+
return fCallback(new Error(`Node template [${pHash}] not found.`));
|
|
151
229
|
}
|
|
152
|
-
return fCallback(null,
|
|
230
|
+
return fCallback(null, this._NodeTemplates[pHash]);
|
|
153
231
|
}
|
|
154
232
|
|
|
155
|
-
|
|
233
|
+
/**
|
|
234
|
+
* List all node templates.
|
|
235
|
+
*/
|
|
236
|
+
getNodeTemplateList(fCallback)
|
|
156
237
|
{
|
|
157
|
-
|
|
238
|
+
let tmpList = [];
|
|
239
|
+
let tmpKeys = Object.keys(this._NodeTemplates);
|
|
240
|
+
|
|
241
|
+
for (let i = 0; i < tmpKeys.length; i++)
|
|
158
242
|
{
|
|
159
|
-
|
|
243
|
+
tmpList.push(this._NodeTemplates[tmpKeys[i]]);
|
|
160
244
|
}
|
|
161
|
-
|
|
245
|
+
|
|
246
|
+
return fCallback(null, tmpList);
|
|
162
247
|
}
|
|
163
248
|
|
|
249
|
+
/**
|
|
250
|
+
* Delete a node template by hash.
|
|
251
|
+
*/
|
|
252
|
+
deleteNodeTemplate(pHash, fCallback)
|
|
253
|
+
{
|
|
254
|
+
if (!this._NodeTemplates.hasOwnProperty(pHash))
|
|
255
|
+
{
|
|
256
|
+
return fCallback(new Error(`Node template [${pHash}] not found.`));
|
|
257
|
+
}
|
|
164
258
|
|
|
165
|
-
|
|
259
|
+
delete this._NodeTemplates[pHash];
|
|
260
|
+
this.persistState();
|
|
261
|
+
|
|
262
|
+
return fCallback(null, true);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ====================================================================
|
|
266
|
+
// Operation CRUD
|
|
267
|
+
// ====================================================================
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Create or update an operation.
|
|
271
|
+
*
|
|
272
|
+
* @param {object} pOperation - The operation definition.
|
|
273
|
+
* Must have a Hash (or one will be auto-generated).
|
|
274
|
+
* @param {function} fCallback - function(pError, pOperation)
|
|
275
|
+
*/
|
|
276
|
+
updateOperation(pOperation, fCallback)
|
|
166
277
|
{
|
|
167
|
-
if (typeof(
|
|
278
|
+
if (typeof(pOperation) !== 'object' || pOperation === null)
|
|
279
|
+
{
|
|
280
|
+
return fCallback(new Error('updateOperation requires a valid object.'));
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Auto-generate hash if not provided
|
|
284
|
+
if (!pOperation.Hash || typeof(pOperation.Hash) !== 'string' || pOperation.Hash.length === 0)
|
|
168
285
|
{
|
|
169
|
-
|
|
286
|
+
pOperation.Hash = this.generateOperationHash();
|
|
170
287
|
}
|
|
171
|
-
|
|
288
|
+
|
|
289
|
+
// Ensure Graph structure exists
|
|
290
|
+
if (!pOperation.Graph)
|
|
172
291
|
{
|
|
173
|
-
|
|
292
|
+
pOperation.Graph = { Nodes: [], Connections: [], ViewState: {} };
|
|
174
293
|
}
|
|
175
294
|
|
|
176
|
-
if (this.
|
|
295
|
+
if (this._Operations.hasOwnProperty(pOperation.Hash))
|
|
177
296
|
{
|
|
178
|
-
|
|
179
|
-
|
|
297
|
+
this._Operations[pOperation.Hash] = Object.assign(
|
|
298
|
+
this._Operations[pOperation.Hash], pOperation);
|
|
180
299
|
}
|
|
181
300
|
else
|
|
182
301
|
{
|
|
183
|
-
|
|
184
|
-
this.
|
|
302
|
+
pOperation.CreatedAt = pOperation.CreatedAt || new Date().toISOString();
|
|
303
|
+
this._Operations[pOperation.Hash] = pOperation;
|
|
185
304
|
}
|
|
186
305
|
|
|
306
|
+
pOperation.UpdatedAt = new Date().toISOString();
|
|
307
|
+
|
|
187
308
|
this.persistState();
|
|
188
309
|
|
|
189
|
-
return fCallback(null, this.
|
|
310
|
+
return fCallback(null, this._Operations[pOperation.Hash]);
|
|
190
311
|
}
|
|
191
312
|
|
|
192
|
-
|
|
313
|
+
/**
|
|
314
|
+
* Get an operation by hash.
|
|
315
|
+
*/
|
|
316
|
+
getOperation(pHash, fCallback)
|
|
193
317
|
{
|
|
194
|
-
|
|
195
|
-
const tmpTasks = [];
|
|
196
|
-
for (let i=0; i<tmpTaskKeys.length; i++)
|
|
318
|
+
if (!this._Operations.hasOwnProperty(pHash))
|
|
197
319
|
{
|
|
198
|
-
|
|
320
|
+
return fCallback(new Error(`Operation [${pHash}] not found.`));
|
|
199
321
|
}
|
|
200
|
-
return fCallback(null,
|
|
322
|
+
return fCallback(null, this._Operations[pHash]);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* List all operations.
|
|
327
|
+
*/
|
|
328
|
+
getOperationList(fCallback)
|
|
329
|
+
{
|
|
330
|
+
let tmpList = [];
|
|
331
|
+
let tmpKeys = Object.keys(this._Operations);
|
|
332
|
+
|
|
333
|
+
for (let i = 0; i < tmpKeys.length; i++)
|
|
334
|
+
{
|
|
335
|
+
tmpList.push(this._Operations[tmpKeys[i]]);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return fCallback(null, tmpList);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Delete an operation by hash.
|
|
343
|
+
*/
|
|
344
|
+
deleteOperation(pHash, fCallback)
|
|
345
|
+
{
|
|
346
|
+
if (!this._Operations.hasOwnProperty(pHash))
|
|
347
|
+
{
|
|
348
|
+
return fCallback(new Error(`Operation [${pHash}] not found.`));
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
delete this._Operations[pHash];
|
|
352
|
+
this.persistState();
|
|
353
|
+
|
|
354
|
+
return fCallback(null, true);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// ====================================================================
|
|
358
|
+
// Global State
|
|
359
|
+
// ====================================================================
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Get the persisted global state.
|
|
363
|
+
*
|
|
364
|
+
* @returns {object} A copy of the global state.
|
|
365
|
+
*/
|
|
366
|
+
getGlobalState()
|
|
367
|
+
{
|
|
368
|
+
return JSON.parse(JSON.stringify(this._GlobalState));
|
|
201
369
|
}
|
|
202
370
|
|
|
203
|
-
|
|
371
|
+
/**
|
|
372
|
+
* Update and persist global state.
|
|
373
|
+
*
|
|
374
|
+
* @param {object} pState - State to merge into global state.
|
|
375
|
+
*/
|
|
376
|
+
updateGlobalState(pState)
|
|
204
377
|
{
|
|
205
|
-
if (
|
|
378
|
+
if (typeof(pState) === 'object' && pState !== null)
|
|
206
379
|
{
|
|
207
|
-
|
|
380
|
+
Object.assign(this._GlobalState, pState);
|
|
381
|
+
this.persistState();
|
|
208
382
|
}
|
|
209
|
-
return fCallback(null, this._Tasks[pGUIDTask]);
|
|
210
383
|
}
|
|
211
384
|
}
|
|
212
385
|
|
|
213
|
-
module.exports = UltravisorHypervisorState;
|
|
386
|
+
module.exports = UltravisorHypervisorState;
|
|
@@ -14,6 +14,16 @@ class UltravisorHypervisor extends libPictService
|
|
|
14
14
|
this._Running = false;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Get a service instance from the fable services map.
|
|
19
|
+
*/
|
|
20
|
+
_getService(pTypeName)
|
|
21
|
+
{
|
|
22
|
+
return this.fable.servicesMap[pTypeName]
|
|
23
|
+
? Object.values(this.fable.servicesMap[pTypeName])[0]
|
|
24
|
+
: null;
|
|
25
|
+
}
|
|
26
|
+
|
|
17
27
|
get schedule()
|
|
18
28
|
{
|
|
19
29
|
return this.getSchedule();
|
|
@@ -24,47 +34,20 @@ class UltravisorHypervisor extends libPictService
|
|
|
24
34
|
return this._Schedule;
|
|
25
35
|
}
|
|
26
36
|
|
|
27
|
-
/**
|
|
28
|
-
* Add a task to the schedule.
|
|
29
|
-
*
|
|
30
|
-
* @param {string} pTaskGUID - The task GUID to schedule.
|
|
31
|
-
* @param {string} pType - Schedule type (cron, daily, hourly).
|
|
32
|
-
* @param {string} pParameters - Schedule parameters (e.g. cron expression).
|
|
33
|
-
* @param {function} fCallback - Callback.
|
|
34
|
-
*/
|
|
35
|
-
scheduleTask(pTaskGUID, pType, pParameters, fCallback)
|
|
36
|
-
{
|
|
37
|
-
let tmpScheduleEntry = {
|
|
38
|
-
GUID: `sched-task-${pTaskGUID}-${Date.now()}`,
|
|
39
|
-
TargetType: 'Task',
|
|
40
|
-
TargetGUID: pTaskGUID,
|
|
41
|
-
ScheduleType: pType || 'cron',
|
|
42
|
-
Parameters: pParameters || '0 * * * *',
|
|
43
|
-
CronExpression: this._resolveScheduleExpression(pType, pParameters),
|
|
44
|
-
Active: false,
|
|
45
|
-
CreatedAt: new Date().toISOString()
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
this._Schedule.push(tmpScheduleEntry);
|
|
49
|
-
this.log.info(`Ultravisor Hypervisor: scheduled task ${pTaskGUID} as ${tmpScheduleEntry.ScheduleType} (${tmpScheduleEntry.CronExpression})`);
|
|
50
|
-
|
|
51
|
-
return fCallback(null, tmpScheduleEntry);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
37
|
/**
|
|
55
38
|
* Add an operation to the schedule.
|
|
56
39
|
*
|
|
57
|
-
* @param {string}
|
|
40
|
+
* @param {string} pOperationHash - The operation hash to schedule.
|
|
58
41
|
* @param {string} pType - Schedule type (cron, daily, hourly).
|
|
59
42
|
* @param {string} pParameters - Schedule parameters.
|
|
60
43
|
* @param {function} fCallback - Callback.
|
|
61
44
|
*/
|
|
62
|
-
scheduleOperation(
|
|
45
|
+
scheduleOperation(pOperationHash, pType, pParameters, fCallback)
|
|
63
46
|
{
|
|
64
47
|
let tmpScheduleEntry = {
|
|
65
|
-
GUID: `sched-op-${
|
|
48
|
+
GUID: `sched-op-${pOperationHash}-${Date.now()}`,
|
|
66
49
|
TargetType: 'Operation',
|
|
67
|
-
|
|
50
|
+
TargetHash: pOperationHash,
|
|
68
51
|
ScheduleType: pType || 'cron',
|
|
69
52
|
Parameters: pParameters || '0 * * * *',
|
|
70
53
|
CronExpression: this._resolveScheduleExpression(pType, pParameters),
|
|
@@ -73,7 +56,7 @@ class UltravisorHypervisor extends libPictService
|
|
|
73
56
|
};
|
|
74
57
|
|
|
75
58
|
this._Schedule.push(tmpScheduleEntry);
|
|
76
|
-
this.log.info(`Ultravisor Hypervisor: scheduled operation ${
|
|
59
|
+
this.log.info(`Ultravisor Hypervisor: scheduled operation ${pOperationHash} as ${tmpScheduleEntry.ScheduleType} (${tmpScheduleEntry.CronExpression})`);
|
|
77
60
|
|
|
78
61
|
return fCallback(null, tmpScheduleEntry);
|
|
79
62
|
}
|
|
@@ -103,10 +86,9 @@ class UltravisorHypervisor extends libPictService
|
|
|
103
86
|
*/
|
|
104
87
|
startSchedule(fCallback)
|
|
105
88
|
{
|
|
106
|
-
let tmpCronService = this.
|
|
107
|
-
let
|
|
108
|
-
let
|
|
109
|
-
let tmpStateService = this.fable['Ultravisor-Hypervisor-State'];
|
|
89
|
+
let tmpCronService = this._getService('UltravisorHypervisorEventCron');
|
|
90
|
+
let tmpStateService = this._getService('UltravisorHypervisorState');
|
|
91
|
+
let tmpEngine = this._getService('UltravisorExecutionEngine');
|
|
110
92
|
|
|
111
93
|
this._Running = true;
|
|
112
94
|
|
|
@@ -124,55 +106,28 @@ class UltravisorHypervisor extends libPictService
|
|
|
124
106
|
tmpCronService.start(tmpEntry,
|
|
125
107
|
(pScheduleEntry) =>
|
|
126
108
|
{
|
|
127
|
-
// On tick, execute the target
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
(pError
|
|
109
|
+
// On tick, execute the target operation
|
|
110
|
+
tmpStateService.getOperation(pScheduleEntry.TargetHash,
|
|
111
|
+
(pError, pOperation) =>
|
|
112
|
+
{
|
|
113
|
+
if (pError)
|
|
132
114
|
{
|
|
133
|
-
|
|
115
|
+
this.log.error(`Ultravisor Hypervisor: scheduled operation ${pScheduleEntry.TargetHash} not found: ${pError.message}`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
tmpEngine.executeOperation(pOperation,
|
|
119
|
+
(pExecError, pContext) =>
|
|
134
120
|
{
|
|
135
|
-
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
tmpTaskService.executeTask(pTask, {},
|
|
139
|
-
(pTaskError, pResult) =>
|
|
121
|
+
if (pExecError)
|
|
140
122
|
{
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
else
|
|
146
|
-
{
|
|
147
|
-
this.log.info(`Ultravisor Hypervisor: scheduled task ${pScheduleEntry.TargetGUID} completed: ${pResult.Status}`);
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
else if (pScheduleEntry.TargetType === 'Operation')
|
|
153
|
-
{
|
|
154
|
-
tmpStateService.getOperation(pScheduleEntry.TargetGUID,
|
|
155
|
-
(pError, pOperation) =>
|
|
156
|
-
{
|
|
157
|
-
if (pError)
|
|
158
|
-
{
|
|
159
|
-
this.log.error(`Ultravisor Hypervisor: scheduled operation ${pScheduleEntry.TargetGUID} not found: ${pError.message}`);
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
tmpOperationService.executeOperation(pOperation,
|
|
163
|
-
(pOpError, pManifest) =>
|
|
123
|
+
this.log.error(`Ultravisor Hypervisor: scheduled operation execution error: ${pExecError.message}`);
|
|
124
|
+
}
|
|
125
|
+
else
|
|
164
126
|
{
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
else
|
|
170
|
-
{
|
|
171
|
-
this.log.info(`Ultravisor Hypervisor: scheduled operation ${pScheduleEntry.TargetGUID} completed: ${pManifest.Status}`);
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
}
|
|
127
|
+
this.log.info(`Ultravisor Hypervisor: scheduled operation ${pScheduleEntry.TargetHash} completed: ${pContext.Status}`);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
});
|
|
176
131
|
});
|
|
177
132
|
}
|
|
178
133
|
|
|
@@ -189,7 +144,7 @@ class UltravisorHypervisor extends libPictService
|
|
|
189
144
|
*/
|
|
190
145
|
stopSchedule(fCallback)
|
|
191
146
|
{
|
|
192
|
-
let tmpCronService = this.
|
|
147
|
+
let tmpCronService = this._getService('UltravisorHypervisorEventCron');
|
|
193
148
|
|
|
194
149
|
tmpCronService.stop();
|
|
195
150
|
this._Running = false;
|
|
@@ -212,7 +167,7 @@ class UltravisorHypervisor extends libPictService
|
|
|
212
167
|
*/
|
|
213
168
|
removeScheduleEntry(pGUID, fCallback)
|
|
214
169
|
{
|
|
215
|
-
let tmpCronService = this.
|
|
170
|
+
let tmpCronService = this._getService('UltravisorHypervisorEventCron');
|
|
216
171
|
|
|
217
172
|
for (let i = 0; i < this._Schedule.length; i++)
|
|
218
173
|
{
|