Skip to content

Commit

Permalink
Add Load and Activate Real Time Commands to SeqN (#1543)
Browse files Browse the repository at this point in the history
* Update grammar for load and activate

* Allow for optional timetag so they can be used as immediate commands

* Add rtc load and activate to content assist

* Add linting for rtc load and activate

* export rtc load and activate and unit test

* Import rtc load and activate from  SeqJson

* Support the new immediateActivate and immediateLoad objects in the SeqJson
* Update the test

* Update the json schema npm package

* Address Bryan PR reviews
  • Loading branch information
goetzrrGit authored Jan 3, 2025
1 parent e9a346c commit a2bed7f
Show file tree
Hide file tree
Showing 9 changed files with 269 additions and 82 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"dependencies": {
"@fontsource/jetbrains-mono": "^5.0.19",
"@nasa-jpl/aerie-ampcs": "^1.0.5",
"@nasa-jpl/seq-json-schema": "^1.0.20",
"@nasa-jpl/seq-json-schema": "^1.2.0",
"@nasa-jpl/stellar": "^1.1.18",
"@streamparser/json": "^0.0.17",
"@sveltejs/adapter-node": "5.0.1",
Expand Down
4 changes: 2 additions & 2 deletions src/utilities/codemirror/sequence.grammar
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ Command {
Models?
}

Activate { TimeTag activateDirective commonLoadActivate }
Activate { TimeTag? activateDirective commonLoadActivate }

Load { TimeTag loadDirective commonLoadActivate }
Load { TimeTag? loadDirective commonLoadActivate }

commonLoadActivate {
"(" SequenceName { String } ")"
Expand Down
25 changes: 25 additions & 0 deletions src/utilities/sequence-editor/from-seq-json.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,16 +461,38 @@ C FSW_CMD_2 10 "ENUM" # fsw cmd 2 description
description: 'immediate command',
metadata: {},
stem: 'IC',
type: 'immediate_command',
},
{
args: [],
stem: 'IC2',
type: 'immediate_command',
},
{
args: [],
description: 'noop command, no arguments',
metadata: { processor: 'VC1A' },
stem: 'NOOP',
type: 'immediate_command',
},
{
args: [
{
type: 'string',
value: 'hi',
},
],
description: 'description',
metadata: {
Key: 'Value',
},
sequence: 'seqA',
type: 'immediate_load',
},
{
description: 'description',
sequence: 'seqB',
type: 'immediate_activate',
},
],
metadata: {},
Expand All @@ -484,6 +506,9 @@ IC "1" 2 3 # immediate command
IC2
NOOP # noop command, no arguments
@METADATA "processor" "VC1A"
@LOAD("seqA") "hi" # description
@METADATA "Key" "Value"
@ACTIVATE("seqB") # description
`;
expect(sequence).toEqual(expectedSequence);
});
Expand Down
66 changes: 47 additions & 19 deletions src/utilities/sequence-editor/from-seq-json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import type {
GroundBlock,
GroundEvent,
HexArgument,
ImmediateActivate,
ImmediateFswCommand,
ImmediateLoad,
Load,
Metadata,
Model,
Expand Down Expand Up @@ -205,18 +208,22 @@ export async function seqJsonToSequence(input: string | null): Promise<string> {
if (seqJson.immediate_commands) {
sequence.push(`\n`);
sequence.push(`@IMMEDIATE\n`);
for (const icmd of seqJson.immediate_commands) {
const args = seqJsonArgsToSequence(icmd.args);
const description = icmd.description ? seqJsonDescriptionToSequence(icmd.description) : '';
const metadata = icmd.metadata ? seqJsonMetadataToSequence(icmd.metadata) : '';
let immediateString = `${icmd.stem}${args}${description}`;
// add a new line if on doesn't exit at the end of the immediateString
if (!immediateString.endsWith('\n')) {
immediateString += '\n';
for (const realTimeCommand of seqJson.immediate_commands) {
switch (realTimeCommand.type) {
case 'immediate_command': {
// FSW Commands
sequence.push(commandToString(realTimeCommand));
break;
}
case 'immediate_activate':
case 'immediate_load': {
sequence.push(loadOrActivateToString(realTimeCommand));
break;
}
default: {
throw new Error(`Invalid immediate command type ${realTimeCommand.type}`);
}
}
// Add metadata data if it exists
immediateString += metadata;
sequence.push(immediateString);
}
}

Expand Down Expand Up @@ -250,14 +257,24 @@ export async function seqJsonToSequence(input: string | null): Promise<string> {
return sequence.join('');
}

function commandToString(step: Command) {
const time = seqJsonTimeToSequence(step.time);
function isCommand(step: Command | ImmediateFswCommand): step is Command {
return (step as Command).time !== undefined;
}

function commandToString(step: Command | ImmediateFswCommand): string {
const args = seqJsonArgsToSequence(step.args);
const metadata = step.metadata ? seqJsonMetadataToSequence(step.metadata) : '';
const models = step.models ? seqJsonModelsToSequence(step.models) : '';
const description = step.description ? seqJsonDescriptionToSequence(step.description) : '';

let commandString = `${time} ${step.stem}${args}${description}`;
// used for commands, ImmediateFswCommand doesn't support 'time' and 'models'
let time = '';
let models = '';
if (isCommand(step)) {
time = step.time ? `${seqJsonTimeToSequence(step.time)} ` : '';
models = step.models ? (step.models ? seqJsonModelsToSequence(step.models) : '') : '';
}

let commandString = `${time}${step.stem}${args}${description}`;
// add a new line if on doesn't exit at the end of the commandString
if (!commandString.endsWith('\n')) {
commandString += '\n';
Expand All @@ -267,16 +284,27 @@ function commandToString(step: Command) {
return commandString;
}

function loadOrActivateToString(step: Activate | Load) {
const time = seqJsonTimeToSequence(step.time);
function loadOrActivateToString(step: Activate | Load | ImmediateActivate | ImmediateLoad) {
const time = (step as Activate | Load).time ? `${seqJsonTimeToSequence((step as Activate | Load).time)} ` : '';
const args = step.args ? seqJsonArgsToSequence(step.args) : '';
const metadata = step.metadata ? seqJsonMetadataToSequence(step.metadata) : '';
const models = step.models ? seqJsonModelsToSequence(step.models) : '';
const engine = step.engine !== undefined ? `@ENGINE ${step.engine.toString(10)}\n` : '';
const epoch = step.epoch !== undefined ? `@EPOCH ${quoteEscape(step.epoch)}\n` : '';
const description = step.description ? seqJsonDescriptionToSequence(step.description) : '';
const stepType = `@${step.type === 'activate' ? 'ACTIVATE' : 'LOAD'}(${quoteEscape(step.sequence)})`;
let stepString = `${time} ${stepType}${args}${description}`;
let stepType = '';
switch (step.type) {
case 'activate':
case 'immediate_activate':
stepType = '@ACTIVATE';
break;
case 'load':
case 'immediate_load':
stepType = '@LOAD';
break;
}
stepType += `(${quoteEscape(step.sequence)})`;
let stepString = `${time}${stepType}${args}${description}`;
if (!stepString.endsWith('\n')) {
stepString += '\n';
}
Expand Down
33 changes: 19 additions & 14 deletions src/utilities/sequence-editor/sequence-completion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,8 +385,8 @@ function generateHardwareCompletions(commandDictionary: CommandDictionary | null
}

function generateStepCompletion(cursor: CursorInfo): Completion[] {
// if cursor is at the LineComment/Description don't show the command completions list
if (cursor.isAtLineComment || !cursor.isBeforeHDWCommands || !cursor.isBeforeImmedOrHDWCommands) {
// if cursor is at the LineComment/Description after hardware commands don't show the command completions list
if (cursor.isAtLineComment || !cursor.isBeforeHDWCommands) {
return [];
}

Expand All @@ -397,13 +397,13 @@ function generateStepCompletion(cursor: CursorInfo): Completion[] {
view.dispatch({
changes: {
from: Math.max(0, from + (!cursor.isAfterTimeTag || cursor.isAtSymbolBefore ? -1 : 0)),
insert: `${!cursor.isAfterTimeTag ? 'C ' : ''}@GROUND_EVENT("ground_event.name")`,
insert: `${!cursor.isAfterTimeTag && cursor.isBeforeImmedOrHDWCommands ? 'C ' : ''}@ACTIVATE("activate.name")`,
to,
},
});
},
info: 'ground event command',
label: '@GROUND_EVENT',
info: 'activate command',
label: '@ACTIVATE',
section: 'Ground Commands',
type: 'function',
});
Expand All @@ -413,29 +413,34 @@ function generateStepCompletion(cursor: CursorInfo): Completion[] {
view.dispatch({
changes: {
from: Math.max(0, from + (!cursor.isAfterTimeTag || cursor.isAtSymbolBefore ? -1 : 0)),
insert: `${!cursor.isAfterTimeTag ? 'C ' : ''}@GROUND_BLOCK("ground_block.name")`,
insert: `${!cursor.isAfterTimeTag && cursor.isBeforeImmedOrHDWCommands ? 'C ' : ''}@LOAD("load.name")`,
to,
},
});
},
info: 'ground block command',
label: '@GROUND_BLOCK',
info: 'load command',
label: '@LOAD',
section: 'Ground Commands',
type: 'function',
});

// if after immediate commands don't show the command completions list below
if (!cursor.isBeforeImmedOrHDWCommands) {
return stepCompletion;
}

stepCompletion.push({
apply: (view, _completion, from: number, to: number) => {
view.dispatch({
changes: {
from: Math.max(0, from + (!cursor.isAfterTimeTag || cursor.isAtSymbolBefore ? -1 : 0)),
insert: `${!cursor.isAfterTimeTag ? 'C ' : ''}@ACTIVATE("activate.name")`,
insert: `${!cursor.isAfterTimeTag ? 'C ' : ''}@GROUND_EVENT("ground_event.name")`,
to,
},
});
},
info: 'activate command',
label: '@ACTIVATE',
info: 'ground event command',
label: '@GROUND_EVENT',
section: 'Ground Commands',
type: 'function',
});
Expand All @@ -445,13 +450,13 @@ function generateStepCompletion(cursor: CursorInfo): Completion[] {
view.dispatch({
changes: {
from: Math.max(0, from + (!cursor.isAfterTimeTag || cursor.isAtSymbolBefore ? -1 : 0)),
insert: `${!cursor.isAfterTimeTag ? 'C ' : ''}@LOAD("load.name")`,
insert: `${!cursor.isAfterTimeTag ? 'C ' : ''}@GROUND_BLOCK("ground_block.name")`,
to,
},
});
},
info: 'load command',
label: '@LOAD',
info: 'ground block command',
label: '@GROUND_BLOCK',
section: 'Ground Commands',
type: 'function',
});
Expand Down
18 changes: 16 additions & 2 deletions src/utilities/sequence-editor/sequence-linter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,11 @@ export function sequenceLinter(
if (commandsNode) {
diagnostics.push(
...commandLinter(
commandsNode.getChildren(TOKEN_COMMAND),
[
...commandsNode.getChildren(TOKEN_COMMAND),
...commandsNode.getChildren(TOKEN_LOAD), // TODO: remove in the library sequence PR because that check should validate load and activates
...commandsNode.getChildren(TOKEN_ACTIVATE), // TODO: remove in the library sequence PR because that check should validate load and activates
],
docText,
variableMap,
commandDictionary,
Expand All @@ -155,7 +159,11 @@ export function sequenceLinter(

diagnostics.push(
...immediateCommandLinter(
treeNode.getChild('ImmediateCommands')?.getChildren(TOKEN_COMMAND) || [],
[
...(treeNode.getChild('ImmediateCommands')?.getChildren(TOKEN_COMMAND) || []),
...(treeNode.getChild('ImmediateCommands')?.getChildren(TOKEN_LOAD) || []),
...(treeNode.getChild('ImmediateCommands')?.getChildren(TOKEN_ACTIVATE) || []),
],
docText,
variableMap,
commandDictionary,
Expand Down Expand Up @@ -693,6 +701,12 @@ function commandLinter(
// Get the TimeTag node for the current command
diagnostics.push(...validateTimeTags(command, text));

// TODO: remove in the library sequence PR because that check should validate
// load and activates
if (command.name === TOKEN_ACTIVATE || command.name === TOKEN_LOAD) {
continue;
}

// Validate the command and push the generated diagnostics to the array
diagnostics.push(
...validateCommand(
Expand Down
43 changes: 43 additions & 0 deletions src/utilities/sequence-editor/to-seq-json.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,49 @@ HDW_CMD`;
expect(actual).toEqual(expectedJson);
});

it('immediate command', async () => {
const seq = `@IMMEDIATE
ECHO "hello"
# activate sequences
@LOAD("seqA")
@ACTIVATE("seqB") 10 #description`;
const id = 'test';
const expectedJson = {
id: 'test',
immediate_commands: [
{
args: [
{
type: 'string',
value: 'hello',
},
],
stem: 'ECHO',
type: 'immediate_command',
},
{
args: [],
sequence: 'seqA',
type: 'immediate_load',
},
{
args: [
{
type: 'number',
value: 10,
},
],
description: 'description',
sequence: 'seqB',
type: 'immediate_activate',
},
],
metadata: {},
};
const actual = JSON.parse(await sequenceToSeqJson(SeqLanguage.parser.parse(seq), seq, commandDictionary, id));
expect(actual).toEqual(expectedJson);
});

it('multiple hardware commands', async () => {
const seq = `@HARDWARE
HDW_CMD_1
Expand Down
Loading

0 comments on commit a2bed7f

Please sign in to comment.