@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,927 @@
1
+ import { getBalancedDuration, getDurationTimeComponents, parseDurationString, validateTime, TimeTypes, } from '@nasa-jpl/aerie-time-utils';
2
+ import { removeEscapedQuotes, removeQuote, unquoteUnescape } from '../utils/string';
3
+ import { SatfLanguage } from '../languages/satf/grammar/satf-sasf.js';
4
+ import { SATF_SASF_NODES } from '../languages/satf/constants/satf-sasf-constants.js';
5
+ import { SeqnParser } from '../languages/seq-n/seq-n';
6
+ import { SEQN_NODES } from '../languages/seq-n/seqn-grammar-constants';
7
+ /**
8
+ * Asynchronously converts a parsed SeqN tree via lezer into a structured SATF representation.
9
+ *
10
+ * Parse different sections (metadata, variables, steps) of the SeqN tree and
11
+ * assemble them into a `ParsedSatf` object containing the corresponding SATF sections.
12
+ *
13
+ * @async
14
+ * @param {string} sequence - The original SeqN source text corresponding to the `seqnTree`.
15
+ * @param {string[]} [globalVariables] - Optional. A list of predefined global variable names to be used
16
+ * @param {CommandDictionary} [commandDictionary] - Optional. A dictionary containing command definitions.
17
+ * @returns {Promise<ParsedSatf>} A Promise that resolves to an object containing the generated
18
+ * SATF `header`, `parameters , `variables`, and `steps` block strings (or undefined if a section is empty/not generated).
19
+ */
20
+ export async function seqnToSATF(seqn, globalVariables, commandDictionary) {
21
+ const seqnTree = SeqnParser.parse(seqn);
22
+ const header = parseHeaderfromSeqn(seqnTree, seqn);
23
+ const parameters = satfVariablesFromSeqn(seqnTree, seqn);
24
+ const variables = satfVariablesFromSeqn(seqnTree, seqn, 'Variables');
25
+ const steps = satfStepsFromSeqn(seqnTree, seqn, [...(globalVariables ? globalVariables : []), ...getSatfVariableNames(seqnTree, seqn)], commandDictionary);
26
+ return {
27
+ header,
28
+ ...(parameters ? { parameters } : {}),
29
+ ...(variables ? { variables } : {}),
30
+ ...(steps ? { steps } : {}),
31
+ };
32
+ }
33
+ /**
34
+ * Asynchronously converts a parsed SeqN tree via lezer into a structured SASF representation.
35
+ *
36
+ * Parse the metadata section and generate SASF request blocks from the SeqN tree.
37
+ * It then combines these into a `ParsedSasf` object.
38
+ *
39
+ * @async
40
+ * @param {string} sequence - The original SeqN source text corresponding to the `seqnTree`.
41
+ * @param {string[]} [globalVariables] - Optional. A list of predefined global variable names to be used
42
+ *
43
+ * @param {CommandDictionary} [commandDictionary] - Optional. A dictionary containing command definitions,
44
+ * @returns {Promise<ParsedSasf>} A Promise that resolves to an object containing the generated
45
+ * SASF `metadata` string and the concatenated `requests` string (or undefined if a section is empty/not generated).
46
+ */
47
+ export async function seqnToSASF(seqn, globalVariables, commandDictionary) {
48
+ const seqnTree = SeqnParser.parse(seqn);
49
+ const header = parseHeaderfromSeqn(seqnTree, seqn);
50
+ // TODO: I don't think sasf have varaibles or parameters:
51
+ // const parameters = satfVariablesFromSeqn(seqnTree, seqn);
52
+ // const variables = satfVariablesFromSeqn(seqnTree, seqn, 'Variables');
53
+ const requests = sasfRequestFromSeqN(seqnTree, seqn, globalVariables, commandDictionary);
54
+ return {
55
+ header,
56
+ requests,
57
+ };
58
+ }
59
+ function parseHeaderfromSeqn(seqnTree, sequence) {
60
+ const metadata = {};
61
+ const metadataEntries = seqnTree.topNode.getChild(SEQN_NODES.METADATA)?.getChildren(SEQN_NODES.METADATA_ENTRY) ?? [];
62
+ for (const entry of metadataEntries) {
63
+ const keyNode = entry.getChild(SEQN_NODES.KEY);
64
+ const valueNode = entry.getChild(SEQN_NODES.VALUE);
65
+ if (keyNode === null || valueNode === null) {
66
+ continue;
67
+ }
68
+ const key = unquoteUnescape(sequence.slice(keyNode.from, keyNode.to));
69
+ const value = sequence.slice(valueNode.from, valueNode.to);
70
+ metadata[key] = value;
71
+ }
72
+ let commentMetadata = seqnTree.topNode.getChildren(SEQN_NODES.LINE_COMMENT);
73
+ if (commentMetadata.length === 0) {
74
+ commentMetadata = seqnTree.topNode.getChild(SEQN_NODES.COMMANDS)?.getChildren(SEQN_NODES.LINE_COMMENT) ?? [];
75
+ }
76
+ for (const comment of commentMetadata) {
77
+ const text = sequence.slice(comment.from, comment.to);
78
+ const [key, value] = text.split('=').map(unquoteUnescape);
79
+ if (key && value) {
80
+ metadata[key.slice(1).trim()] = value;
81
+ }
82
+ }
83
+ return metadata;
84
+ }
85
+ function satfStepsFromSeqn(seqnTree, sequence, variables, commandDictionary) {
86
+ let stepNode = seqnTree.topNode.getChild(SATF_SASF_NODES.COMMANDS)?.firstChild;
87
+ let steps = [];
88
+ while (stepNode) {
89
+ steps.push(stepNode);
90
+ stepNode = stepNode.nextSibling;
91
+ }
92
+ steps = steps.filter((step) => step.name !== SATF_SASF_NODES.LINE_COMMENT);
93
+ if (steps === null || steps.length === 0) {
94
+ return undefined;
95
+ }
96
+ return `STEPS,\n${steps
97
+ .map((step, index) => {
98
+ return parseSeqNStep(step, sequence, commandDictionary, variables, 1 + index++);
99
+ })
100
+ .filter((step) => step)
101
+ .join('\n')}\nend`;
102
+ }
103
+ function parseSeqNStep(child, text, commandDictionary, variables, order) {
104
+ switch (child?.name) {
105
+ case SEQN_NODES.COMMAND:
106
+ return parseSeqNCommand(child, text, commandDictionary, variables, order);
107
+ case SEQN_NODES.ACTIVATE:
108
+ return parseSeqNActivate(child, text, commandDictionary, variables, order);
109
+ case 'Load':
110
+ case 'GroundBlock':
111
+ case 'GroundEvent':
112
+ default:
113
+ return undefined;
114
+ }
115
+ }
116
+ function parseSeqNCommand(commandNode, sequence, commandDictionary, variables, order) {
117
+ const time = parseSeqNTime(commandNode, sequence);
118
+ const stemNode = commandNode.getChild(SEQN_NODES.STEM);
119
+ const stem = stemNode ? sequence.slice(stemNode.from, stemNode.to) : 'UNKNOWN';
120
+ const argsNode = commandNode.getChild(SEQN_NODES.ARGS);
121
+ const args = argsNode ? parseSeqNArgs(argsNode, sequence, commandDictionary, variables, stem) : [];
122
+ const description = parseSeqNDescription(commandNode, sequence);
123
+ const metadata = parsSeqNMetadata(commandNode, sequence, [SEQN_NODES.NTEXT]);
124
+ const models = parseSeqNModel(commandNode, sequence);
125
+ return (`${'\t'}command(${order},` +
126
+ `\n${'\t'.repeat(2)}SCHEDULED_TIME,\\${time.tag}\\,${time.type},` +
127
+ `${metadata ? `\n${'\t'.repeat(2)}${metadata},` : ''}` +
128
+ `${description ? `\n${'\t'.repeat(2)}COMMENT,\\${description}\\,` : ''}` +
129
+ `${models ? `\n${'\t'.repeat(2)}ASSUMED_MODEL_VALUES,\\${models}\\,` : ''}` +
130
+ `\n${'\t'.repeat(2)}${stem}${args.length !== 0 ? `(${serializeSeqNArgs(args)})` : ''}` +
131
+ `\n${'\t'}),`);
132
+ }
133
+ function parseSeqNActivate(stepNode, sequence, commandDictionary, variables, order) {
134
+ const nameNode = stepNode.getChild(SEQN_NODES.SEQUENCE_NAME);
135
+ const sequenceName = nameNode ? unquoteUnescape(sequence.slice(nameNode.from, nameNode.to)) : 'UNKNOWN';
136
+ const time = parseSeqNTime(stepNode, sequence);
137
+ const argsNode = stepNode.getChild(SEQN_NODES.ARGS);
138
+ const args = argsNode ? parseSeqNArgs(argsNode, sequence, commandDictionary, variables, sequenceName) : [];
139
+ const engine = parseSeqNEngine(stepNode, sequence);
140
+ const epoch = parseSeqNEpoch(stepNode, sequence);
141
+ return `${'\t'}SPAWN(${order},
142
+ ${'\t'.repeat(2)}SCHEDULED_TIME,\\${time.tag}\\,${time.type},${engine !== undefined
143
+ ? `
144
+ ${'\t'.repeat(2)}RETURN_ENGINE_ID_TO,\\${engine}\\,`
145
+ : ''}${epoch !== undefined
146
+ ? `
147
+ ${'\t'.repeat(2)}EPOCH,${epoch},`
148
+ : ''}
149
+ ${'\t'.repeat(2)}RT_on_board_block(${sequenceName},${serializeSeqNArgs(args)})
150
+ ${'\t'}),`;
151
+ }
152
+ function parseSeqNEngine(stepNode, text) {
153
+ const engineNode = stepNode.getChild(SATF_SASF_NODES.ENGINE)?.getChild(SATF_SASF_NODES.NUMBER);
154
+ return engineNode ? parseInt(text.slice(engineNode.from, engineNode.to), 10) : undefined;
155
+ }
156
+ function parseSeqNEpoch(stepNode, text) {
157
+ const epochNode = stepNode.getChild(SATF_SASF_NODES.EPOCH)?.getChild(SATF_SASF_NODES.STRING);
158
+ return epochNode ? unquoteUnescape(text.slice(epochNode.from, epochNode.to)) : undefined;
159
+ }
160
+ function parseSeqNTime(commandNode, sequence) {
161
+ const tag = '00:00:01';
162
+ const timeTagNode = commandNode.getChild('TimeTag');
163
+ if (timeTagNode === null) {
164
+ return { tag: '00:00:00', type: 'UNKNOWN' };
165
+ }
166
+ const time = timeTagNode.firstChild;
167
+ if (time === null) {
168
+ return { tag, type: 'UNKNOWN' };
169
+ }
170
+ const timeValue = sequence.slice(time.from + 1, time.to).trim();
171
+ if (time.name === 'TimeComplete') {
172
+ return { tag, type: 'WAIT_PREVIOUS_END' };
173
+ }
174
+ else if (time.name === 'TimeGroundEpoch') {
175
+ const parentNode = time.parent;
176
+ if (parentNode) {
177
+ // ex: G+3:00 "GroundEpochName"
178
+ const parentNodeText = sequence.slice(parentNode.from, parentNode.to);
179
+ const splitParentNodeText = parentNodeText.slice(1, parentNodeText.length).split(' ');
180
+ if (splitParentNodeText.length > 0) {
181
+ const epochTag = splitParentNodeText[0];
182
+ const epochName = unquoteUnescape(splitParentNodeText[1]);
183
+ return { tag: `${epochName}${epochTag}`, type: 'GROUND_EPOCH' };
184
+ }
185
+ }
186
+ }
187
+ else {
188
+ if (validateTime(timeValue, TimeTypes.ISO_ORDINAL_TIME)) {
189
+ return { tag: timeValue, type: 'ABSOLUTE' };
190
+ }
191
+ else if (validateTime(timeValue, TimeTypes.DOY_TIME)) {
192
+ const { isNegative, days, hours, minutes, seconds, milliseconds } = getDurationTimeComponents(parseDurationString(timeValue, 'seconds'));
193
+ const signed = timeValue.charAt(0) === '-' || timeValue.charAt(0) === '+';
194
+ return {
195
+ tag: `${isNegative}${days}${hours}:${minutes}:${seconds}${milliseconds}`,
196
+ type: signed ? 'EPOCH' : 'FROM_PREVIOUS_START',
197
+ };
198
+ }
199
+ else if (validateTime(timeValue, TimeTypes.SECOND_TIME)) {
200
+ const signed = timeValue.charAt(0) === '-' || timeValue.charAt(0) === '+';
201
+ let balancedTime = getBalancedDuration(timeValue);
202
+ if (parseDurationString(balancedTime, 'seconds').milliseconds === 0) {
203
+ balancedTime = balancedTime.slice(0, -4);
204
+ }
205
+ return { tag: balancedTime, type: signed ? 'EPOCH' : 'FROM_PREVIOUS_START' };
206
+ }
207
+ }
208
+ return { tag, type: 'UNKNOWN' };
209
+ }
210
+ function parseSeqNDescription(node, text) {
211
+ const descriptionNode = node.getChild(SEQN_NODES.LINE_COMMENT);
212
+ if (!descriptionNode) {
213
+ return undefined;
214
+ }
215
+ // +1 offset to drop '#' prefix
216
+ const description = text.slice(descriptionNode.from + 1, descriptionNode.to).trim();
217
+ return removeEscapedQuotes(description);
218
+ }
219
+ function parseSeqNArgs(argsNode, sequence, commandDictionary, variables, stem) {
220
+ const args = [];
221
+ let argNode = argsNode.firstChild;
222
+ const dictArguments = commandDictionary?.fswCommandMap[stem]?.arguments ?? [];
223
+ let i = 0;
224
+ while (argNode) {
225
+ const dictionaryArg = dictArguments[i] ?? null;
226
+ const arg = parseSeqNArg(argNode, sequence, dictionaryArg, variables);
227
+ if (arg !== undefined) {
228
+ args.push(arg);
229
+ }
230
+ argNode = argNode?.nextSibling;
231
+ i++;
232
+ }
233
+ return args;
234
+ }
235
+ function parseSeqNArg(argNode, sequence, dictionaryArg, variables) {
236
+ const nodeValue = sequence.slice(argNode.from, argNode.to);
237
+ if (variables.includes(nodeValue)) {
238
+ return {
239
+ name: undefined,
240
+ type: 'string',
241
+ value: `"${nodeValue}"`,
242
+ };
243
+ }
244
+ switch (argNode.name) {
245
+ case SEQN_NODES.BOOLEAN: {
246
+ return {
247
+ name: dictionaryArg ? dictionaryArg.name : undefined,
248
+ type: 'boolean',
249
+ value: nodeValue === 'true' ? 'TRUE' : 'FALSE',
250
+ };
251
+ }
252
+ case SEQN_NODES.ENUM: {
253
+ return {
254
+ name: dictionaryArg ? dictionaryArg.name : undefined,
255
+ type: 'enum',
256
+ value: nodeValue,
257
+ };
258
+ }
259
+ case SEQN_NODES.NUMBER: {
260
+ const decimalCount = nodeValue.slice(nodeValue.indexOf('.') + 1).length;
261
+ return {
262
+ name: dictionaryArg ? dictionaryArg.name : undefined,
263
+ type: 'number',
264
+ value: parseFloat(nodeValue).toFixed(decimalCount),
265
+ };
266
+ }
267
+ case SEQN_NODES.STRING: {
268
+ return {
269
+ name: dictionaryArg ? dictionaryArg.name : undefined,
270
+ type: 'string',
271
+ value: nodeValue,
272
+ };
273
+ }
274
+ default: {
275
+ break;
276
+ }
277
+ }
278
+ }
279
+ function serializeSeqNArgs(args) {
280
+ return args
281
+ .map(arg => {
282
+ return `${arg.value}`;
283
+ })
284
+ .join(', ');
285
+ }
286
+ function getSatfVariableNames(seqnTree, text) {
287
+ let types = [SEQN_NODES.PARAMETER_DECLARATION, SEQN_NODES.LOCAL_DECLARATION];
288
+ let names = [];
289
+ for (let i = 0; i < types.length; i++) {
290
+ let variableContainer = seqnTree.topNode.getChild(types[i]);
291
+ if (!variableContainer) {
292
+ continue;
293
+ }
294
+ const variables = variableContainer.getChildren(SEQN_NODES.VARIABLE);
295
+ if (!variables || variables.length === 0) {
296
+ continue;
297
+ }
298
+ variables.forEach((variableNode) => {
299
+ const nameNode = variableNode.getChild(SEQN_NODES.ENUM);
300
+ if (nameNode) {
301
+ names.push(text.slice(nameNode.from, nameNode.to));
302
+ }
303
+ });
304
+ }
305
+ return names;
306
+ }
307
+ function satfVariablesFromSeqn(seqnTree, text, type = 'Parameters') {
308
+ let nType = 'ParameterDeclaration';
309
+ if (type === 'Variables') {
310
+ nType = 'LocalDeclaration';
311
+ }
312
+ const variableContainer = seqnTree.topNode.getChild(nType);
313
+ if (!variableContainer) {
314
+ return undefined;
315
+ }
316
+ const variables = variableContainer.getChildren(SEQN_NODES.VARIABLE);
317
+ if (!variables || variables.length === 0) {
318
+ return undefined;
319
+ }
320
+ const serializedVariables = variables
321
+ .map((variableNode) => {
322
+ const nameNode = variableNode.getChild(SEQN_NODES.ENUM);
323
+ const typeNode = variableNode.getChild(SEQN_NODES.TYPE);
324
+ const enumNode = variableNode.getChild(SEQN_NODES.ENUM_NAME);
325
+ const rangeNode = variableNode.getChild(SEQN_NODES.RANGE);
326
+ const allowableValuesNode = variableNode.getChild(SEQN_NODES.VALUES);
327
+ const objects = variableNode.getChildren(SEQN_NODES.OBJECT);
328
+ const variableText = nameNode ? text.slice(nameNode.from, nameNode.to) : 'UNKNOWN';
329
+ const variable = { name: variableText, type: 'INT' };
330
+ if (typeNode) {
331
+ variable.type = text.slice(typeNode.from, typeNode.to);
332
+ if (enumNode) {
333
+ variable.enum_name = text.slice(enumNode.from, enumNode.to);
334
+ }
335
+ if (rangeNode) {
336
+ const allowableRanges = parseAllowableRanges(text, rangeNode);
337
+ if (allowableRanges && allowableRanges.length > 0) {
338
+ variable.allowable_ranges = allowableRanges;
339
+ }
340
+ }
341
+ if (allowableValuesNode) {
342
+ const allowableValues = parseAllowableValues(text, allowableValuesNode);
343
+ if (allowableValues && allowableValues.length > 0) {
344
+ variable.allowable_values = allowableValues;
345
+ }
346
+ }
347
+ // old style for parameters and variables
348
+ }
349
+ else {
350
+ for (const object of objects) {
351
+ const properties = object.getChildren(SEQN_NODES.PROPERTY);
352
+ properties.forEach(property => {
353
+ const propertyName = property.getChild(SEQN_NODES.PROPERTY_NAME);
354
+ const propertyValue = propertyName?.nextSibling;
355
+ if (!propertyName || !propertyValue) {
356
+ return;
357
+ }
358
+ const propertyNameString = text.slice(propertyName?.from, propertyName?.to).replaceAll('"', '');
359
+ const propertyValueString = text.slice(propertyValue?.from, propertyValue?.to).replaceAll('"', '');
360
+ switch (propertyNameString.toLowerCase()) {
361
+ case 'allowable_ranges': {
362
+ if (!propertyValue) {
363
+ break;
364
+ }
365
+ const allowableRanges = parseAllowableRanges(text, propertyValue);
366
+ if (allowableRanges && allowableRanges.length > 0) {
367
+ variable.allowable_ranges = allowableRanges;
368
+ }
369
+ break;
370
+ }
371
+ case 'allowable_values':
372
+ {
373
+ if (!propertyValue) {
374
+ break;
375
+ }
376
+ const allowableValues = parseAllowableValues(text, propertyValue);
377
+ if (allowableValues && allowableValues.length > 0) {
378
+ variable.allowable_values = allowableValues;
379
+ }
380
+ }
381
+ break;
382
+ case 'enum_name':
383
+ variable.enum_name = propertyValueString;
384
+ break;
385
+ case 'sc_name':
386
+ variable.sc_name = propertyValueString;
387
+ break;
388
+ case 'type':
389
+ variable.type = propertyValueString;
390
+ break;
391
+ }
392
+ });
393
+ }
394
+ }
395
+ //convert seqn type to satf type
396
+ switch (variable.type) {
397
+ case SEQN_NODES.VAR_UINT:
398
+ variable.type = SATF_SASF_NODES.PARAM_UNSIGNED_DECIMAL;
399
+ break;
400
+ case SEQN_NODES.VAR_INT:
401
+ variable.type = SATF_SASF_NODES.PARAM_SIGNED_DECIMAL;
402
+ break;
403
+ case SEQN_NODES.VAR_STRING:
404
+ variable.type = SATF_SASF_NODES.PARAM_QUOTED_STRING;
405
+ break;
406
+ case SEQN_NODES.VAR_FLOAT:
407
+ break;
408
+ case SEQN_NODES.VAR_ENUM:
409
+ variable.type = SATF_SASF_NODES.PARAM_STRING;
410
+ break;
411
+ }
412
+ return (`\t${variable.name}` +
413
+ `(\n\t\tTYPE,${variable.type}${variable.enum_name ? `\n\t\t\ENUM_NAME,${variable.enum_name}` : ''}` +
414
+ `${variable.allowable_ranges
415
+ ? variable.allowable_ranges
416
+ .map(range => {
417
+ return `\n\t\tRANGES,\\${range.min}...${range.max}\\`;
418
+ })
419
+ .join(',')
420
+ : ''}` +
421
+ `${variable.allowable_values ? `\n\t\t\RANGES,\\${variable.allowable_values}\\` : ''}` +
422
+ `${variable.sc_name ? `\n\t\tSC_NAME,${variable.sc_name}` : ''}` +
423
+ `\n\t)`);
424
+ })
425
+ .join(',\n');
426
+ return `${type.toUpperCase()},\n ${serializedVariables},\nend,\n`;
427
+ }
428
+ function parseAllowableRanges(text, rangeNode) {
429
+ if (!rangeNode) {
430
+ return [];
431
+ }
432
+ return text
433
+ .slice(rangeNode.from, rangeNode.to)
434
+ .split(',')
435
+ .map(range => {
436
+ const rangeMatch = /^(?<min>[-+]?\d+(\.\d*)?)?(\.\.\.)(?<max>[-+]?\d+(\.\d*)?)?$/.exec(range.replaceAll('"', '').trim());
437
+ if (rangeMatch && rangeMatch.groups) {
438
+ const { min, max } = rangeMatch.groups;
439
+ const maxNum = !isNaN(Number(max)) ? Number(max) : Infinity;
440
+ const minNum = !isNaN(Number(min)) ? Number(min) : -Infinity;
441
+ return { max: maxNum, min: minNum };
442
+ }
443
+ return undefined;
444
+ })
445
+ .filter(range => range !== undefined);
446
+ }
447
+ function parseAllowableValues(text, allowableValuesNode) {
448
+ const allowableValues = text
449
+ .slice(allowableValuesNode.from + 1, allowableValuesNode.to - 1)
450
+ .split(',')
451
+ .map(value => value.trim());
452
+ return allowableValues.length > 0 ? allowableValues : undefined;
453
+ }
454
+ function sasfRequestFromSeqN(seqnTree, sequence, variables = [], commandDictionary) {
455
+ const requests = seqnTree.topNode.getChild(SEQN_NODES.COMMANDS)?.getChildren(SEQN_NODES.REQUEST);
456
+ if (requests == null || requests.length === 0) {
457
+ return undefined;
458
+ }
459
+ return requests
460
+ .map((requestNode) => {
461
+ const nameNode = requestNode.getChild(SEQN_NODES.REQUEST_NAME);
462
+ const name = nameNode ? unquoteUnescape(sequence.slice(nameNode.from, nameNode.to)) : 'UNKNOWN';
463
+ const parsedTime = parseSeqNTime(requestNode, sequence);
464
+ const requester = parsSeqNMetadata(requestNode, sequence, ['REQUESTOR']);
465
+ const processor = parsSeqNMetadata(requestNode, sequence, ['PROCESSOR']);
466
+ const key = parsSeqNMetadata(requestNode, sequence, ['KEY']);
467
+ const request = `request(${name},` +
468
+ `\n\tSTART_TIME, ${parsedTime.tag},` +
469
+ `${requester ? `\n\t${requester.replaceAll('\\', '\"')}` : ''},` +
470
+ `${processor ? `\n\t${processor.replaceAll('\\', '\"')}` : ''},` +
471
+ `${key ? `\n\t${key.replaceAll('\\', '\"')}` : ''})`;
472
+ `\n\n`;
473
+ let order = 1;
474
+ let child = requestNode?.getChild(SEQN_NODES.STEPS)?.firstChild;
475
+ const steps = [];
476
+ while (child) {
477
+ steps.push(`\t${parseSeqNStep(child, sequence, commandDictionary, variables, order++)?.replaceAll('\n', '\n\t')}`);
478
+ child = child?.nextSibling;
479
+ }
480
+ return `${request}\n${steps.join('\n')}\nend;\n`;
481
+ })
482
+ .join(',\n');
483
+ }
484
+ /**
485
+ * Parses a SATF formatted string asynchronously to extract header information and sequence data.
486
+ * It utilizes the SatfLanguage parser to generate SeqN parts.
487
+ *
488
+ * @async
489
+ * @function satfToSeqn
490
+ * @param {string} satf - The SATF or SASF formatted string content to parse.
491
+ * @param {string[]} [globalVariables] - Optional. A list of predefined global variable names to be used
492
+ * during the parsing of steps
493
+ * @returns {Promise<ParsedSequence>} A Promise that resolves to an object containing the parsed header
494
+ * and an array of sequence objects.
495
+ * If the input string does not contain a top-level SATF structure recognized by the parser,
496
+ * it resolves with a default empty ParsedSequence object (e.g., { header: "", sequences: [] }).
497
+ */
498
+ export async function satfToSeqn(satf, globalVariables) {
499
+ const base = SatfLanguage.parser.parse(satf).topNode;
500
+ const satfNode = base.getChild(SATF_SASF_NODES.SATF);
501
+ if (!satfNode) {
502
+ return { metadata: '', sequences: [] };
503
+ }
504
+ const metadata = parseHeader(satfNode.getChild(SATF_SASF_NODES.HEADER), satf);
505
+ const sequences = parseBody(satfNode.getChild(SATF_SASF_NODES.BODY), globalVariables, satf);
506
+ return { metadata, sequences };
507
+ }
508
+ export async function sasfToSeqn(sasf, globalVariables) {
509
+ const base = SatfLanguage.parser.parse(sasf).topNode;
510
+ const sasfNode = base.getChild(SATF_SASF_NODES.SASF);
511
+ if (!sasfNode) {
512
+ return { metadata: '', sequences: [] };
513
+ }
514
+ const metadata = parseHeader(sasfNode.getChild(SATF_SASF_NODES.HEADER), sasf);
515
+ const sequences = parseBody(sasfNode.getChild(SATF_SASF_NODES.BODY), globalVariables, sasf);
516
+ return { metadata, sequences };
517
+ }
518
+ function parseHeader(headerNode, text) {
519
+ const header = '';
520
+ if (!headerNode) {
521
+ return header;
522
+ }
523
+ const sfduHeader = headerNode
524
+ .getChild(SATF_SASF_NODES.SFDU_HEADER)
525
+ ?.getChild(SATF_SASF_NODES.HEADER_PAIRS)
526
+ ?.getChildren(SATF_SASF_NODES.HEADER_PAIR) ?? [];
527
+ return sfduHeader
528
+ .map((pairNode) => {
529
+ const keyNode = pairNode.getChild(SATF_SASF_NODES.KEY);
530
+ const valueNode = pairNode.getChild(SATF_SASF_NODES.VALUE);
531
+ if (!keyNode || !valueNode) {
532
+ console.error(`Error processing header entry: ${text.slice(pairNode.from, pairNode.to)}`);
533
+ return '';
534
+ }
535
+ const key = text.slice(keyNode.from, keyNode.to).trim();
536
+ const value = text.slice(valueNode.from, valueNode.to).trim();
537
+ if (key.length === 0 || value.length === 0) {
538
+ return '';
539
+ }
540
+ return `@METADATA "${key}" "${value}"`;
541
+ })
542
+ .join('\n');
543
+ }
544
+ function parseBody(bodyNode, globalVariables = [], text) {
545
+ if (!bodyNode) {
546
+ return [];
547
+ }
548
+ //satf
549
+ if (bodyNode.getChild(SATF_SASF_NODES.ACTIVITY_TYPE_DEFINITIONS)) {
550
+ const activityTypeNodes = bodyNode.getChild(SATF_SASF_NODES.ACTIVITY_TYPE_DEFINITIONS)?.getChildren(SATF_SASF_NODES.ACTIVITY_TYPE_GROUP) ??
551
+ [];
552
+ return activityTypeNodes.map((group, i) => {
553
+ let sequenceName = 'sequence-' + i;
554
+ const sequenceNameNode = group.getChild(SATF_SASF_NODES.ACTIVITY_TYPE_NAME);
555
+ const seqGenNode = group.getChild(SATF_SASF_NODES.SEQGEN);
556
+ const vcNode = group.getChild(SATF_SASF_NODES.VIRTUAL_CHANNEL);
557
+ const onBoardFilenameNode = group.getChild(SATF_SASF_NODES.ON_BOARD_FILENAME);
558
+ const onBoardFilePathNode = group.getChild(SATF_SASF_NODES.ON_BOARD_PATH);
559
+ if (sequenceNameNode) {
560
+ const name = text.slice(sequenceNameNode.from, sequenceNameNode.to);
561
+ sequenceName = name.split('/').pop() || 'sequence-' + i;
562
+ }
563
+ const inputParameters = parseParameters(group.getChild(SATF_SASF_NODES.PARAMETERS), 'INPUT_PARAMS', text);
564
+ const localVariables = parseParameters(group.getChild(SATF_SASF_NODES.VARIABLES), 'LOCALS', text);
565
+ let metadata = '';
566
+ if (vcNode) {
567
+ metadata += `@METADATA "VIRTUAL_CHANNEL" "${text.slice(vcNode.from, vcNode.to)}"\n`;
568
+ }
569
+ if (onBoardFilenameNode) {
570
+ metadata += `@METADATA "ON_BOARD_FILENAME" "${text.slice(onBoardFilenameNode.from, onBoardFilenameNode.to)}"\n`;
571
+ }
572
+ if (onBoardFilePathNode) {
573
+ metadata += `@METADATA "ON_BOARD_PATH" "${text.slice(onBoardFilePathNode.from, onBoardFilePathNode.to)}"\n`;
574
+ }
575
+ if (seqGenNode) {
576
+ metadata += `@METADATA "SEQGEN" "${text.slice(seqGenNode.from, seqGenNode.to)}"\n`;
577
+ }
578
+ metadata = metadata.trimEnd();
579
+ const steps = parseSteps(group.getChild(SATF_SASF_NODES.STEPS), [
580
+ ...parseVariableName(group.getChild(SATF_SASF_NODES.PARAMETERS), text),
581
+ ...parseVariableName(group.getChild(SATF_SASF_NODES.VARIABLES), text),
582
+ ...globalVariables,
583
+ ], text);
584
+ return {
585
+ name: sequenceName,
586
+ metadata,
587
+ inputParameters,
588
+ localVariables,
589
+ steps,
590
+ };
591
+ });
592
+ }
593
+ //sasf
594
+ if (bodyNode.getChild(SATF_SASF_NODES.REQUESTS)) {
595
+ const requestNodes = bodyNode.getChild(SATF_SASF_NODES.REQUESTS)?.getChildren(SATF_SASF_NODES.REQUEST) ?? [];
596
+ return requestNodes.map((group, i) => {
597
+ let requests = '';
598
+ const requestNameNode = group.getChild(SATF_SASF_NODES.REQUEST_NAME);
599
+ const requestorNode = group.getChild(SATF_SASF_NODES.REQUESTOR);
600
+ const processorNode = group.getChild(SATF_SASF_NODES.PROCESSOR);
601
+ const keyNode = group.getChild(SATF_SASF_NODES.KEY);
602
+ const startTimeNode = group.getChild(SATF_SASF_NODES.START_TIME);
603
+ const sequenceName = requestNameNode ? text.slice(requestNameNode.from, requestNameNode.to) : 'sequence-' + i;
604
+ requests += parseTimeTagNode(startTimeNode ? startTimeNode.getChild(SATF_SASF_NODES.TIME) : null, startTimeNode ? startTimeNode.getChild(SATF_SASF_NODES.TIME_RELATION) : null, text);
605
+ requests += `@REQUEST_BEGIN("${sequenceName}")\n`;
606
+ requests += parseSteps(group.getChild(SATF_SASF_NODES.STEPS), globalVariables, text)
607
+ .split('\n')
608
+ .map(line => ' '.repeat(2) + line)
609
+ .join('\n');
610
+ requests += `\n@REQUEST_END\n`;
611
+ if (requestorNode) {
612
+ requests += `@METADATA "REQUESTOR" "${removeQuote(text.slice(requestorNode.from, requestorNode.to))}"\n`;
613
+ }
614
+ if (processorNode) {
615
+ requests += `@METADATA "PROCESSOR" "${removeQuote(text.slice(processorNode.from, processorNode.to))}"\n`;
616
+ }
617
+ if (keyNode) {
618
+ requests += `@METADATA "KEY" "${removeQuote(text.slice(keyNode.from, keyNode.to))}"\n`;
619
+ }
620
+ return { name: sequenceName, requests };
621
+ });
622
+ }
623
+ return [];
624
+ }
625
+ function parseVariableName(parameterNode, text) {
626
+ if (!parameterNode) {
627
+ return [];
628
+ }
629
+ const entries = parameterNode.getChildren(SATF_SASF_NODES.ENTRY);
630
+ if (!entries || entries.length == 0) {
631
+ return [];
632
+ }
633
+ return entries.map(param => {
634
+ const nameNode = param.getChild(SATF_SASF_NODES.NAME);
635
+ return nameNode ? `${text.slice(nameNode.from, nameNode.to)}` : '';
636
+ });
637
+ }
638
+ /** Mapping between SATF and SeqN from Taifun
639
+ UNSIGNED_DECIMAL -> UINT
640
+ SIGNED_DECIMAL -> INT
641
+ HEXADECIMAL -> STRING
642
+ OCTAL -> STRING
643
+ BINARY -> STRING
644
+ ENGINEERING -> FLOAT
645
+ TIME -> STRING
646
+ DURATION -> STRING
647
+ STRING -> STRING | ENUM (if enum_name is defined)
648
+ QUOATED_STRING -> STRING
649
+ NOTE: HEXADECIMAL, OCTAL, BINARY are all uint in seqgen
650
+ */
651
+ function parseParameters(parameterNode, variableType, text) {
652
+ if (!parameterNode) {
653
+ return '';
654
+ }
655
+ const entries = parameterNode.getChildren(SATF_SASF_NODES.ENTRY);
656
+ if (entries && entries.length > 0) {
657
+ let parameter = `@${variableType}_BEGIN\n`;
658
+ parameter += entries
659
+ .map(param => {
660
+ const nameNode = param.getChild(SATF_SASF_NODES.NAME);
661
+ const typeNode = param.getChild(SATF_SASF_NODES.TYPE);
662
+ const rangesNode = param.getChildren(SATF_SASF_NODES.RANGE);
663
+ const enumNameNode = param.getChild(SATF_SASF_NODES.ENUM_NAME);
664
+ const name = nameNode ? `${text.slice(nameNode.from, nameNode.to)}` : '';
665
+ const enumName = enumNameNode ? ` ${text.slice(enumNameNode.from, enumNameNode.to)}` : '';
666
+ let type = typeNode ? text.slice(typeNode.from, typeNode.to).trim() : '';
667
+ switch (type) {
668
+ case SATF_SASF_NODES.PARAM_UNSIGNED_DECIMAL:
669
+ type = SEQN_NODES.VAR_UINT;
670
+ break;
671
+ case SATF_SASF_NODES.PARAM_SIGNED_DECIMAL:
672
+ type = SEQN_NODES.VAR_INT;
673
+ break;
674
+ case SATF_SASF_NODES.PARAM_HEXADECIMAL:
675
+ case SATF_SASF_NODES.PARAM_OCTAL:
676
+ case SATF_SASF_NODES.PARAM_BINARY:
677
+ case SATF_SASF_NODES.PARAM_TIME:
678
+ case SATF_SASF_NODES.PARAM_DURATION:
679
+ case SATF_SASF_NODES.PARAM_QUOTED_STRING:
680
+ type = SEQN_NODES.VAR_STRING;
681
+ break;
682
+ case SATF_SASF_NODES.PARAM_ENGINEERING:
683
+ type = SEQN_NODES.VAR_FLOAT;
684
+ break;
685
+ case SEQN_NODES.VAR_STRING:
686
+ {
687
+ // Always an enum matches the jpl_sequence tool
688
+ //if (enumNameNode) {
689
+ type = SEQN_NODES.VAR_ENUM;
690
+ //} else {
691
+ // type = VAR_STRING;
692
+ //}
693
+ }
694
+ break;
695
+ default:
696
+ console.log(`type: ${type} is not supported`);
697
+ }
698
+ const allowableValues = [];
699
+ const allowableRanges = [];
700
+ rangesNode.forEach((range) => {
701
+ text
702
+ .slice(range.from, range.to)
703
+ .split(',')
704
+ .forEach(r => {
705
+ r = r.replaceAll('"', '').trim();
706
+ if (r.includes('...')) {
707
+ allowableRanges.push(r);
708
+ }
709
+ else {
710
+ allowableValues.push(r);
711
+ }
712
+ });
713
+ });
714
+ return `${name} ${type}${enumName}${allowableRanges.length === 0 ? (allowableValues.length === 0 ? '' : ' ""') : ` "${allowableRanges.join(', ')}"`}${allowableValues.length === 0 ? '' : ` "${allowableValues.join(', ')}"`}`;
715
+ })
716
+ .join('\n');
717
+ parameter += `\n@${variableType}_END`;
718
+ return parameter;
719
+ }
720
+ return '';
721
+ }
722
+ function parseSteps(stepNode, variableNames, text) {
723
+ const step = '';
724
+ if (!stepNode) {
725
+ return step;
726
+ }
727
+ const commandNodes = stepNode.getChildren(SATF_SASF_NODES.COMMAND);
728
+ return commandNodes
729
+ .map(command => {
730
+ const time = parseTimeNode(command.getChild(SATF_SASF_NODES.SCHEDULED_TIME), text);
731
+ const stem = parseStem(command.getChild(SATF_SASF_NODES.STEM), text);
732
+ const comment = parseComment(command.getChild(SATF_SASF_NODES.COMMENT), text);
733
+ const args = parseArgsNode(command.getChild(SATF_SASF_NODES.ARGS), variableNames, text);
734
+ const models = parseModel(command.getChild(SATF_SASF_NODES.ASSUMED_MODEL_VALUES), text);
735
+ const metadata = parseSatfCommandMetadata(command, text);
736
+ //metadata
737
+ return `${time}${stem}${args.length > 0 ? ` ${args}` : ''}${comment.length > 0 ? ` ${comment}` : ''}${metadata.length > 0 ? `\n${metadata}` : ''}${models.length > 0 ? `\n${models}` : ''}`;
738
+ })
739
+ .join('\n');
740
+ }
741
+ function parseTimeNode(timeNode, text) {
742
+ if (!timeNode) {
743
+ return 'C ';
744
+ }
745
+ const timeValueNode = timeNode.getChild(SATF_SASF_NODES.TIME);
746
+ const timeTagNode = timeNode.getChild(SATF_SASF_NODES.TIME_RELATION);
747
+ return parseTimeTagNode(timeValueNode, timeTagNode, text);
748
+ }
749
+ function parseTimeTagNode(timeValueNode, timeTagNode, text) {
750
+ if (timeValueNode && !timeTagNode) {
751
+ return `A${text.slice(timeValueNode.from, timeValueNode.to)} `;
752
+ }
753
+ else if (!timeValueNode || !timeTagNode) {
754
+ return `R00:00:00`;
755
+ }
756
+ const time = text.slice(timeValueNode.from, timeValueNode.to);
757
+ const timeTag = text.slice(timeTagNode.from, timeTagNode.to);
758
+ switch (timeTag.trim()) {
759
+ case 'ABSOLUTE':
760
+ return `A${time} `;
761
+ case 'EPOCH':
762
+ return `E${time} `;
763
+ case 'FROM_PREVIOUS_START':
764
+ return `R${time} `;
765
+ case 'FROM_REQUEST_START':
766
+ case 'FROM_ACTIVITY_START':
767
+ return `B${time} `;
768
+ case 'WAIT_PREVIOUS_END':
769
+ return `C `;
770
+ default:
771
+ return 'error';
772
+ }
773
+ }
774
+ function parseComment(commentNode, text) {
775
+ let comment = commentNode
776
+ ? `${text
777
+ .slice(commentNode.from, commentNode.to)
778
+ .split('\n')
779
+ .map(line => line.trim())
780
+ .join(' ')
781
+ .trim()}` // flatten comment to one line SeqN doesn't support multi-line comments on a command
782
+ : '';
783
+ if (comment.length === 0) {
784
+ return comment;
785
+ }
786
+ return `# ${removeQuote(comment)}`;
787
+ }
788
+ function parseStem(stemNode, text) {
789
+ return stemNode ? text.slice(stemNode.from, stemNode.to) : '';
790
+ }
791
+ function parseArgsNode(argsNode, variableNames, text) {
792
+ if (!argsNode) {
793
+ return '';
794
+ }
795
+ let argNode = argsNode.firstChild;
796
+ const args = [];
797
+ while (argNode) {
798
+ args.push(`${parseArgNode(argNode, variableNames, text)}`);
799
+ argNode = argNode?.nextSibling;
800
+ }
801
+ return args.join(' ');
802
+ }
803
+ function parseArgNode(argNode, variableNames, text) {
804
+ if (!argNode) {
805
+ return '';
806
+ }
807
+ const argValue = removeQuote(text.slice(argNode.from, argNode.to));
808
+ if (variableNames.includes(argValue)) {
809
+ return argValue;
810
+ }
811
+ switch (argNode.name) {
812
+ case SATF_SASF_NODES.STRING:
813
+ return `"${argValue}"`;
814
+ case SATF_SASF_NODES.NUMBER:
815
+ case SATF_SASF_NODES.BOOLEAN:
816
+ case SATF_SASF_NODES.ENUM:
817
+ case SATF_SASF_NODES.GLOBAL:
818
+ return `${argValue}`;
819
+ case SATF_SASF_NODES.ARITHMETICAL:
820
+ return `(${argValue})`;
821
+ default: {
822
+ console.log(`${argNode.name}: ${argValue} is not supported`);
823
+ return 'Error';
824
+ }
825
+ }
826
+ }
827
+ function parsSeqNMetadata(node, text, filter) {
828
+ const metadataNode = node.getChild('Metadata');
829
+ if (!metadataNode) {
830
+ return undefined;
831
+ }
832
+ const metadataEntry = metadataNode.getChildren('MetaEntry');
833
+ if (!metadataEntry || metadataEntry.length === 0) {
834
+ return undefined;
835
+ }
836
+ const obj = [];
837
+ metadataEntry.forEach(entry => {
838
+ const keyNode = entry.getChild('Key');
839
+ const valueNode = entry.getChild('Value');
840
+ if (!keyNode || !valueNode) {
841
+ return; // Skip this entry if either the key or value is missing
842
+ }
843
+ const keyText = unquoteUnescape(text.slice(keyNode.from, keyNode.to));
844
+ if (!filter.includes(keyText)) {
845
+ return;
846
+ }
847
+ let value = text.slice(valueNode.from, valueNode.to);
848
+ try {
849
+ value = JSON.parse(value);
850
+ }
851
+ catch (e) {
852
+ console.log(`Malformed metadata ${value}`);
853
+ }
854
+ obj.push(`${keyText.toUpperCase()},\\${value}\\`);
855
+ });
856
+ return obj.join(`,\n${'\t'.repeat(2)}`);
857
+ }
858
+ function parseSatfCommandMetadata(commandNode, text) {
859
+ let metadata = '';
860
+ if (!commandNode) {
861
+ return metadata;
862
+ }
863
+ const inclusionNode = commandNode.getChild(SATF_SASF_NODES.INCLUSION_CONDITION);
864
+ const drawNode = commandNode.getChild(SATF_SASF_NODES.DRAW);
865
+ const nTextNode = commandNode.getChild(SATF_SASF_NODES.NTEXT);
866
+ if (inclusionNode) {
867
+ metadata += `@METADATA "INCLUSION_CONDITION" "${removeQuote(text.slice(inclusionNode.from, inclusionNode.to))}"\n`;
868
+ }
869
+ if (drawNode) {
870
+ metadata += `@METADATA "DRAW" "${removeQuote(text.slice(drawNode.from, drawNode.to))}"\n`;
871
+ }
872
+ if (nTextNode) {
873
+ metadata += `@METADATA "NTEXT" "${removeQuote(text.slice(nTextNode.from, nTextNode.to))}"\n`;
874
+ }
875
+ return metadata.slice(0, -1);
876
+ }
877
+ function parseModel(modelNode, text) {
878
+ if (!modelNode) {
879
+ return '';
880
+ }
881
+ const modelsNode = modelNode.getChildren(SATF_SASF_NODES.MODEL);
882
+ return modelsNode
883
+ .map(model => {
884
+ const keyNode = model.getChild(SATF_SASF_NODES.KEY);
885
+ const valueNode = model.getChild(SATF_SASF_NODES.VALUE);
886
+ if (!keyNode || !valueNode) {
887
+ return null;
888
+ }
889
+ return `@MODEL "${text.slice(keyNode.from, keyNode.to)}" ${text.slice(valueNode.from, valueNode.to)} "00:00:00"`;
890
+ })
891
+ .filter(model => model !== null)
892
+ .join('\n');
893
+ }
894
+ function parseSeqNModel(node, text) {
895
+ const modelContainer = node.getChild('Models');
896
+ if (!modelContainer) {
897
+ return undefined;
898
+ }
899
+ const modelNodes = modelContainer.getChildren('Model');
900
+ if (!modelNodes || modelNodes.length === 0) {
901
+ return undefined;
902
+ }
903
+ const models = [];
904
+ for (const modelNode of modelNodes) {
905
+ const variableNode = modelNode.getChild('Variable');
906
+ const valueNode = modelNode.getChild('Value');
907
+ const variable = variableNode ? unquoteUnescape(text.slice(variableNode.from, variableNode.to)) : 'UNKNOWN';
908
+ // Value can be string, number or boolean
909
+ let value;
910
+ const valueChild = valueNode?.firstChild;
911
+ if (valueChild) {
912
+ const valueText = text.slice(valueChild.from, valueChild.to);
913
+ if (valueChild.name === 'String') {
914
+ value = valueText;
915
+ }
916
+ else if (valueChild.name === 'Boolean') {
917
+ value = !/^FALSE$/i.test(valueText);
918
+ }
919
+ else if (valueChild.name === 'Number') {
920
+ value = Number(valueText);
921
+ }
922
+ }
923
+ models.push(`${variable}=${value}`);
924
+ }
925
+ return models.join(',');
926
+ }
927
+ //# sourceMappingURL=satf-sasf-utils.js.map