diff --git a/ChessSim/Components/CMakeLists.txt b/ChessSim/Components/CMakeLists.txt index 71acf80..176454f 100644 --- a/ChessSim/Components/CMakeLists.txt +++ b/ChessSim/Components/CMakeLists.txt @@ -2,3 +2,4 @@ # add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/MyComponent") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/CsvTM/") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/GsStatusStore/") diff --git a/ChessSim/Components/GsStatusStore/CMakeLists.txt b/ChessSim/Components/GsStatusStore/CMakeLists.txt new file mode 100644 index 0000000..b58d285 --- /dev/null +++ b/ChessSim/Components/GsStatusStore/CMakeLists.txt @@ -0,0 +1,6 @@ +register_fprime_library( + AUTOCODER_INPUTS + "${CMAKE_CURRENT_LIST_DIR}/GsStatusStore.fpp" + SOURCES + "${CMAKE_CURRENT_LIST_DIR}/GsStatusStore.cpp" +) \ No newline at end of file diff --git a/ChessSim/Components/GsStatusStore/GsStatusStore.cpp b/ChessSim/Components/GsStatusStore/GsStatusStore.cpp new file mode 100644 index 0000000..7ebe017 --- /dev/null +++ b/ChessSim/Components/GsStatusStore/GsStatusStore.cpp @@ -0,0 +1,37 @@ +#include "ChessSim/Components/GsStatusStore/GsStatusStore.hpp" + +namespace ChessSim { + +GsStatusStore::GsStatusStore(const char* const compName) + : GsStatusStoreComponentBase(compName) {} + +GsStatusStore::~GsStatusStore() {} + +void GsStatusStore::UPDATE_STATUS_cmdHandler( + const FwOpcodeType opCode, + const U32 cmdSeq, + const Fw::CmdStringArg& gs_status, + const Fw::CmdStringArg& backend_health, + const Fw::CmdStringArg& service_status_raw, + const Fw::CmdStringArg& downlink_status, + const bool pass_active, + const I32 time_to_aos_s, + const I32 time_to_los_s, + const F32 rssi_dbm, + const F32 snr_db +) { + this->tlmWrite_LatestGsStatus(gs_status); + this->tlmWrite_LatestBackendHealth(backend_health); + this->tlmWrite_LatestServiceStatusRaw(service_status_raw); + this->tlmWrite_LatestDownlinkStatus(downlink_status); + this->tlmWrite_LatestPassActive(pass_active); + this->tlmWrite_LatestTimeToAosS(time_to_aos_s); + this->tlmWrite_LatestTimeToLosS(time_to_los_s); + this->tlmWrite_LatestRssiDbm(rssi_dbm); + this->tlmWrite_LatestSnrDb(snr_db); + + this->log_ACTIVITY_HI_StatusUpdated(gs_status, backend_health, pass_active); + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); +} + +} // namespace ChessSim \ No newline at end of file diff --git a/ChessSim/Components/GsStatusStore/GsStatusStore.fpp b/ChessSim/Components/GsStatusStore/GsStatusStore.fpp new file mode 100644 index 0000000..a6fecfa --- /dev/null +++ b/ChessSim/Components/GsStatusStore/GsStatusStore.fpp @@ -0,0 +1,44 @@ +module ChessSim { + + active component GsStatusStore { + + command recv port cmdIn + command reg port cmdRegOut + command resp port cmdResponseOut + + event port logOut + text event port logTextOut + telemetry port tlmOut + time get port timeGetOut + + async command UPDATE_STATUS( + gs_status: string size 32, + backend_health: string size 32, + service_status_raw: string size 32, + downlink_status: string size 32, + pass_active: bool, + time_to_aos_s: I32, + time_to_los_s: I32, + rssi_dbm: F32, + snr_db: F32 + ) + + telemetry LatestGsStatus: string size 32 format "{}" + telemetry LatestBackendHealth: string size 32 format "{}" + telemetry LatestServiceStatusRaw: string size 32 format "{}" + telemetry LatestDownlinkStatus: string size 32 format "{}" + telemetry LatestPassActive: bool format "{}" + telemetry LatestTimeToAosS: I32 format "{}" + telemetry LatestTimeToLosS: I32 format "{}" + telemetry LatestRssiDbm: F32 format "{}" + telemetry LatestSnrDb: F32 format "{}" + + event StatusUpdated( + gs_status: string size 32, + backend_health: string size 32, + pass_active: bool + ) severity activity high format "GS status={}, backend={}, pass_active={}" + + } + +} \ No newline at end of file diff --git a/ChessSim/Components/GsStatusStore/GsStatusStore.hpp b/ChessSim/Components/GsStatusStore/GsStatusStore.hpp new file mode 100644 index 0000000..419b499 --- /dev/null +++ b/ChessSim/Components/GsStatusStore/GsStatusStore.hpp @@ -0,0 +1,31 @@ +#ifndef CHESSSIM_COMPONENTS_GSSTATUSSTORE_GSSTATUSSTORE_HPP +#define CHESSSIM_COMPONENTS_GSSTATUSSTORE_GSSTATUSSTORE_HPP + +#include "ChessSim/Components/GsStatusStore/GsStatusStoreComponentAc.hpp" + +namespace ChessSim { + +class GsStatusStore : public GsStatusStoreComponentBase { + public: + GsStatusStore(const char* const compName); + ~GsStatusStore() override; + + private: + void UPDATE_STATUS_cmdHandler( + const FwOpcodeType opCode, + const U32 cmdSeq, + const Fw::CmdStringArg& gs_status, + const Fw::CmdStringArg& backend_health, + const Fw::CmdStringArg& service_status_raw, + const Fw::CmdStringArg& downlink_status, + const bool pass_active, + const I32 time_to_aos_s, + const I32 time_to_los_s, + const F32 rssi_dbm, + const F32 snr_db + ) override; +}; + +} // namespace ChessSim + +#endif \ No newline at end of file diff --git a/DeploymentSim/Top/instances.fpp b/DeploymentSim/Top/instances.fpp index b979cf0..4daec5d 100644 --- a/DeploymentSim/Top/instances.fpp +++ b/DeploymentSim/Top/instances.fpp @@ -51,6 +51,12 @@ module DeploymentSim { stack size Default.STACK_SIZE \ priority 100 + + instance gsStatusStore: ChessSim.GsStatusStore base id 0x10005000 \ + queue size Default.QUEUE_SIZE \ + stack size Default.STACK_SIZE \ + priority 98 + # ---------------------------------------------------------------------- # Queued component instances # ---------------------------------------------------------------------- diff --git a/DeploymentSim/Top/topology.fpp b/DeploymentSim/Top/topology.fpp index 10a1b13..a910762 100644 --- a/DeploymentSim/Top/topology.fpp +++ b/DeploymentSim/Top/topology.fpp @@ -34,6 +34,11 @@ module DeploymentSim { instance cmdSeq instance csvTM + # ---------------------------------------------------------------------- + # Ground Software - State + # ---------------------------------------------------------------------- + instance gsStatusStore + # ---------------------------------------------------------------------- # Pattern graph specifiers # ---------------------------------------------------------------------- @@ -134,6 +139,20 @@ module DeploymentSim { } + # connections GsStatusStore_CdhCore { + # CdhCore.cmdDisp.compCmdSend[0] -> gsStatusStore.cmdIn + # gsStatusStore.cmdRegOut -> CdhCore.cmdDisp.compCmdReg[0] + #gsStatusStore.cmdResponseOut -> CdhCore.cmdDisp.compCmdStat[0] +# + # gsStatusStore.logOut -> CdhCore.events.LogRecv + # #gsStatusStore.logTextOut -> CdhCore.textLogger.TextRecv + # gsStatusStore.tlmOut -> CdhCore.tlmSend.TlmRecv + # gsStatusStore.timeGetOut -> chronoTime.timeGet + #} + + + + } } diff --git a/README.md b/README.md index bf84678..4819eda 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ This installs: * F′ core tools, * the F′ GDS, * and all required Python dependencies. +* Fast API to run the command interface No additional packages are needed. @@ -108,6 +109,8 @@ Open that address in a browser. You should see: * telemetry updating once per second from `sim_data.csv`, * the `DeploymentSim` executable running automatically. +If you want the custom Dashboard view, open the `Dashboard` tab and upload `dashboard.xml` from the repository root. + --- ## 7. Stopping and restarting diff --git a/__pycache__/command_reciever.cpython-312.pyc b/__pycache__/command_reciever.cpython-312.pyc new file mode 100644 index 0000000..48a01ae Binary files /dev/null and b/__pycache__/command_reciever.cpython-312.pyc differ diff --git a/command_reciever.py b/command_reciever.py new file mode 100644 index 0000000..b0ef66e --- /dev/null +++ b/command_reciever.py @@ -0,0 +1,72 @@ +# comand_reciever.py +from pathlib import Path +import shlex +import subprocess + +from fastapi import FastAPI, HTTPException +from pydantic import BaseModel, Field + + +app = FastAPI(title="Command Receiver") + + +ROOT = Path(__file__).resolve().parent +DICT_PATH = ROOT / "build-artifacts" / "Linux" / "DeploymentSim" / "dict" / "DeploymentSimTopologyDictionary.json" + + +class CommandRequest(BaseModel): + command: str + arguments: list[str] = Field(default_factory=list) + + +@app.get("/health") +def health(): + return {"ok": True} + + +@app.post("/command-send") +def command_send(req: CommandRequest): + if not DICT_PATH.exists(): + raise HTTPException(status_code=500, detail=f"Dictionary not found: {DICT_PATH}") + + cmd = [ + "fprime-cli", + "command-send", + "--tts-addr", + "127.0.0.1", + "--tts-port", + "50050", + "--dictionary", + str(DICT_PATH), + req.command, + "--arguments", + *[str(arg) for arg in req.arguments], + ] + + print("[FPRIME CLI CMD]", " ".join(shlex.quote(x) for x in cmd)) + + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=False, + cwd=ROOT, + ) + + if result.stdout: + print("[STDOUT]", result.stdout.strip()) + if result.stderr: + print("[STDERR]", result.stderr.strip()) + + if result.returncode != 0: + raise HTTPException( + status_code=500, + detail=result.stderr.strip() or result.stdout.strip() or f"fprime-cli failed with code {result.returncode}", + ) + + return { + "ok": True, + "returncode": result.returncode, + "stdout": result.stdout, + "stderr": result.stderr, + } \ No newline at end of file diff --git a/dashboard.xml b/dashboard.xml new file mode 100644 index 0000000..5b75472 --- /dev/null +++ b/dashboard.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index bdf85ba..465aa02 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,6 @@ # Local requirements: -r ./lib/fprime/requirements.txt + +fastapi==0.135.2 +uvicorn==0.42.0 diff --git a/run_local_stack.sh b/run_local_stack.sh new file mode 100644 index 0000000..c7532d0 --- /dev/null +++ b/run_local_stack.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -e + +source .venv/bin/activate + +uvicorn command_reciever:app --host 0.0.0.0 --port 8091 & +API_PID=$! + +cleanup() { + kill $API_PID 2>/dev/null || true +} +trap cleanup EXIT + +cd DeploymentSim +fprime-gds -n --dictionary ../build-artifacts/Linux/DeploymentSim/dict/DeploymentSimTopologyDictionary.json \ No newline at end of file