Skip to content

FpySequencer Fix no response from validate, refactor to prevent this class of issu…#5315

Open
zimri-leisher wants to merge 5 commits into
nasa:develfrom
zimri-leisher:5314-fpy-validate-no-cmd-resp
Open

FpySequencer Fix no response from validate, refactor to prevent this class of issu…#5315
zimri-leisher wants to merge 5 commits into
nasa:develfrom
zimri-leisher:5314-fpy-validate-no-cmd-resp

Conversation

@zimri-leisher

@zimri-leisher zimri-leisher commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator
Related Issue(s) #5314
Has Unit Tests (y/n) y
Documentation Included (y/n) y
Generative AI was used in this contribution (y/n) y

Change Description

  • Fix issue FpySequencer no cmd response if running VALIDATE after first RUN with NO_BLOCK #5314, if you run RUN with NO_BLOCK and then run VALIDATE, you don't get a response from VALIDATE
  • Refactor the state machine and member variables slightly to prevent this class of issue from occurring
  • The refactor brought up some other issues with the design of the SEQ_BASE_DIR param, which necessitated making it a compile time constant shared among all sequencers
    • The refactor made it so that the seq file path, block state and sequence arguments are all modified together, at once
    • This meant that the file path had to be set the moment the user calls the RUN cmd, e.g.
    • But because setting the file path can fail (due to truncation when the SEQ_BASE_DIR param would make the result too large), we need to allow it to gracefully fail
    • Right now, there are only two ways that a sequence can fail to run: in the validation state (header issues, crc, etc) or in the run state (early exit with error, overflow, etc)
    • I decided to refactor to move the file path concatenation into the validate() method, which can just return Fw::Success:FAILURE if there is overflow.
    • However, by moving the calculation of the actual sequence file path into the validate() method, we can't report the actual sequence file path in the seqStartOut port until we've already started validating
    • This is a problem, because it means there would be a brief window where the sequence is actually occupied (running VALIDATE) but hasn't reported that it is occupied (through seqStartOut)
    • But if we make the SEQ_BASE_DIR path a compile time constant, then both sides know what the path prefix is, so I don't need to include it in the seqStartOut port call
    • This brings up some more fundamental issues about sequence identity. Also, I think the existing CmdSequencer should use the SEQ_BASE_DIR compile time constant too, but that's for later.

Important note: this makes it somewhat more difficult to call sequences at absolute paths, because there is no option to turn off the suffix. However, I think this is okay, as it's a compile time constant, so as long as you know what FSW you're running, you know the path prefix, and you can always just back out of the prefix directory.

One small other change was adding single quotes around file names in event format strings, so that you can differentiate between the path and the event msg.

Also, updated the state machine SDD

AI Usage (see policy)

Used for writing tests and some final edits. Mostly human written (a rarity for me these days)

