@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.
- package/LICENSE +21 -0
- package/README.md +2 -0
- package/dist/cjs/converters/satf-sasf-utils.d.ts +48 -0
- package/dist/cjs/converters/satf-sasf-utils.d.ts.map +1 -0
- package/dist/cjs/converters/satf-sasf-utils.js +939 -0
- package/dist/cjs/converters/satf-sasf-utils.js.map +1 -0
- package/dist/cjs/converters/seqJsonToSeqn.d.ts +6 -0
- package/dist/cjs/converters/seqJsonToSeqn.d.ts.map +1 -0
- package/dist/cjs/converters/seqJsonToSeqn.js +310 -0
- package/dist/cjs/converters/seqJsonToSeqn.js.map +1 -0
- package/dist/cjs/converters/seqnToSeqJson.d.ts +11 -0
- package/dist/cjs/converters/seqnToSeqJson.d.ts.map +1 -0
- package/dist/cjs/converters/seqnToSeqJson.js +713 -0
- package/dist/cjs/converters/seqnToSeqJson.js.map +1 -0
- package/dist/cjs/index.d.ts +9 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +22 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/languages/satf/constants/satf-sasf-constants.d.ts +139 -0
- package/dist/cjs/languages/satf/constants/satf-sasf-constants.d.ts.map +1 -0
- package/dist/cjs/languages/satf/constants/satf-sasf-constants.js +143 -0
- package/dist/cjs/languages/satf/constants/satf-sasf-constants.js.map +1 -0
- package/dist/cjs/languages/satf/grammar/satf-sasf.d.ts +4 -0
- package/dist/cjs/languages/satf/grammar/satf-sasf.d.ts.map +1 -0
- package/dist/cjs/languages/satf/grammar/satf-sasf.grammar.d.ts +3 -0
- package/dist/cjs/languages/satf/grammar/satf-sasf.grammar.d.ts.map +1 -0
- package/dist/cjs/languages/satf/grammar/satf-sasf.grammar.js +20 -0
- package/dist/cjs/languages/satf/grammar/satf-sasf.grammar.js.map +1 -0
- package/dist/cjs/languages/satf/grammar/satf-sasf.grammar.terms.d.ts +69 -0
- package/dist/cjs/languages/satf/grammar/satf-sasf.grammar.terms.d.ts.map +1 -0
- package/dist/cjs/languages/satf/grammar/satf-sasf.grammar.terms.js +7 -0
- package/dist/cjs/languages/satf/grammar/satf-sasf.grammar.terms.js.map +1 -0
- package/dist/cjs/languages/satf/grammar/satf-sasf.js +56 -0
- package/dist/cjs/languages/satf/grammar/satf-sasf.js.map +1 -0
- package/dist/cjs/languages/satf/types/types.d.ts +23 -0
- package/dist/cjs/languages/satf/types/types.d.ts.map +1 -0
- package/dist/cjs/languages/satf/types/types.js +3 -0
- package/dist/cjs/languages/satf/types/types.js.map +1 -0
- package/dist/cjs/languages/seq-n/seq-n.d.ts +2 -0
- package/dist/cjs/languages/seq-n/seq-n.d.ts.map +1 -0
- package/dist/cjs/languages/seq-n/seq-n.grammar.d.ts +3 -0
- package/dist/cjs/languages/seq-n/seq-n.grammar.d.ts.map +1 -0
- package/dist/cjs/languages/seq-n/seq-n.grammar.js +24 -0
- package/dist/cjs/languages/seq-n/seq-n.grammar.js.map +1 -0
- package/dist/cjs/languages/seq-n/seq-n.grammar.terms.d.ts +39 -0
- package/dist/cjs/languages/seq-n/seq-n.grammar.terms.d.ts.map +1 -0
- package/dist/cjs/languages/seq-n/seq-n.grammar.terms.js +6 -0
- package/dist/cjs/languages/seq-n/seq-n.grammar.terms.js.map +1 -0
- package/dist/cjs/languages/seq-n/seq-n.js +6 -0
- package/dist/cjs/languages/seq-n/seq-n.js.map +1 -0
- package/dist/cjs/languages/seq-n/seqn-grammar-constants.d.ts +45 -0
- package/dist/cjs/languages/seq-n/seqn-grammar-constants.d.ts.map +1 -0
- package/dist/cjs/languages/seq-n/seqn-grammar-constants.js +48 -0
- package/dist/cjs/languages/seq-n/seqn-grammar-constants.js.map +1 -0
- package/dist/cjs/logger.d.ts +3 -0
- package/dist/cjs/logger.d.ts.map +1 -0
- package/dist/cjs/logger.js +11 -0
- package/dist/cjs/logger.js.map +1 -0
- package/dist/cjs/utils/string.d.ts +8 -0
- package/dist/cjs/utils/string.d.ts.map +1 -0
- package/dist/cjs/utils/string.js +32 -0
- package/dist/cjs/utils/string.js.map +1 -0
- package/dist/esm/converters/satf-sasf-utils.d.ts +48 -0
- package/dist/esm/converters/satf-sasf-utils.d.ts.map +1 -0
- package/dist/esm/converters/satf-sasf-utils.js +927 -0
- package/dist/esm/converters/satf-sasf-utils.js.map +1 -0
- package/dist/esm/converters/seqJsonToSeqn.d.ts +6 -0
- package/dist/esm/converters/seqJsonToSeqn.d.ts.map +1 -0
- package/dist/esm/converters/seqJsonToSeqn.js +305 -0
- package/dist/esm/converters/seqJsonToSeqn.js.map +1 -0
- package/dist/esm/converters/seqnToSeqJson.d.ts +11 -0
- package/dist/esm/converters/seqnToSeqJson.d.ts.map +1 -0
- package/dist/esm/converters/seqnToSeqJson.js +705 -0
- package/dist/esm/converters/seqnToSeqJson.js.map +1 -0
- package/dist/esm/index.d.ts +9 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +8 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/languages/satf/constants/satf-sasf-constants.d.ts +139 -0
- package/dist/esm/languages/satf/constants/satf-sasf-constants.d.ts.map +1 -0
- package/dist/esm/languages/satf/constants/satf-sasf-constants.js +139 -0
- package/dist/esm/languages/satf/constants/satf-sasf-constants.js.map +1 -0
- package/dist/esm/languages/satf/grammar/satf-sasf.d.ts +4 -0
- package/dist/esm/languages/satf/grammar/satf-sasf.d.ts.map +1 -0
- package/dist/esm/languages/satf/grammar/satf-sasf.grammar.d.ts +3 -0
- package/dist/esm/languages/satf/grammar/satf-sasf.grammar.d.ts.map +1 -0
- package/dist/esm/languages/satf/grammar/satf-sasf.grammar.js +17 -0
- package/dist/esm/languages/satf/grammar/satf-sasf.grammar.js.map +1 -0
- package/dist/esm/languages/satf/grammar/satf-sasf.grammar.terms.d.ts +69 -0
- package/dist/esm/languages/satf/grammar/satf-sasf.grammar.terms.d.ts.map +1 -0
- package/dist/esm/languages/satf/grammar/satf-sasf.grammar.terms.js +3 -0
- package/dist/esm/languages/satf/grammar/satf-sasf.grammar.terms.js.map +1 -0
- package/dist/esm/languages/satf/grammar/satf-sasf.js +53 -0
- package/dist/esm/languages/satf/grammar/satf-sasf.js.map +1 -0
- package/dist/esm/languages/satf/types/types.d.ts +23 -0
- package/dist/esm/languages/satf/types/types.d.ts.map +1 -0
- package/dist/esm/languages/satf/types/types.js +2 -0
- package/dist/esm/languages/satf/types/types.js.map +1 -0
- package/dist/esm/languages/seq-n/seq-n.d.ts +2 -0
- package/dist/esm/languages/seq-n/seq-n.d.ts.map +1 -0
- package/dist/esm/languages/seq-n/seq-n.grammar.d.ts +3 -0
- package/dist/esm/languages/seq-n/seq-n.grammar.d.ts.map +1 -0
- package/dist/esm/languages/seq-n/seq-n.grammar.js +21 -0
- package/dist/esm/languages/seq-n/seq-n.grammar.js.map +1 -0
- package/dist/esm/languages/seq-n/seq-n.grammar.terms.d.ts +39 -0
- package/dist/esm/languages/seq-n/seq-n.grammar.terms.d.ts.map +1 -0
- package/dist/esm/languages/seq-n/seq-n.grammar.terms.js +3 -0
- package/dist/esm/languages/seq-n/seq-n.grammar.terms.js.map +1 -0
- package/dist/esm/languages/seq-n/seq-n.js +3 -0
- package/dist/esm/languages/seq-n/seq-n.js.map +1 -0
- package/dist/esm/languages/seq-n/seqn-grammar-constants.d.ts +45 -0
- package/dist/esm/languages/seq-n/seqn-grammar-constants.d.ts.map +1 -0
- package/dist/esm/languages/seq-n/seqn-grammar-constants.js +45 -0
- package/dist/esm/languages/seq-n/seqn-grammar-constants.js.map +1 -0
- package/dist/esm/logger.d.ts +3 -0
- package/dist/esm/logger.d.ts.map +1 -0
- package/dist/esm/logger.js +7 -0
- package/dist/esm/logger.js.map +1 -0
- package/dist/esm/utils/string.d.ts +8 -0
- package/dist/esm/utils/string.d.ts.map +1 -0
- package/dist/esm/utils/string.js +25 -0
- package/dist/esm/utils/string.js.map +1 -0
- 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
|