@nasa-jpl/aerie-sequence-languages 0.0.1

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.
Files changed (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2 -0
  3. package/dist/cjs/converters/satf-sasf-utils.d.ts +48 -0
  4. package/dist/cjs/converters/satf-sasf-utils.d.ts.map +1 -0
  5. package/dist/cjs/converters/satf-sasf-utils.js +939 -0
  6. package/dist/cjs/converters/satf-sasf-utils.js.map +1 -0
  7. package/dist/cjs/converters/seqJsonToSeqn.d.ts +6 -0
  8. package/dist/cjs/converters/seqJsonToSeqn.d.ts.map +1 -0
  9. package/dist/cjs/converters/seqJsonToSeqn.js +310 -0
  10. package/dist/cjs/converters/seqJsonToSeqn.js.map +1 -0
  11. package/dist/cjs/converters/seqnToSeqJson.d.ts +11 -0
  12. package/dist/cjs/converters/seqnToSeqJson.d.ts.map +1 -0
  13. package/dist/cjs/converters/seqnToSeqJson.js +713 -0
  14. package/dist/cjs/converters/seqnToSeqJson.js.map +1 -0
  15. package/dist/cjs/index.d.ts +9 -0
  16. package/dist/cjs/index.d.ts.map +1 -0
  17. package/dist/cjs/index.js +22 -0
  18. package/dist/cjs/index.js.map +1 -0
  19. package/dist/cjs/languages/satf/constants/satf-sasf-constants.d.ts +139 -0
  20. package/dist/cjs/languages/satf/constants/satf-sasf-constants.d.ts.map +1 -0
  21. package/dist/cjs/languages/satf/constants/satf-sasf-constants.js +143 -0
  22. package/dist/cjs/languages/satf/constants/satf-sasf-constants.js.map +1 -0
  23. package/dist/cjs/languages/satf/grammar/satf-sasf.d.ts +4 -0
  24. package/dist/cjs/languages/satf/grammar/satf-sasf.d.ts.map +1 -0
  25. package/dist/cjs/languages/satf/grammar/satf-sasf.grammar.d.ts +3 -0
  26. package/dist/cjs/languages/satf/grammar/satf-sasf.grammar.d.ts.map +1 -0
  27. package/dist/cjs/languages/satf/grammar/satf-sasf.grammar.js +20 -0
  28. package/dist/cjs/languages/satf/grammar/satf-sasf.grammar.js.map +1 -0
  29. package/dist/cjs/languages/satf/grammar/satf-sasf.grammar.terms.d.ts +69 -0
  30. package/dist/cjs/languages/satf/grammar/satf-sasf.grammar.terms.d.ts.map +1 -0
  31. package/dist/cjs/languages/satf/grammar/satf-sasf.grammar.terms.js +7 -0
  32. package/dist/cjs/languages/satf/grammar/satf-sasf.grammar.terms.js.map +1 -0
  33. package/dist/cjs/languages/satf/grammar/satf-sasf.js +56 -0
  34. package/dist/cjs/languages/satf/grammar/satf-sasf.js.map +1 -0
  35. package/dist/cjs/languages/satf/types/types.d.ts +23 -0
  36. package/dist/cjs/languages/satf/types/types.d.ts.map +1 -0
  37. package/dist/cjs/languages/satf/types/types.js +3 -0
  38. package/dist/cjs/languages/satf/types/types.js.map +1 -0
  39. package/dist/cjs/languages/seq-n/seq-n.d.ts +2 -0
  40. package/dist/cjs/languages/seq-n/seq-n.d.ts.map +1 -0
  41. package/dist/cjs/languages/seq-n/seq-n.grammar.d.ts +3 -0
  42. package/dist/cjs/languages/seq-n/seq-n.grammar.d.ts.map +1 -0
  43. package/dist/cjs/languages/seq-n/seq-n.grammar.js +24 -0
  44. package/dist/cjs/languages/seq-n/seq-n.grammar.js.map +1 -0
  45. package/dist/cjs/languages/seq-n/seq-n.grammar.terms.d.ts +39 -0
  46. package/dist/cjs/languages/seq-n/seq-n.grammar.terms.d.ts.map +1 -0
  47. package/dist/cjs/languages/seq-n/seq-n.grammar.terms.js +6 -0
  48. package/dist/cjs/languages/seq-n/seq-n.grammar.terms.js.map +1 -0
  49. package/dist/cjs/languages/seq-n/seq-n.js +6 -0
  50. package/dist/cjs/languages/seq-n/seq-n.js.map +1 -0
  51. package/dist/cjs/languages/seq-n/seqn-grammar-constants.d.ts +45 -0
  52. package/dist/cjs/languages/seq-n/seqn-grammar-constants.d.ts.map +1 -0
  53. package/dist/cjs/languages/seq-n/seqn-grammar-constants.js +48 -0
  54. package/dist/cjs/languages/seq-n/seqn-grammar-constants.js.map +1 -0
  55. package/dist/cjs/logger.d.ts +3 -0
  56. package/dist/cjs/logger.d.ts.map +1 -0
  57. package/dist/cjs/logger.js +11 -0
  58. package/dist/cjs/logger.js.map +1 -0
  59. package/dist/cjs/utils/string.d.ts +8 -0
  60. package/dist/cjs/utils/string.d.ts.map +1 -0
  61. package/dist/cjs/utils/string.js +32 -0
  62. package/dist/cjs/utils/string.js.map +1 -0
  63. package/dist/esm/converters/satf-sasf-utils.d.ts +48 -0
  64. package/dist/esm/converters/satf-sasf-utils.d.ts.map +1 -0
  65. package/dist/esm/converters/satf-sasf-utils.js +927 -0
  66. package/dist/esm/converters/satf-sasf-utils.js.map +1 -0
  67. package/dist/esm/converters/seqJsonToSeqn.d.ts +6 -0
  68. package/dist/esm/converters/seqJsonToSeqn.d.ts.map +1 -0
  69. package/dist/esm/converters/seqJsonToSeqn.js +305 -0
  70. package/dist/esm/converters/seqJsonToSeqn.js.map +1 -0
  71. package/dist/esm/converters/seqnToSeqJson.d.ts +11 -0
  72. package/dist/esm/converters/seqnToSeqJson.d.ts.map +1 -0
  73. package/dist/esm/converters/seqnToSeqJson.js +705 -0
  74. package/dist/esm/converters/seqnToSeqJson.js.map +1 -0
  75. package/dist/esm/index.d.ts +9 -0
  76. package/dist/esm/index.d.ts.map +1 -0
  77. package/dist/esm/index.js +8 -0
  78. package/dist/esm/index.js.map +1 -0
  79. package/dist/esm/languages/satf/constants/satf-sasf-constants.d.ts +139 -0
  80. package/dist/esm/languages/satf/constants/satf-sasf-constants.d.ts.map +1 -0
  81. package/dist/esm/languages/satf/constants/satf-sasf-constants.js +139 -0
  82. package/dist/esm/languages/satf/constants/satf-sasf-constants.js.map +1 -0
  83. package/dist/esm/languages/satf/grammar/satf-sasf.d.ts +4 -0
  84. package/dist/esm/languages/satf/grammar/satf-sasf.d.ts.map +1 -0
  85. package/dist/esm/languages/satf/grammar/satf-sasf.grammar.d.ts +3 -0
  86. package/dist/esm/languages/satf/grammar/satf-sasf.grammar.d.ts.map +1 -0
  87. package/dist/esm/languages/satf/grammar/satf-sasf.grammar.js +17 -0
  88. package/dist/esm/languages/satf/grammar/satf-sasf.grammar.js.map +1 -0
  89. package/dist/esm/languages/satf/grammar/satf-sasf.grammar.terms.d.ts +69 -0
  90. package/dist/esm/languages/satf/grammar/satf-sasf.grammar.terms.d.ts.map +1 -0
  91. package/dist/esm/languages/satf/grammar/satf-sasf.grammar.terms.js +3 -0
  92. package/dist/esm/languages/satf/grammar/satf-sasf.grammar.terms.js.map +1 -0
  93. package/dist/esm/languages/satf/grammar/satf-sasf.js +53 -0
  94. package/dist/esm/languages/satf/grammar/satf-sasf.js.map +1 -0
  95. package/dist/esm/languages/satf/types/types.d.ts +23 -0
  96. package/dist/esm/languages/satf/types/types.d.ts.map +1 -0
  97. package/dist/esm/languages/satf/types/types.js +2 -0
  98. package/dist/esm/languages/satf/types/types.js.map +1 -0
  99. package/dist/esm/languages/seq-n/seq-n.d.ts +2 -0
  100. package/dist/esm/languages/seq-n/seq-n.d.ts.map +1 -0
  101. package/dist/esm/languages/seq-n/seq-n.grammar.d.ts +3 -0
  102. package/dist/esm/languages/seq-n/seq-n.grammar.d.ts.map +1 -0
  103. package/dist/esm/languages/seq-n/seq-n.grammar.js +21 -0
  104. package/dist/esm/languages/seq-n/seq-n.grammar.js.map +1 -0
  105. package/dist/esm/languages/seq-n/seq-n.grammar.terms.d.ts +39 -0
  106. package/dist/esm/languages/seq-n/seq-n.grammar.terms.d.ts.map +1 -0
  107. package/dist/esm/languages/seq-n/seq-n.grammar.terms.js +3 -0
  108. package/dist/esm/languages/seq-n/seq-n.grammar.terms.js.map +1 -0
  109. package/dist/esm/languages/seq-n/seq-n.js +3 -0
  110. package/dist/esm/languages/seq-n/seq-n.js.map +1 -0
  111. package/dist/esm/languages/seq-n/seqn-grammar-constants.d.ts +45 -0
  112. package/dist/esm/languages/seq-n/seqn-grammar-constants.d.ts.map +1 -0
  113. package/dist/esm/languages/seq-n/seqn-grammar-constants.js +45 -0
  114. package/dist/esm/languages/seq-n/seqn-grammar-constants.js.map +1 -0
  115. package/dist/esm/logger.d.ts +3 -0
  116. package/dist/esm/logger.d.ts.map +1 -0
  117. package/dist/esm/logger.js +7 -0
  118. package/dist/esm/logger.js.map +1 -0
  119. package/dist/esm/utils/string.d.ts +8 -0
  120. package/dist/esm/utils/string.d.ts.map +1 -0
  121. package/dist/esm/utils/string.js +25 -0
  122. package/dist/esm/utils/string.js.map +1 -0
  123. package/package.json +74 -0
@@ -0,0 +1,705 @@
1
+ import { SEQN_NODES } from '../languages/seq-n/seqn-grammar-constants';
2
+ import { getBalancedDuration, getDurationTimeComponents, parseDurationString, TimeTypes, validateTime, } from '@nasa-jpl/aerie-time-utils';
3
+ import { logInfo } from '../logger';
4
+ import { removeEscapedQuotes, unquoteUnescape } from '../utils/string';
5
+ /**
6
+ * Returns a minimal valid Seq JSON object.
7
+ * Use for getting a default Seq JSON throughout the application.
8
+ */
9
+ function seqJsonDefault() {
10
+ return { id: '', metadata: {} };
11
+ }
12
+ /**
13
+ * Walks the sequence parse tree and converts it to a valid Seq JSON object.
14
+ */
15
+ export function seqnToSeqJson(node, text, commandDictionary, sequenceName) {
16
+ const baseNode = node.topNode;
17
+ const seqJson = seqJsonDefault();
18
+ const variableList = [];
19
+ seqJson.id = parseId(baseNode, text, sequenceName);
20
+ seqJson.metadata = { ...parseLGO(baseNode), ...parseMetadata(baseNode, text) };
21
+ seqJson.locals = parseVariables(baseNode, text, 'LocalDeclaration') ?? undefined;
22
+ if (seqJson.locals) {
23
+ variableList.push(...seqJson.locals.map(value => value.name));
24
+ }
25
+ seqJson.parameters = parseVariables(baseNode, text, 'ParameterDeclaration') ?? undefined;
26
+ if (seqJson.parameters) {
27
+ variableList.push(...seqJson.parameters.map(value => value.name));
28
+ }
29
+ let child = baseNode.getChild('Commands')?.firstChild;
30
+ seqJson.steps = [];
31
+ while (child) {
32
+ const step = parseStep(child, text, commandDictionary);
33
+ if (step) {
34
+ seqJson.steps.push(step);
35
+ }
36
+ child = child?.nextSibling;
37
+ }
38
+ if (!seqJson.steps.length) {
39
+ seqJson.steps = undefined;
40
+ }
41
+ seqJson.immediate_commands =
42
+ parseImmediateCommand(baseNode.getChild('ImmediateCommands'), text, commandDictionary) ?? undefined;
43
+ seqJson.hardware_commands =
44
+ baseNode
45
+ .getChild('HardwareCommands')
46
+ ?.getChildren('Command')
47
+ .map(command => parseHardwareCommand(command, text)) ?? undefined;
48
+ seqJson.requests = baseNode
49
+ .getChild('Commands')
50
+ ?.getChildren('Request')
51
+ .map(requestNode => parseRequest(requestNode, text, commandDictionary));
52
+ if (seqJson.requests?.length === 0) {
53
+ seqJson.requests = undefined;
54
+ }
55
+ return seqJson;
56
+ }
57
+ function parseRequest(requestNode, text, commandDictionary) {
58
+ let groundEpoch = undefined;
59
+ let time = undefined;
60
+ const groundEpochNode = requestNode.getChild('TimeTag')?.getChild('TimeGroundEpoch');
61
+ if (groundEpochNode) {
62
+ groundEpoch = parseGroundEpoch(requestNode.getChild('TimeTag'), text);
63
+ }
64
+ else {
65
+ time = parseTime(requestNode, text);
66
+ }
67
+ const nameNode = requestNode.getChild('RequestName');
68
+ const name = nameNode ? unquoteUnescape(text.slice(nameNode.from, nameNode.to)) : 'UNKNOWN';
69
+ const description = parseDescription(requestNode, text);
70
+ const metadata = parseMetadata(requestNode, text);
71
+ const steps = [];
72
+ const stepsNode = requestNode.getChild('Steps');
73
+ if (stepsNode) {
74
+ let stepNode = stepsNode.firstChild;
75
+ while (stepNode) {
76
+ const step = parseStep(stepNode, text, commandDictionary);
77
+ if (step) {
78
+ steps.push(step);
79
+ }
80
+ stepNode = stepNode?.nextSibling;
81
+ }
82
+ }
83
+ if (steps.length === 0) {
84
+ // request with empty steps is disallowed in seq.json
85
+ steps.push({
86
+ args: [],
87
+ stem: 'UNKNOWN',
88
+ time: { tag: 'UNKNOWN', type: 'ABSOLUTE' },
89
+ type: 'command',
90
+ });
91
+ }
92
+ // ground epoch
93
+ return {
94
+ description,
95
+ ground_epoch: groundEpoch,
96
+ metadata,
97
+ name,
98
+ steps: steps,
99
+ time,
100
+ type: 'request',
101
+ };
102
+ }
103
+ function parseNote(stepNode, text) {
104
+ const time = parseTime(stepNode, text);
105
+ const noteValueNode = stepNode.getChild('NoteValue');
106
+ const noteValue = noteValueNode ? unquoteUnescape(text.slice(noteValueNode.from, noteValueNode.to)) : 'UNKNOWN';
107
+ const description = parseDescription(stepNode, text);
108
+ const metadata = parseMetadata(stepNode, text);
109
+ const models = parseModel(stepNode, text);
110
+ return {
111
+ description,
112
+ metadata,
113
+ models,
114
+ string_arg: noteValue,
115
+ time,
116
+ type: 'note',
117
+ };
118
+ }
119
+ function parseGroundBlockEvent(stepNode, text) {
120
+ const time = parseTime(stepNode, text);
121
+ const nameNode = stepNode.getChild('GroundName');
122
+ const name = nameNode ? unquoteUnescape(text.slice(nameNode.from, nameNode.to)) : 'UNKNOWN';
123
+ const argsNode = stepNode.getChild('Args');
124
+ // step not in dictionary, so not passing command dict
125
+ const args = argsNode ? parseArgs(argsNode, text, null, name) : [];
126
+ const description = parseDescription(stepNode, text);
127
+ const metadata = parseMetadata(stepNode, text);
128
+ const models = parseModel(stepNode, text);
129
+ return {
130
+ args,
131
+ description,
132
+ metadata,
133
+ models,
134
+ name,
135
+ time,
136
+ type: stepNode.name === 'GroundBlock' ? 'ground_block' : 'ground_event',
137
+ };
138
+ }
139
+ function parseActivateLoad(stepNode, text, isRTC) {
140
+ const time = parseTime(stepNode, text);
141
+ const nameNode = stepNode.getChild('SequenceName');
142
+ const sequence = nameNode ? unquoteUnescape(text.slice(nameNode.from, nameNode.to)) : 'UNKNOWN';
143
+ const argsNode = stepNode.getChild('Args');
144
+ // step not in dictionary, so not passing command dict
145
+ const args = argsNode ? parseArgs(argsNode, text, null, sequence) : [];
146
+ const engine = parseEngine(stepNode, text);
147
+ const epoch = parseEpoch(stepNode, text);
148
+ const description = parseDescription(stepNode, text);
149
+ const metadata = parseMetadata(stepNode, text);
150
+ const models = parseModel(stepNode, text);
151
+ if (stepNode.name === 'Activate') {
152
+ if (!isRTC) {
153
+ return {
154
+ args,
155
+ description,
156
+ engine,
157
+ epoch,
158
+ metadata,
159
+ models,
160
+ sequence,
161
+ time,
162
+ type: 'activate',
163
+ };
164
+ }
165
+ return {
166
+ args,
167
+ description,
168
+ engine,
169
+ epoch,
170
+ metadata,
171
+ models,
172
+ sequence,
173
+ type: 'immediate_activate',
174
+ };
175
+ }
176
+ else {
177
+ if (!isRTC) {
178
+ return {
179
+ args,
180
+ description,
181
+ engine,
182
+ epoch,
183
+ metadata,
184
+ models,
185
+ sequence,
186
+ time,
187
+ type: 'load',
188
+ };
189
+ }
190
+ return {
191
+ args,
192
+ description,
193
+ engine,
194
+ epoch,
195
+ metadata,
196
+ models,
197
+ sequence,
198
+ type: 'immediate_load',
199
+ };
200
+ }
201
+ }
202
+ function parseEngine(stepNode, text) {
203
+ const engineNode = stepNode.getChild('Engine')?.getChild('Number');
204
+ return engineNode ? parseInt(text.slice(engineNode.from, engineNode.to), 10) : undefined;
205
+ }
206
+ function parseEpoch(stepNode, text) {
207
+ const epochNode = stepNode.getChild('Epoch')?.getChild('String');
208
+ return epochNode ? unquoteUnescape(text.slice(epochNode.from, epochNode.to)) : undefined;
209
+ }
210
+ function parseStep(child, text, commandDictionary) {
211
+ switch (child.name) {
212
+ case 'Command':
213
+ return parseCommand(child, text, commandDictionary);
214
+ case 'Activate':
215
+ case 'Load':
216
+ return parseActivateLoad(child, text);
217
+ case 'GroundBlock':
218
+ case 'GroundEvent':
219
+ return parseGroundBlockEvent(child, text);
220
+ case 'Note':
221
+ return parseNote(child, text);
222
+ }
223
+ // Standalone comment nodes (not descriptions of steps), are not supported in the seq.json schema
224
+ // Until a schema change is coordinated, comments will dropped while writing out seq.json.
225
+ // Requests are parsed outside this block since they are not allowed to be nested.
226
+ return null;
227
+ }
228
+ function parseLGO(node) {
229
+ const lgoNode = node.getChild('Commands')?.getChild('LoadAndGoDirective');
230
+ if (!lgoNode) {
231
+ return undefined;
232
+ }
233
+ return {
234
+ lgo: true,
235
+ };
236
+ }
237
+ function parseArg(node, text, dictionaryArg) {
238
+ const nodeValue = text.slice(node.from, node.to);
239
+ if (node.name === 'Boolean') {
240
+ const value = nodeValue === 'true' ? true : false;
241
+ const booleanArg = { type: 'boolean', value };
242
+ if (dictionaryArg) {
243
+ booleanArg.name = dictionaryArg.name;
244
+ }
245
+ return booleanArg;
246
+ }
247
+ else if (node.name === 'Enum') {
248
+ const value = nodeValue;
249
+ const enumArg = { type: 'symbol', value };
250
+ if (dictionaryArg) {
251
+ enumArg.name = dictionaryArg.name;
252
+ }
253
+ return enumArg;
254
+ }
255
+ else if (node.name === 'Number') {
256
+ if (nodeValue.slice(0, 2) === '0x') {
257
+ const hexArg = { type: 'hex', value: nodeValue };
258
+ if (dictionaryArg) {
259
+ hexArg.name = dictionaryArg.name;
260
+ }
261
+ return hexArg;
262
+ }
263
+ else {
264
+ const value = parseFloat(nodeValue);
265
+ const numberArg = { type: 'number', value };
266
+ if (dictionaryArg) {
267
+ numberArg.name = dictionaryArg.name;
268
+ }
269
+ return numberArg;
270
+ }
271
+ }
272
+ else if (node.name === 'String') {
273
+ const value = JSON.parse(nodeValue);
274
+ const arg = { type: 'string', value };
275
+ if (dictionaryArg) {
276
+ arg.name = dictionaryArg.name;
277
+ }
278
+ return arg;
279
+ }
280
+ }
281
+ function parseRepeatArgs(repeatArgsNode, text, dictRepeatArgument) {
282
+ const repeatArg = { name: dictRepeatArgument?.name, type: 'repeat', value: [] };
283
+ const repeatArgs = dictRepeatArgument?.repeat?.arguments;
284
+ const repeatArgsLength = repeatArgs?.length ?? Infinity;
285
+ let repeatArgNode = repeatArgsNode;
286
+ if (repeatArgNode) {
287
+ let args = [];
288
+ let argNode = repeatArgNode.firstChild;
289
+ let i = 0;
290
+ while (argNode) {
291
+ if (i % repeatArgsLength === 0) {
292
+ // [[1 2] [3 4]] in seq.json is flattened in seqN [1 2 3 4]
293
+ // dictionary definition is required to disambiguate
294
+ args = [];
295
+ repeatArg.value.push(args);
296
+ }
297
+ const arg = parseArg(argNode, text, repeatArgs?.[i % repeatArgsLength] ?? null);
298
+ if (arg) {
299
+ args.push(arg);
300
+ }
301
+ else {
302
+ logInfo(`Could not parse arg for node with name ${argNode.name}`);
303
+ }
304
+ argNode = argNode.nextSibling;
305
+ i++;
306
+ }
307
+ repeatArgNode = repeatArgNode.nextSibling;
308
+ }
309
+ return repeatArg;
310
+ }
311
+ function parseArgs(argsNode, text, commandDictionary, stem) {
312
+ const args = [];
313
+ let argNode = argsNode.firstChild;
314
+ const dictArguments = commandDictionary?.fswCommandMap[stem]?.arguments ?? [];
315
+ let i = 0;
316
+ while (argNode) {
317
+ const dictArg = dictArguments[i] ?? null;
318
+ if (argNode.name === SEQN_NODES.REPEAT_ARG) {
319
+ const arg = parseRepeatArgs(argNode, text, dictArg ?? null);
320
+ if (arg) {
321
+ args.push(arg);
322
+ }
323
+ else {
324
+ logInfo(`Could not parse repeat arg for node with name ${argNode.name}`);
325
+ }
326
+ }
327
+ else {
328
+ const arg = parseArg(argNode, text, dictArg);
329
+ if (arg) {
330
+ args.push(arg);
331
+ }
332
+ else {
333
+ logInfo(`Could not parse arg for node with name ${argNode.name}`);
334
+ }
335
+ }
336
+ argNode = argNode?.nextSibling;
337
+ ++i;
338
+ }
339
+ return args;
340
+ }
341
+ function parseGroundEpoch(groundEpochNode, text) {
342
+ if (!groundEpochNode) {
343
+ return { delta: '', name: '' };
344
+ }
345
+ const nameNode = groundEpochNode.getChild('Name');
346
+ let tag = '';
347
+ if (groundEpochNode.parent) {
348
+ const time = parseTime(groundEpochNode.parent, text);
349
+ if (time.type !== 'COMMAND_COMPLETE') {
350
+ tag = time.tag;
351
+ }
352
+ }
353
+ return {
354
+ delta: tag,
355
+ name: nameNode ? unquoteUnescape(text.slice(nameNode.from, nameNode.to)) : '',
356
+ };
357
+ }
358
+ /**
359
+ * Parses a time tag node and returns a Seq JSON time.
360
+ * Defaults to an unknown absolute time if a command does not have a valid time tag.
361
+ */
362
+ function parseTime(commandNode, text) {
363
+ const timeTagNode = commandNode.getChild('TimeTag');
364
+ let tag = 'UNKNOWN';
365
+ if (timeTagNode == null) {
366
+ return { tag, type: 'ABSOLUTE' };
367
+ }
368
+ const timeTagAbsoluteNode = timeTagNode.getChild('TimeAbsolute');
369
+ const timeTagCompleteNode = timeTagNode.getChild('TimeComplete');
370
+ const timeTagEpochNode = timeTagNode.getChild('TimeEpoch') || timeTagNode.getChild('TimeGroundEpoch');
371
+ const timeTagRelativeNode = timeTagNode.getChild('TimeRelative');
372
+ const timeTagBlockRelativeNode = timeTagNode.getChild('TimeBlockRelative');
373
+ if (timeTagCompleteNode) {
374
+ return { type: 'COMMAND_COMPLETE' };
375
+ }
376
+ if (!timeTagAbsoluteNode && !timeTagEpochNode && !timeTagRelativeNode && !timeTagBlockRelativeNode) {
377
+ return { tag, type: 'ABSOLUTE' };
378
+ }
379
+ if (timeTagAbsoluteNode) {
380
+ const absoluteTag = text.slice(timeTagAbsoluteNode.from + 1, timeTagAbsoluteNode.to).trim();
381
+ return { tag: absoluteTag, type: 'ABSOLUTE' };
382
+ }
383
+ else if (timeTagEpochNode) {
384
+ const timeTagEpochText = text.slice(timeTagEpochNode.from + 1, timeTagEpochNode.to).trim();
385
+ // a regex to determine if this string [+/-]####T##:##:##.###
386
+ if (validateTime(timeTagEpochText, TimeTypes.DOY_TIME) || validateTime(timeTagEpochText, TimeTypes.SECOND_TIME)) {
387
+ const { isNegative, days, hours, minutes, seconds, milliseconds } = getDurationTimeComponents(parseDurationString(timeTagEpochText, 'seconds'));
388
+ tag = `${isNegative}${days}${days ? 'T' : ''}${hours}:${minutes}:${seconds}${milliseconds ? '.' : ''}${milliseconds}`;
389
+ return { tag, type: 'EPOCH_RELATIVE' };
390
+ }
391
+ // a regex to determine if this string [+/-]###.###
392
+ if (validateTime(timeTagEpochText, TimeTypes.SECOND_TIME)) {
393
+ tag = getBalancedDuration(timeTagEpochText);
394
+ if (parseDurationString(tag, 'seconds').milliseconds === 0) {
395
+ tag = tag.slice(0, -4);
396
+ }
397
+ return { tag, type: 'EPOCH_RELATIVE' };
398
+ }
399
+ }
400
+ else if (timeTagRelativeNode) {
401
+ const timeTagRelativeText = text.slice(timeTagRelativeNode.from + 1, timeTagRelativeNode.to).trim();
402
+ // a regex to determine if this string ####T##:##:##.###
403
+ if (validateTime(timeTagRelativeText, TimeTypes.DOY_TIME)) {
404
+ const { isNegative, days, hours, minutes, seconds, milliseconds } = getDurationTimeComponents(parseDurationString(timeTagRelativeText, 'seconds'));
405
+ tag = `${isNegative}${days}${days ? 'T' : ''}${hours}:${minutes}:${seconds}${milliseconds ? '.' : ''}${milliseconds}`;
406
+ return { tag, type: 'COMMAND_RELATIVE' };
407
+ }
408
+ if (validateTime(timeTagRelativeText, TimeTypes.SECOND_TIME)) {
409
+ tag = getBalancedDuration(timeTagRelativeText);
410
+ if (parseDurationString(tag).milliseconds === 0) {
411
+ tag = tag.slice(0, -4);
412
+ }
413
+ return { tag, type: 'COMMAND_RELATIVE' };
414
+ }
415
+ }
416
+ else if (timeTagBlockRelativeNode) {
417
+ const timeTagBlockRelativeText = text.slice(timeTagBlockRelativeNode.from + 1, timeTagBlockRelativeNode.to).trim();
418
+ if (validateTime(timeTagBlockRelativeText, TimeTypes.DOY_TIME)) {
419
+ const { isNegative, days, hours, minutes, seconds, milliseconds } = getDurationTimeComponents(parseDurationString(timeTagBlockRelativeText, 'seconds'));
420
+ tag = `${isNegative}${days}${days ? 'T' : ''}${hours}:${minutes}:${seconds}${milliseconds ? '.' : ''}${milliseconds}`;
421
+ return { tag, type: 'BLOCK_RELATIVE' };
422
+ }
423
+ }
424
+ return { tag, type: 'ABSOLUTE' };
425
+ }
426
+ export function parseVariables(node, text, type = 'LocalDeclaration') {
427
+ const variableContainer = node.getChild(type);
428
+ if (!variableContainer) {
429
+ return undefined;
430
+ }
431
+ const variables = variableContainer.getChildren('Variable');
432
+ if (!variables || variables.length === 0) {
433
+ return undefined;
434
+ }
435
+ return variables.map((variableNode) => {
436
+ const nameNode = variableNode.getChild('Enum');
437
+ const typeNode = variableNode.getChild('Type');
438
+ const enumNode = variableNode.getChild('EnumName');
439
+ const rangeNode = variableNode.getChild('Range');
440
+ const allowableValuesNode = variableNode.getChild('Values');
441
+ const objects = variableNode.getChildren('Object');
442
+ const variableText = nameNode ? text.slice(nameNode.from, nameNode.to) : 'UNKNOWN';
443
+ const variable = { name: variableText, type: 'INT' };
444
+ if (typeNode) {
445
+ variable.type = text.slice(typeNode.from, typeNode.to);
446
+ if (enumNode) {
447
+ variable.enum_name = text.slice(enumNode.from, enumNode.to);
448
+ }
449
+ if (rangeNode) {
450
+ const allowableRanges = parseAllowableRanges(text, rangeNode);
451
+ if (allowableRanges && allowableRanges.length > 0) {
452
+ variable.allowable_ranges = allowableRanges;
453
+ }
454
+ }
455
+ if (allowableValuesNode) {
456
+ const allowableValues = parseAllowableValues(text, allowableValuesNode);
457
+ if (allowableValues && allowableValues.length > 0) {
458
+ variable.allowable_values = allowableValues;
459
+ }
460
+ }
461
+ }
462
+ else {
463
+ for (const object of objects) {
464
+ const properties = object.getChildren('Property');
465
+ properties.forEach(property => {
466
+ const propertyName = property.getChild('PropertyName');
467
+ const propertyValue = propertyName?.nextSibling;
468
+ const propertyNameString = text.slice(propertyName?.from, propertyName?.to).replaceAll('"', '');
469
+ const propertyValueString = text.slice(propertyValue?.from, propertyValue?.to).replaceAll('"', '');
470
+ switch (propertyNameString.toLowerCase()) {
471
+ case 'allowable_ranges': {
472
+ if (!propertyValue) {
473
+ break;
474
+ }
475
+ const allowableRanges = parseAllowableRanges(text, propertyValue);
476
+ if (allowableRanges && allowableRanges.length > 0) {
477
+ variable.allowable_ranges = allowableRanges;
478
+ }
479
+ break;
480
+ }
481
+ case 'allowable_values':
482
+ {
483
+ if (!propertyValue) {
484
+ break;
485
+ }
486
+ const allowableValues = parseAllowableValues(text, propertyValue);
487
+ if (allowableValues && allowableValues.length > 0) {
488
+ variable.allowable_values = allowableValues;
489
+ }
490
+ }
491
+ break;
492
+ case 'enum_name':
493
+ variable.enum_name = propertyValueString;
494
+ break;
495
+ case 'sc_name':
496
+ variable.sc_name = propertyValueString;
497
+ break;
498
+ case 'type':
499
+ variable.type = propertyValueString;
500
+ break;
501
+ }
502
+ });
503
+ }
504
+ }
505
+ const match = /(?:[a-zA-Z]*)(?:[0-9]{2})(INT|UINT|FLT|ENUM|STR)/g.exec(variableText);
506
+ if (match) {
507
+ const kind = match[1];
508
+ switch (kind) {
509
+ case 'STR':
510
+ variable.type = 'STRING';
511
+ break;
512
+ case 'FLT':
513
+ variable.type = 'FLOAT';
514
+ break;
515
+ case 'INT':
516
+ variable.type = 'INT';
517
+ break;
518
+ case 'UINT':
519
+ variable.type = 'UINT';
520
+ break;
521
+ case 'ENUM':
522
+ variable.type = 'ENUM';
523
+ break;
524
+ }
525
+ return variable;
526
+ }
527
+ else {
528
+ return variable;
529
+ }
530
+ });
531
+ }
532
+ function parseAllowableRanges(text, rangeNode) {
533
+ return text
534
+ .slice(rangeNode.from, rangeNode.to)
535
+ .split(',')
536
+ .map(range => {
537
+ const rangeMatch = /^([-+]?\d+)?(\.\.\.)([-+]?\d+)?$/.exec(range.replaceAll('"', '').trim());
538
+ if (rangeMatch) {
539
+ const [, min, , max] = rangeMatch;
540
+ const parsedMaxNum = Number(max);
541
+ const parsedMinNum = Number(min);
542
+ const maxNum = !Number.isNaN(parsedMaxNum) ? parsedMaxNum : Infinity;
543
+ const minNum = !Number.isNaN(parsedMinNum) ? parsedMinNum : -Infinity;
544
+ return { max: maxNum, min: minNum };
545
+ }
546
+ return undefined;
547
+ })
548
+ .filter(range => range !== undefined);
549
+ }
550
+ function parseAllowableValues(text, allowableValuesNode) {
551
+ const allowableValues = text
552
+ .slice(allowableValuesNode.from + 1, allowableValuesNode.to - 1)
553
+ .split(',')
554
+ .map(value => value.trim());
555
+ return allowableValues.length > 0 ? allowableValues : undefined;
556
+ }
557
+ function parseModel(node, text) {
558
+ const modelContainer = node.getChild('Models');
559
+ if (!modelContainer) {
560
+ return undefined;
561
+ }
562
+ const modelNodes = modelContainer.getChildren('Model');
563
+ if (!modelNodes || modelNodes.length === 0) {
564
+ return undefined;
565
+ }
566
+ const models = [];
567
+ for (const modelNode of modelNodes) {
568
+ const variableNode = modelNode.getChild('Variable');
569
+ const valueNode = modelNode.getChild('Value');
570
+ const offsetNode = modelNode.getChild('Offset');
571
+ const variable = variableNode ? unquoteUnescape(text.slice(variableNode.from, variableNode.to)) : 'UNKNOWN';
572
+ // Value can be string, number or boolean
573
+ let value = 0;
574
+ const valueChild = valueNode?.firstChild;
575
+ if (valueChild) {
576
+ const valueText = text.slice(valueChild.from, valueChild.to);
577
+ if (valueChild.name === 'String') {
578
+ value = unquoteUnescape(valueText);
579
+ }
580
+ else if (valueChild.name === 'Boolean') {
581
+ value = !/^FALSE$/i.test(valueText);
582
+ }
583
+ else if (valueChild.name === 'Number') {
584
+ value = Number(valueText);
585
+ }
586
+ }
587
+ const offset = offsetNode ? unquoteUnescape(text.slice(offsetNode.from, offsetNode.to)) : 'UNKNOWN';
588
+ models.push({ offset, value, variable });
589
+ }
590
+ return models;
591
+ }
592
+ function parseDescription(node, text) {
593
+ const descriptionNode = node.getChild('LineComment');
594
+ if (!descriptionNode) {
595
+ return undefined;
596
+ }
597
+ // +1 offset to drop '#' prefix
598
+ const description = text.slice(descriptionNode.from + 1, descriptionNode.to).trim();
599
+ return removeEscapedQuotes(description);
600
+ }
601
+ function parseCommand(commandNode, text, commandDictionary, isRTC) {
602
+ const time = parseTime(commandNode, text);
603
+ const stemNode = commandNode.getChild('Stem');
604
+ const stem = stemNode ? text.slice(stemNode.from, stemNode.to) : 'UNKNOWN';
605
+ const argsNode = commandNode.getChild('Args');
606
+ const args = argsNode ? parseArgs(argsNode, text, commandDictionary, stem) : [];
607
+ const description = parseDescription(commandNode, text);
608
+ const metadata = parseMetadata(commandNode, text);
609
+ const models = parseModel(commandNode, text);
610
+ if (!isRTC) {
611
+ return {
612
+ args,
613
+ description,
614
+ metadata,
615
+ models,
616
+ stem,
617
+ time,
618
+ type: 'command',
619
+ };
620
+ }
621
+ return {
622
+ args,
623
+ description,
624
+ metadata,
625
+ stem,
626
+ type: 'immediate_command',
627
+ };
628
+ }
629
+ function parseImmediateCommand(immediateCommandNode, text, commandDictionary) {
630
+ if (!immediateCommandNode) {
631
+ return undefined;
632
+ }
633
+ const steps = [
634
+ ...(immediateCommandNode.getChildren(SEQN_NODES.COMMAND) || []),
635
+ ...(immediateCommandNode.getChildren(SEQN_NODES.LOAD) || []),
636
+ ...(immediateCommandNode.getChildren(SEQN_NODES.ACTIVATE) || []),
637
+ ];
638
+ return steps.map((step) => {
639
+ switch (step.name) {
640
+ case 'Command':
641
+ return parseCommand(step, text, commandDictionary, true);
642
+ case 'Load':
643
+ case 'Activate':
644
+ return parseActivateLoad(step, text, true);
645
+ default:
646
+ throw new Error(`Unexpected step type: ${step.name}`);
647
+ }
648
+ });
649
+ }
650
+ function parseHardwareCommand(commandNode, text) {
651
+ const stemNode = commandNode.getChild('Stem');
652
+ const stem = stemNode ? text.slice(stemNode.from, stemNode.to) : 'UNKNOWN';
653
+ const description = parseDescription(commandNode, text);
654
+ const metadata = parseMetadata(commandNode, text);
655
+ return {
656
+ description,
657
+ metadata,
658
+ stem,
659
+ };
660
+ }
661
+ /**
662
+ * This looks for a @ID directive to specify sequence id, if not present use ground name without extensions
663
+ *
664
+ * @param node - top node of parsed tree
665
+ * @param text - text of sequence
666
+ * @param sequenceName - ground name of sequence
667
+ * @returns
668
+ */
669
+ function parseId(node, text, sequenceName) {
670
+ const stringNode = node.getChild('IdDeclaration')?.getChild('String');
671
+ if (!stringNode) {
672
+ return sequenceName.split('.')[0];
673
+ }
674
+ const id = JSON.parse(text.slice(stringNode.from, stringNode.to));
675
+ return id;
676
+ }
677
+ function parseMetadata(node, text) {
678
+ const metadataNode = node.getChild('Metadata');
679
+ if (!metadataNode) {
680
+ return undefined;
681
+ }
682
+ const metadataEntry = metadataNode.getChildren('MetaEntry');
683
+ if (!metadataEntry || metadataEntry.length === 0) {
684
+ return undefined;
685
+ }
686
+ const obj = {};
687
+ metadataEntry.forEach(entry => {
688
+ const keyNode = entry.getChild('Key');
689
+ const valueNode = entry.getChild('Value');
690
+ if (!keyNode || !valueNode) {
691
+ return; // Skip this entry if either the key or value is missing
692
+ }
693
+ const keyText = unquoteUnescape(text.slice(keyNode.from, keyNode.to));
694
+ let value = text.slice(valueNode.from, valueNode.to);
695
+ try {
696
+ value = JSON.parse(value);
697
+ }
698
+ catch (e) {
699
+ logInfo(`Malformed metadata ${value}`);
700
+ }
701
+ obj[keyText] = value;
702
+ });
703
+ return obj;
704
+ }
705
+ //# sourceMappingURL=seqnToSeqJson.js.map