#ifndef STREAMFSM_H #define STREAMFSM_H #include #include #include #include #include /// A formal 2-state FSM wrapping a QProcess streaming lifecycle. /// /// States: Idle ──start()──→ Running ──stop()──→ Idle /// Running ──process exit──→ Idle ──[policy]──→ Running | emit died() /// /// Three restart policies: /// Oneshot — process runs once, then emits died() /// Looping — process always restarts after exit /// Guarded — process restarts only if guard() returns true /// /// All signal connections are managed internally. stop() is safe to call /// from any state; it disconnects before killing to prevent spurious signals. class StreamFsm : public QObject { Q_OBJECT public: enum State { Idle, Running }; enum RestartPolicy { Oneshot, Looping, Guarded }; explicit StreamFsm(RestartPolicy policy, QObject *parent = nullptr); ~StreamFsm(); State state() const { return m_state; } RestartPolicy policy() const { return m_policy; } /// Store program/args and start the process (no-op if already Running). void start(const QString &program, const QStringList &args); /// Kill the process and transition to Idle. Does not emit died(). void stop(); /// Guard predicate for Guarded policy — called after exit to decide /// whether to restart. Not used for Oneshot or Looping. void setGuard(std::function guard); signals: /// Emitted with data from the process's stdout (partial reads). void readyRead(const QByteArray &data); /// Emitted when the process has exited and will NOT be restarted /// (Oneshot policy, or Guarded policy with a false guard). void died(); private slots: void onReadyRead(); void onFinished(int exitCode, QProcess::ExitStatus status); private: void startProcess(); void transitTo(State s); State m_state = Idle; QProcess *m_proc = nullptr; RestartPolicy m_policy; std::function m_guard; QString m_program; QStringList m_args; }; #endif // STREAMFSM_H