// override the getSequenceBaseDir() helper to return a settable member.
class TestFpySequencer : public FpySequencer {
public:
TestFpySequencer(const char* compName) : FpySequencer(compName), m_baseDirOverride(Fpy::SEQ_BASE_DIR) {}

// returns the base directory against which sequence file paths are resolved.
// defaults to the SEQ_BASE_DIR config constant; overridable in tests/deployments
const char* FpySequencer::getSequenceBaseDir() const {
this->m_computedCRC.init();

// concat the seq base dir prefix to the seq file path
const char* baseDir = this->getSequenceBaseDir();
Svc_FpySequencer_SequencerStateMachine::Signal signal,
const Svc::FpySequencer_SequenceExecutionArgs& value) {
this->m_sequenceArgs = value.get_buffer();
this->m_sequenceExecArgs = value;
Svc_FpySequencer_SequencerStateMachine::Signal signal,
const Svc::FpySequencer_SequenceExecutionArgs& value) {
this->m_sequenceArgs = value.get_buffer();
this->m_sequenceExecArgs = value;
// also, the filePath string in SequenceExecutionArgs is guaranteed
// to be truncated to FileNameStringSize chars, so it will not
// be truncated by this assignment
this->m_fullSequenceFilePath = this->m_sequenceExecArgs.get_filePath();

if (fileStatus != Os::File::OP_OK) {
this->log_WARNING_HI_FileReadError(readStage, this->m_sequenceFilePath, static_cast<I32>(fileStatus));
this->log_WARNING_HI_FileReadError(readStage, this->m_fullSequenceFilePath, static_cast<I32>(fileStatus));

if (actualReadLen < expectedReadLen) {
this->log_WARNING_HI_EndOfFileError(readStage, this->m_sequenceFilePath);
this->log_WARNING_HI_EndOfFileError(readStage, this->m_fullSequenceFilePath);
@github-actions

github-actions Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Coverage report — base devel

Overall (line): 81.38% → 81.37% (-0.01)
Regression threshold: 0.50% (line).

Regressions

(none over threshold)

Modules changed

Module Line Δ Function Δ Branch Δ
Svc/FpySequencer 86.46 -0.11 98.08 -0.49 76.52 -0.23
Os/Posix 62.40 +0.40 84.21 +0.00 44.10 +0.23

Modules without UTs

CFDP/Checksum/GTest, Drv/ByteStreamDriverModel, Drv/Interfaces, Drv/LinuxGpioDriver, Drv/LinuxI2cDriver, Drv/LinuxSpiDriver, Drv/LinuxUartDriver, Drv/Ports, Drv/Ports/DataTypes, FppTestProject/FppTest/interfaces, FppTestProject/FppTest/topology/async, FppTestProject/FppTest/topology/components/Comp, FppTestProject/FppTest/topology/components/Framework, FppTestProject/FppTest/topology/components/Receiver, FppTestProject/FppTest/topology/components/Sender, FppTestProject/FppTest/topology/guarded, FppTestProject/FppTest/topology/ports, FppTestProject/FppTest/topology/sync, FppTestProject/FppTest/topology/top_ports, FppTestProject/FppTest/topology/types, Fw/Cmd, Fw/Com, Fw/Comp, Fw/FilePacket/GTest, Fw/Fpy, Fw/Interfaces, Fw/Obj, Fw/Port, Fw/Ports/CompletionStatus, Fw/Ports/Ready, Fw/Ports/Signal, Fw/Ports/SuccessCondition, Fw/Prm, Fw/SerializableFile/test/TestSerializable, Fw/Sm, Fw/Test, Fw/Types/GTest, Os/Models, Svc/Cycle, Svc/DpPorts, Svc/Fatal, Svc/FatalHandler, Svc/FileDownlinkPorts, Svc/FprimeProtocol, Svc/Interfaces, Svc/PassiveConsoleTextLogger, Svc/Ping, Svc/PolyIf, Svc/Ports/CommsPorts, Svc/Ports/FilePorts, Svc/Ports/OsTimeEpoch, Svc/Ports/TlmPacketizerPorts, Svc/Ports/VersionPorts, Svc/Sched, Svc/Seq, Svc/Subtopologies/CdhCore, Svc/Subtopologies/ComCcsds, Svc/Subtopologies/ComFprime, Svc/Subtopologies/ComLoggerTee, Svc/Subtopologies/DataProducts, Svc/Subtopologies/FileHandling, Svc/Types/TlmPacketizerTypes, Svc/WatchDog, TestDeploymentsProject/Ref/PingReceiver, TestDeploymentsProject/Ref/RecvBuffApp, TestDeploymentsProject/Ref/SendBuffApp, TestDeploymentsProject/Ref/Top, TestDeploymentsProject/Ref/TypeDemo, cmake/test/data/TestDeployment/TestBuildAutocoder, cmake/test/data/TestDeployment/TestChainedAutocoder, cmake/test/data/TestDeployment/TestHeaderAutocoder, cmake/test/data/TestDeployment/TestTargetAutocoder, cmake/test/data/test-fprime-library/TestLibrary/TestComponent, cmake/test/data/test-fprime-library2/TestLibrary2/TestComponent

// * the block state
// * the sequence runtime args
// See the struct member docstrings for more info
Svc::FpySequencer_SequenceExecutionArgs m_sequenceExecArgs;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to begin running the sequence, we need these three args (filename, block state and seq runtime args). Store them all in a single struct so updates to them can be "atomic"


// the full (including the SEQ_BASE_DIR prefix) path
// to the sequence file. Note: not updated until validate() is run
Fw::FileNameString m_fullSequenceFilePath;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the file name provided in the sequence exec args is not the "final" filename that we actually try to open. we need to prefix with seq base dir first.

action setSequenceBlockState: SequenceExecutionArgs
@ sets the arguments to pass to the sequence
action setSequenceArguments: SequenceExecutionArgs
@ sets the sequence execution arguments (seq path, block state, seq args)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a clear example of how i made the args "atomic"--you can only update all three at once

// loads the sequence in memory, and does header/crc/integrity checks.
// return SUCCESS if sequence is valid, FAILURE otherwise
Fw::Success FpySequencer::validate() {
FW_ASSERT(this->m_sequenceFilePath.length() > 0);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

honestly no idea how this assert got in here. it was a clear mistake. the sequencer should not crash if the path length is zero. this has been here for a while.

It is impossible to send an empty string arg in fprime-gds, which I think is the wrong behavior, but was in practice preventing this from occurring.

stateDiagram-v2
direction LR
state "IDLE
entry / clearBreakpoint, clearSequenceFile

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

state machine sdd hadn't been updated in a while

@zimri-leisher zimri-leisher marked this pull request as ready for review June 25, 2026 00:57
@zimri-leisher

Copy link
Copy Markdown
Collaborator Author

@LeStarch @thomas-bc whenever you get a sec, this PR is ready for review

@zimri-leisher

zimri-leisher commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator Author

TODO make sure we can call abs paths

EDIT--updated description to make it clear that you will have to prefix "../../" etc out of the SEQ_BASE_DIR prefix if you want to call something outside of seq base dir. See desc for rationale

@zimri-leisher zimri-leisher changed the title Fix no response from validate, refactor to prevent this class of issu… FpySequencer Fix no response from validate, refactor to prevent this class of issu… Jul 1, 2026
@zimri-leisher zimri-leisher marked this pull request as draft July 1, 2026 16:00
@zimri-leisher zimri-leisher marked this pull request as ready for review July 4, 2026 17:57

@LeStarch LeStarch left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure I agree with this change. It pushes the need to understand internal details of this component (how full paths are constructed, what command arguments look like) through a port interface and into any user of that port.

This means that any time this component is adjusted, remote components need to be adjusted in parallel to ensure that they remain in-sync with this component.

It would be much better, in my opinion, to send out the full-file path on the seq start port as that is really the expected contract of that port. There are several other ways that could be explored to accomplish this:

  1. Compute the full file path where it was, store the path and the status, check status in validate and emit an error.
  2. Consider only "starting a sequence" if it passes validate
  3. Allow for errors that can occur before validate runs (like we did before).

// we report the raw operator-supplied path rather than m_fullSequenceFilePath
// because this action runs in the VALIDATING entry, before validate() resolves
// the base dir prefix into m_fullSequenceFilePath
this->seqStartOut_out(0, this->m_sequenceExecArgs.get_filePath(), this->m_sequenceExecArgs.get_runArgsBuf());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this breaks encapsulation.

The expected contract of this port is that it includes the sequence file path that was run. The remote receiver should not need to know the internals of how FpySequencer constructs the file path in order to have that information. Nor should the remote component need to know how to handle operator commands to this particular component.

@zimri-leisher

Copy link
Copy Markdown
Collaborator Author

Fair point. I think I was imagining a future in which all sequence ecosystem components would use the SEQ_BASE_DIR prefix, so everyone would be "speaking the same language" when it comes to sequence paths.

  1. Compute the full file path where it was, store the path and the status, check status in validate and emit an error.
  2. Consider only "starting a sequence" if it passes validate
  3. Allow for errors that can occur before validate runs (like we did before).

I'll consider these options again. Another option which I think might be the best, is a new "seqRunReject" port back to the SeqDispatcher.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants