diff --git a/.gitignore b/.gitignore index 1260e6ac..b95e7d15 100644 --- a/.gitignore +++ b/.gitignore @@ -45,12 +45,17 @@ Gemfile.lock persistent/.bash_history # For fprime lib -cubics-obc-test/fprime/** +cubics-obc-test/fprime** # For Fprime project builds and auto generated code cubics-obc-test/build** cubics-obc-test/venv/** -# For individual component builds and auto generated code +# For auto generated code cubics-obc-test/BurnwireTester/build** cubics-obc-test/BurnwireTester/logs/** + +cubics-obc-test/SimulationDeployment/build** +cubics-obc-test/SimulationDeployment/logs/** + + diff --git a/cubics-obc-test/CMakeLists.txt b/cubics-obc-test/CMakeLists.txt index e4a8bdd3..94a2d4aa 100644 --- a/cubics-obc-test/CMakeLists.txt +++ b/cubics-obc-test/CMakeLists.txt @@ -12,6 +12,12 @@ include("${CMAKE_CURRENT_LIST_DIR}/fprime/cmake/FPrime.cmake") # NOTE: register custom targets between these two lines include("${FPRIME_FRAMEWORK_PATH}/cmake/FPrime-Code.cmake") - # This includes project-wide objects include("${CMAKE_CURRENT_LIST_DIR}/project.cmake") + + +### Manually added CMAKE stuff: + +# Enables debug symbols in compilation +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") + diff --git a/cubics-obc-test/Components/Burnwire/Burnwire.cpp b/cubics-obc-test/Components/Burnwire/Burnwire.cpp index 27c29077..540a2c22 100644 --- a/cubics-obc-test/Components/Burnwire/Burnwire.cpp +++ b/cubics-obc-test/Components/Burnwire/Burnwire.cpp @@ -4,107 +4,89 @@ // \brief cpp file for Burnwire component implementation class // ====================================================================== - #include #include #include namespace Components { - // ---------------------------------------------------------------------- - // Construction, initialization, and destruction - // ---------------------------------------------------------------------- - - Burnwire :: - Burnwire( - const char *const compName - ) : BurnwireComponentBase(compName) - { +// ---------------------------------------------------------------------- +// Construction, initialization, and destruction +// ---------------------------------------------------------------------- - } +Burnwire ::Burnwire(const char *const compName) + : BurnwireComponentBase(compName) {} - Burnwire :: - ~Burnwire() - { +Burnwire ::~Burnwire() {} - } - - // ---------------------------------------------------------------------- - // Handler implementations for user-defined typed input ports - // ---------------------------------------------------------------------- +// ---------------------------------------------------------------------- +// Handler implementations for user-defined typed input ports +// ---------------------------------------------------------------------- /* - * When ACTIVATE_BURNWIRE_cmdHandler() toggles the burnwire state to Fw::On::ON, and - * sets the counter value is to zero, this function toggles the burnwire GPIO high. - * This function will report via telemetry and log the burnwire state, and increment - * a counter once per second. Once the counter value exceeds BURNWIRE_TIME_SECONDS, - * the state is set of OFF. The counter value is not reset here in case the state is - * somehow set to ON erronesouly, then the function will simply set the state to back - * to OFF. - * - * A 1Hz rate group timer output must be attached to the input port of this component. - * This is done within the topology.fpp file of the deployment using it. + * When ACTIVATE_BURNWIRE_cmdHandler() toggles the burnwire state to + * Fw::On::ON, and sets the counter value is to zero, this function toggles the + * burnwire GPIO high. This function will report via telemetry and log the + * burnwire state, and increment a counter once per second. Once the counter + * value exceeds BURNWIRE_TIME_SECONDS, the state is set of OFF. The counter + * value is not reset here in case the state is somehow set to ON erronesouly, + * then the function will simply set the state to back to OFF. + * + * A 1Hz rate group timer output must be attached to the input port of this + * component. This is done within the topology.fpp file of the deployment using + * it. */ - void Burnwire :: - run_handler( - const NATIVE_INT_TYPE portNum, - NATIVE_UINT_TYPE context - ) - { - if(state == Fw::On::ON){ - if(count >= BURNWIRE_TIME_SECONDS){ - //Turn off gpio - //reset state to off - // Port may not be connected, so check before sending output - // if (this->isConnected_gpioSet_OutputPort(0)) - // { - // this->gpioSet_out(0, Fw::Logic::LOW); - state = Fw::On::OFF; - this->tlmWrite_BurnwireState(state); - this->log_ACTIVITY_HI_SetBurnwireState(state); - // } - } - else if(count == 0){ - //turn on gpio - // if (this->isConnected_gpioSet_OutputPort(0)) - // { - // this->gpioSet_out(0, Fw::Logic::HIGH); - this->tlmWrite_BurnwireState(state); - this->log_ACTIVITY_HI_SetBurnwireState(state); - count = count + 1; - // } - } - else{ - count = count+1; - this->tlmWrite_BurnwireCounter(count); - } +void Burnwire ::run_handler(const NATIVE_INT_TYPE portNum, + NATIVE_UINT_TYPE context) { + if (state == Fw::On::ON) { + if (count >= BURNWIRE_TIME_SECONDS) { + // Turn off gpio + // reset state to off + // Port may not be connected, so check before sending output + // if (this->isConnected_gpioSet_OutputPort(0)) + // { + // this->gpioSet_out(0, Fw::Logic::LOW); + state = Fw::On::OFF; + this->tlmWrite_BurnwireState(state); + this->log_ACTIVITY_HI_SetBurnwireState(state); + // } + } else if (count == 0) { + // turn on gpio + // if (this->isConnected_gpioSet_OutputPort(0)) + // { + // this->gpioSet_out(0, Fw::Logic::HIGH); + this->tlmWrite_BurnwireState(state); + this->log_ACTIVITY_HI_SetBurnwireState(state); + count = count + 1; + // } + } else { + count = count + 1; + this->tlmWrite_BurnwireCounter(count); + } } - } +} - // ---------------------------------------------------------------------- - // Command handler implementations - // ---------------------------------------------------------------------- +// ---------------------------------------------------------------------- +// Command handler implementations +// ---------------------------------------------------------------------- /* - * Sets the burnwire state to ON and resets the counter to zero. This will allow the - * run_handler to begin. + * Sets the burnwire state to ON and resets the counter to zero. This will + * allow the run_handler to begin. */ - void Burnwire :: - ACTIVATE_BURNWIRE_cmdHandler( - const FwOpcodeType opCode, - const U32 cmdSeq - ) - { +void Burnwire ::ACTIVATE_BURNWIRE_cmdHandler(const FwOpcodeType opCode, + const U32 cmdSeq) { // Create a variable to represent the command response auto cmdResp = Fw::CmdResponse::OK; state = Fw::On::ON; - count = 0; + count = 0; this->tlmWrite_BurnwireCounter(count); - - //run_handler function uses a scheduled input port to receive calls from rate group. If the burnwire state is 'off', ignore them. - this->cmdResponse_out(opCode,cmdSeq,Fw::CmdResponse::OK); - } + // run_handler function uses a scheduled input port to receive calls from + // rate group. If the burnwire state is 'off', ignore them. + + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); +} } // end namespace Components diff --git a/cubics-obc-test/Components/Burnwire/Burnwire.hpp b/cubics-obc-test/Components/Burnwire/Burnwire.hpp index b46101a9..ff03530c 100644 --- a/cubics-obc-test/Components/Burnwire/Burnwire.hpp +++ b/cubics-obc-test/Components/Burnwire/Burnwire.hpp @@ -14,52 +14,46 @@ namespace Components { - class Burnwire : - public BurnwireComponentBase - { - - public: - - // ---------------------------------------------------------------------- - // Construction, initialization, and destruction - // ---------------------------------------------------------------------- - - //! Construct object Burnwire - //! - Burnwire( - const char *const compName /*!< The component name*/ - ); - - //! Destroy object Burnwire - //! - ~Burnwire(); - - PRIVATE: - - // Component state tracking variables - Fw::On state; //Tracks state of whether or not burnwire is on or off. - U32 count; //Incremented over a 1Hz rate group, until a certain time has passed - - // ---------------------------------------------------------------------- - // Command handler implementations - // ---------------------------------------------------------------------- - - //! Handler implementation for run - //! - void run_handler( - const NATIVE_INT_TYPE portNum, /*!< The port number*/ - NATIVE_UINT_TYPE context /*!< The call order*/ - ); - - //! Implementation for ACTIVATE_BURNWIRE command handler - //! Comand to activate the burnwire - void ACTIVATE_BURNWIRE_cmdHandler( - const FwOpcodeType opCode, /*!< The opcode*/ - const U32 cmdSeq /*!< The command sequence number*/ - ); - - - }; +class Burnwire : public BurnwireComponentBase { + + public: + // ---------------------------------------------------------------------- + // Construction, initialization, and destruction + // ---------------------------------------------------------------------- + + //! Construct object Burnwire + //! + Burnwire(const char *const compName /*!< The component name*/ + ); + + //! Destroy object Burnwire + //! + ~Burnwire(); + + PRIVATE : + + // Component state tracking variables + Fw::On state; // Tracks state of whether or not burnwire is on or off. + U32 count; // Incremented over a 1Hz rate group, until a certain time has + // passed + + // ---------------------------------------------------------------------- + // Command handler implementations + // ---------------------------------------------------------------------- + + //! Handler implementation for run + //! + void run_handler(const NATIVE_INT_TYPE portNum, /*!< The port number*/ + NATIVE_UINT_TYPE context /*!< The call order*/ + ); + + //! Implementation for ACTIVATE_BURNWIRE command handler + //! Comand to activate the burnwire + void ACTIVATE_BURNWIRE_cmdHandler( + const FwOpcodeType opCode, /*!< The opcode*/ + const U32 cmdSeq /*!< The command sequence number*/ + ); +}; } // end namespace Components diff --git a/cubics-obc-test/Components/DeployablesInterface/CMakeLists.txt b/cubics-obc-test/Components/DeployablesInterface/CMakeLists.txt new file mode 100644 index 00000000..9886391d --- /dev/null +++ b/cubics-obc-test/Components/DeployablesInterface/CMakeLists.txt @@ -0,0 +1,21 @@ +#### +# F prime CMakeLists.txt: +# +# SOURCE_FILES: combined list of source and autocoding files +# MOD_DEPS: (optional) module dependencies +# UT_SOURCE_FILES: list of source files for unit tests +# +#### +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/DeployablesInterface.fpp" + "${CMAKE_CURRENT_LIST_DIR}/DeployablesInterface.cpp" +) + +# Uncomment and add any modules that this component depends on, else +# they might not be available when cmake tries to build this component. + +# set(MOD_DEPS +# Add your dependencies here +# ) + +register_fprime_module() diff --git a/cubics-obc-test/Components/DeployablesInterface/DeployablesInterface.cpp b/cubics-obc-test/Components/DeployablesInterface/DeployablesInterface.cpp new file mode 100644 index 00000000..2ab03cf5 --- /dev/null +++ b/cubics-obc-test/Components/DeployablesInterface/DeployablesInterface.cpp @@ -0,0 +1,155 @@ +// ====================================================================== +// \title DeployablesInterface.cpp +// \author fprime-dev +// \brief cpp file for DeployablesInterface component implementation class +// ====================================================================== + +#include +#include + +#include // For non blocking port reading with 'select' fxn +#include +#include //For seperate thread to read port constantly +#include +#include + +namespace Components { + +// ---------------------------------------------------------------------- +// Construction, initialization, and destruction +// ---------------------------------------------------------------------- + +DeployablesInterface ::DeployablesInterface(const char *const compName) + : DeployablesInterfaceComponentBase(compName) { + openSocket(); +} + +DeployablesInterface ::~DeployablesInterface() { closeSocket(); } + +int DeployablesInterface ::openSocket() { + struct sockaddr_in servaddr; + + client_fd = socket(AF_INET, SOCK_STREAM, 0); + if (client_fd == -1) { + perror("Socket creation for Deployables Interface failed"); + return 1; + } + printf("Socket for Deployables Interface successfully created\n"); + + memset(&servaddr, 0, sizeof(servaddr)); + + servaddr.sin_family = AF_INET; + servaddr.sin_port = htons(TCP_PORT); + servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); + + int flags = fcntl(client_fd, F_GETFL, 0); + fcntl(client_fd, F_SETFL, flags | O_NONBLOCK); + + if (connect(client_fd, (SA *)&servaddr, sizeof(servaddr)) < 0) { + if (errno == EINPROGRESS) { + fd_set set; + struct timeval timeout; + FD_ZERO(&set); + FD_SET(client_fd, &set); + timeout.tv_sec = THREAD_READ_TIMEOUT_SECONDS; + timeout.tv_usec = 0; + + int result = select(client_fd + 1, NULL, &set, NULL, &timeout); + if (result < 0 && errno != EINTR) { + perror( + "Error during connection to Deployables Interface socket"); + return 1; + } else if (result > 0) { + int so_error; + socklen_t len = sizeof so_error; + + getsockopt(client_fd, SOL_SOCKET, SO_ERROR, &so_error, &len); + + if (so_error == 0) { + printf("Successfully connected to Deployables Interface " + "socket\n"); + } else { + // Connection failed + errno = so_error; + perror("Error during connection to Deployables Interface "); + return 1; + } + } else { + fprintf(stderr, "Timeout or error during Deployables Interface " + "connection\n"); + return 1; + } + } else { + perror("Connection to Deployables Interface socket failed"); + return 1; + } + } + + // Create a thread to handle socket reading + std::thread socketReadThread(&DeployablesInterface::handleSocketRead, this, + client_fd); + socketReadThread.detach(); // Detach the thread so it runs independently + + return 0; +} + +// Runs in an independent thread, handles reading from the socket with the +// select function +void DeployablesInterface ::handleSocketRead(int client_fd) { + char buffer[MAX_BUFFER_CHARS]; + fd_set set; + struct timeval timeout; + + while (1) { + FD_ZERO(&set); + FD_SET(client_fd, &set); + timeout.tv_sec = 10; + timeout.tv_usec = 0; + + int activity = select(client_fd + 1, &set, NULL, NULL, &timeout); + + if ((activity < 0) && (errno != EINTR)) { + perror("Select error"); + } + + // If there is data to be read from this socket, read it and send it to + // DeployablesService component + if (FD_ISSET(client_fd, &set)) { + memset(buffer, 0, sizeof(buffer)); + read(client_fd, buffer, sizeof(buffer)); + // fprintf(stderr, "From server: %s\n", buffer); + this->log_ACTIVITY_LO_receivedDeployableData(buffer); + this->deployableDataIncoming_out(0, buffer); + } + } +} + +void DeployablesInterface ::closeSocket() { + close(client_fd); + // fprintf(stderr, "Socket closed \n"); + this->log_ACTIVITY_LO_socketClosed(); +} + +// ---------------------------------------------------------------------- +// Handler implementations for user-defined typed input ports +// ---------------------------------------------------------------------- + +// Put the received data into the client TCP socket to be sent to the +// deployables component +void DeployablesInterface ::deployableDataOutgoing_handler( + const NATIVE_INT_TYPE portNum, const aString &a) { + char outgoingData[MAX_BUFFER_CHARS] = {0}; + strcpy(outgoingData, a.toChar()); + + ssize_t len = send(client_fd, outgoingData, strlen(outgoingData), 0); + if (len > 0) { + // fprintf(stderr, "Socket send success \n"); + this->log_ACTIVITY_LO_sentDeployableData(outgoingData); + } else { + // fprintf(stderr, "ERROR: Socket send failure: %s \n", + // strerror(errno)); + this->log_WARNING_HI_socketSendFailure("Socket send failure"); + } +} + +} // end namespace Components diff --git a/cubics-obc-test/Components/DeployablesInterface/DeployablesInterface.fpp b/cubics-obc-test/Components/DeployablesInterface/DeployablesInterface.fpp new file mode 100644 index 00000000..5c621168 --- /dev/null +++ b/cubics-obc-test/Components/DeployablesInterface/DeployablesInterface.fpp @@ -0,0 +1,101 @@ +module Components { + + @ A port for carrying a string + port StringValue(a: string) + + @ Interface for deployable items (gpios for burnwires and switches) on SC + passive component DeployablesInterface { + + # ---------------------------------------------------------------------- + # Ports + # ---------------------------------------------------------------------- + + + @ Port for sending data to the deployable (toggling a burnwire) + sync input port deployableDataOutgoing: StringValue + + @ Port for sending data from the deployable (switch status) to an Fprime component + output port deployableDataIncoming: StringValue + + # ---------------------------------------------------------------------- + # Commands + # ---------------------------------------------------------------------- + # Shouldn't issue any commands is it is just an interface for sending and receiving data + + # ---------------------------------------------------------------------- + # Events + # ---------------------------------------------------------------------- + @ Log recevied data over event - log of system activity + event receivedDeployableData(receivedDeployableData: string) \ + severity activity low \ + format "Received Deployable Data: {}" + + @ Log send data over event - log of system activity + event sentDeployableData(sentDeployableData: string) \ + severity activity low \ + format "Sent Deployable Data: {}" + + @ Log socket open success + event socketOpenSuccess() \ + severity activity low \ + format "Deployable interface socket open success" + + @ Log socket open failure + event socketOpenFailure(failureMessage: string) \ + severity warning high \ + format "Deployable interface socket open failure: {}" + + @ Log socket send failure + event socketSendFailure(failureMessage: string) \ + severity warning high \ + format "Deployable interface socket send failure: {}" + + @ Log socket read failure + event socketReadFailure(failureMessage: string) \ + severity warning high \ + format "Deployable interface socket read failure: {}" + + @ Log socket closed + event socketClosed() \ + severity activity low \ + format "Deployable interface socket closed" + + + # ---------------------------------------------------------------------- + # Parameters + # ---------------------------------------------------------------------- + param TCPPortNumber: U32 default 1811 + + + ############################################################################### + # Standard AC Ports: Required for Channels, Events, Commands, and Parameters # + ############################################################################### + @ Port for requesting the current time + time get port timeCaller + + @ Port for sending command registrations + command reg port cmdRegOut + + @ Port for receiving commands + command recv port cmdIn + + @ Port for sending command responses + command resp port cmdResponseOut + + @ Port for sending textual representation of events + text event port logTextOut + + @ Port for sending events to downlink + event port logOut + + @ Port for sending telemetry channels to downlink + telemetry port tlmOut + + @ Port to return the value of a parameter + param get port prmGetOut + + @Port to set the value of a parameter + param set port prmSetOut + + } +} \ No newline at end of file diff --git a/cubics-obc-test/Components/DeployablesInterface/DeployablesInterface.hpp b/cubics-obc-test/Components/DeployablesInterface/DeployablesInterface.hpp new file mode 100644 index 00000000..3c4619e7 --- /dev/null +++ b/cubics-obc-test/Components/DeployablesInterface/DeployablesInterface.hpp @@ -0,0 +1,68 @@ +// ====================================================================== +// \title DeployablesInterface.hpp +// \author fprime-dev +// \brief hpp file for DeployablesInterface component implementation class +// ====================================================================== + +#ifndef DeployablesInterface_HPP +#define DeployablesInterface_HPP + +#define LOCAL_HOST "127.0.0.1" +#define TCP_PORT 1811 +#define MAX_BUFFER_CHARS 1024 +#define THREAD_READ_TIMEOUT_SECONDS 10 + +#define SA struct sockaddr + +#include +#include //For socket connection (TCP) +#include +#include +#include +#include + +#include "Components/DeployablesInterface/DeployablesInterfaceComponentAc.hpp" + +namespace Components { + +class DeployablesInterface : public DeployablesInterfaceComponentBase { + + public: + // ---------------------------------------------------------------------- + // Construction, initialization, and destruction + // ---------------------------------------------------------------------- + + //! Construct object DeployablesInterface + //! + DeployablesInterface(const char *const compName /*!< The component name*/ + ); + + //! Destroy object DeployablesInterface + //! + ~DeployablesInterface(); + + PRIVATE : + + int status; + int client_fd; + struct sockaddr_in serv_addr; + char incomingData[MAX_BUFFER_CHARS] = {0}; + + int openSocket(); + void closeSocket(); + void handleSocketRead(int sockfd); + + // ---------------------------------------------------------------------- + // Handler implementations for user-defined typed input ports + // ---------------------------------------------------------------------- + + //! Handler implementation for deployableDataOutgoing + //! + void deployableDataOutgoing_handler( + const NATIVE_INT_TYPE portNum, /*!< The port number*/ + const aString &a); +}; + +} // end namespace Components + +#endif diff --git a/cubics-obc-test/Components/DeployablesInterface/docs/sdd.md b/cubics-obc-test/Components/DeployablesInterface/docs/sdd.md new file mode 100644 index 00000000..63bd8613 --- /dev/null +++ b/cubics-obc-test/Components/DeployablesInterface/docs/sdd.md @@ -0,0 +1,66 @@ +# Components::DeployablesInterface + +Interface for deployable items (burnwires) on SC + +## Usage Examples +Add usage examples here + +### Diagrams +Add diagrams here + +### Typical Usage +And the typical usage of the component here + +## Class Diagram +Add a class diagram here + +## Port Descriptions +| Name | Description | +|---|---| +|---|---| + +## Component States +Add component states in the chart below +| Name | Description | +|---|---| +|---|---| + +## Sequence Diagrams +Add sequence diagrams here + +## Parameters +| Name | Description | +|---|---| +|---|---| + +## Commands +| Name | Description | +|---|---| +|---|---| + +## Events +| Name | Description | +|---|---| +|---|---| + +## Telemetry +| Name | Description | +|---|---| +|---|---| + +## Unit Tests +Add unit test descriptions in the chart below +| Name | Description | Output | Coverage | +|---|---|---|---| +|---|---|---|---| + +## Requirements +Add requirements in the chart below +| Name | Description | Validation | +|---|---|---| +|---|---|---| + +## Change Log +| Date | Description | +|---|---| +|---| Initial Draft | \ No newline at end of file diff --git a/cubics-obc-test/Components/DeployablesService/CMakeLists.txt b/cubics-obc-test/Components/DeployablesService/CMakeLists.txt new file mode 100644 index 00000000..e79c7a6c --- /dev/null +++ b/cubics-obc-test/Components/DeployablesService/CMakeLists.txt @@ -0,0 +1,21 @@ +#### +# F prime CMakeLists.txt: +# +# SOURCE_FILES: combined list of source and autocoding files +# MOD_DEPS: (optional) module dependencies +# UT_SOURCE_FILES: list of source files for unit tests +# +#### +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/DeployablesService.fpp" + "${CMAKE_CURRENT_LIST_DIR}/DeployablesService.cpp" +) + +# Uncomment and add any modules that this component depends on, else +# they might not be available when cmake tries to build this component. + +# set(MOD_DEPS +# Add your dependencies here +# ) + +register_fprime_module() diff --git a/cubics-obc-test/Components/DeployablesService/DeployablesService.cpp b/cubics-obc-test/Components/DeployablesService/DeployablesService.cpp new file mode 100644 index 00000000..14691104 --- /dev/null +++ b/cubics-obc-test/Components/DeployablesService/DeployablesService.cpp @@ -0,0 +1,176 @@ +// ====================================================================== +// \title DeployablesService.cpp +// \author fprime-dev +// \brief cpp file for DeployablesService component implementation class +// ====================================================================== + +#include +#include + +#include +#include + +namespace Components { + +// ---------------------------------------------------------------------- +// Construction, initialization, and destruction +// ---------------------------------------------------------------------- + +DeployablesService ::DeployablesService(const char *const compName) + : DeployablesServiceComponentBase(compName) { + // Initialize deployables map with all deployable items set to stowed + // TODO - this will have to fetch the deployables deployment state from some + // sort of non volatile memory + deployablesMap["DFGM"] = {DeployableStatus::STOWED, 0}; + deployablesMap["UHF_P"] = {DeployableStatus::STOWED, 0}; + deployablesMap["UHF_Z"] = {DeployableStatus::STOWED, 0}; + deployablesMap["UHF_N"] = {DeployableStatus::STOWED, 0}; + deployablesMap["UHF_S"] = {DeployableStatus::STOWED, 0}; + deployablesMap["SOLAR_S"] = {DeployableStatus::STOWED, 0}; + deployablesMap["SOLAR_P"] = {DeployableStatus::STOWED, 0}; + + LEOPDeploymentSequence = {"DFGM", "UHF_P", "UHF_Z", "UHF_N", + "UHF_S", "SOLAR_S", "SOLAR_P"}; +} + +DeployablesService ::~DeployablesService() {} + +std::string DeployablesService ::getStatusString(DeployableStatus status) { + switch (status) { + case DeployableStatus::STOWED: + return "STOWED"; + case DeployableStatus::PENDING: + return "PENDING"; + case DeployableStatus::DEPLOYED: + return "DEPLOYED"; + case DeployableStatus::FAILED_ALL_ATTEMPTS: + return "FAILED_ALL_ATTEMPTS"; + default: + return "UNKNOWN"; + } +} + +void DeployablesService ::printAllDeployablesStatus() { + std::cout << "---------- Deployables Status ----------" << std::endl; + for (const std::string &deployableName : LEOPDeploymentSequence) { + const DeployableInfo &deployableInfo = deployablesMap[deployableName]; + std::cout << "Deployable: " << deployableName + << ", Status: " << getStatusString(deployableInfo.status) + << ", Attempts: " << deployableInfo.numAttempts << std::endl; + } + std::cout << "----------------------------------------" << std::endl; +} + +void DeployablesService ::updateDeployableStatus( + const std::string &deployableName, DeployableStatus status) { + deployablesMap[deployableName.c_str()].status = status; + + if (status == DeployableStatus::DEPLOYED) { + this->log_ACTIVITY_LO_deployableDeployed(deployableName.c_str()); + } else if (status == DeployableStatus::STOWED) { + this->log_ACTIVITY_LO_deployableStowed(deployableName.c_str()); + } else if (status == DeployableStatus::PENDING) { + this->log_ACTIVITY_LO_deployablePending(deployableName.c_str()); + } +} + +void DeployablesService ::updateDeployableAttempts( + const std::string &deployableName, int numAttempts) { + deployablesMap[deployableName.c_str()].numAttempts = numAttempts; +} + +void DeployablesService ::attemptDeployment(const std::string &deployableName) { + DeployableInfo &deployableInfo = deployablesMap[deployableName.c_str()]; + + // Build string to send to deployables interface + std::string burnwireSetString = "burnwire_set:" + deployableName + ":1"; + + // Send command to toggle target component burnwire HIGH + this->deployableToggleBurnwirePort_out(0, burnwireSetString.c_str()); + + while (deployableInfo.numAttempts < NUMBER_OF_DEPLOYMENT_ATTEMPTS && + deployableInfo.status != DeployableStatus::DEPLOYED) { + deployableInfo = deployablesMap[deployableName.c_str()]; + + // Wait for deployable burnwire to physically burn + sleep(BURNWIRE_WAIT_TIME_SECONDS); + + // Build string to send query for target components feedback switch + // status + std::string switchStatusRequestString = + "switch_request:" + deployableName; + + // Send command to query target component feedback switch status + this->deployableToggleBurnwirePort_out( + 0, switchStatusRequestString.c_str()); + + // Wait for response to come in from deployables interface + sleep(SWITCH_QUERY_RESPONSE_WAIT_TIME_SECONDS); + + updateDeployableAttempts(deployableName, + deployableInfo.numAttempts + 1); + + // If N attempts fail, then this component is skipped + if (deployableInfo.numAttempts >= NUMBER_OF_DEPLOYMENT_ATTEMPTS) { + updateDeployableStatus(deployableName, + DeployableStatus::FAILED_ALL_ATTEMPTS); + } + + printAllDeployablesStatus(); + } + + // Set burnwire LOW + burnwireSetString = "burnwire_set:" + deployableName + ":0"; + + this->deployableToggleBurnwirePort_out(0, burnwireSetString.c_str()); +} + +// ---------------------------------------------------------------------- +// Handler implementations for user-defined typed input ports +// ---------------------------------------------------------------------- + +// This handler is called when a deployable feedback switch status is received +// from the deployables interface (via async input port) +// TODO - error handling and checking response string +void DeployablesService ::deployableFeedbackSwitchPort_handler( + const NATIVE_INT_TYPE portNum, const aString &a) { + // Convert the response string to std::string for easier manipulation + std::string responseString = a.toChar(); + + // Parse the response string to get the deployable name and feedback switch + // status + std::string delimiter = ":"; + std::string deployableName = + responseString.substr(0, responseString.find(delimiter)); + + // Ignore the string if the first token is not a deployable name + if (deployablesMap.find(deployableName.c_str()) == deployablesMap.end()) { + return; + } + std::string feedbackSwitchStatus = responseString.substr( + responseString.find(delimiter) + 1, responseString.length()); + + if (feedbackSwitchStatus == "1") { + updateDeployableStatus(deployableName, DeployableStatus::DEPLOYED); + } else { + updateDeployableStatus(deployableName, DeployableStatus::STOWED); + } +} + +// ---------------------------------------------------------------------- +// Command handler implementations +// ---------------------------------------------------------------------- + +void DeployablesService ::ActivateDeploymentSequence_cmdHandler( + const FwOpcodeType opCode, const U32 cmdSeq) { + this->log_ACTIVITY_HI_deploymentSequenceStarted(); + // Loop through all items in deployables map and attempt to deploy them + for (const std::string &deployableName : LEOPDeploymentSequence) { + attemptDeployment(deployableName); + } + this->log_ACTIVITY_HI_deploymentSequenceEnded(); + + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); +} + +} // end namespace Components diff --git a/cubics-obc-test/Components/DeployablesService/DeployablesService.fpp b/cubics-obc-test/Components/DeployablesService/DeployablesService.fpp new file mode 100644 index 00000000..60bdbcdd --- /dev/null +++ b/cubics-obc-test/Components/DeployablesService/DeployablesService.fpp @@ -0,0 +1,100 @@ +module Components { + + @ Service specifically for deployables subsystem + active component DeployablesService { + + # ---------------------------------------------------------------------- + # Ports + # ---------------------------------------------------------------------- + @ Port for receiving status from feedback switch query + sync input port deployableFeedbackSwitchPort: StringValue + + @ Port for toggling a burnwire + output port deployableToggleBurnwirePort: StringValue + + # ---------------------------------------------------------------------- + # Commands + # ---------------------------------------------------------------------- + async command ActivateDeploymentSequence + + # ---------------------------------------------------------------------- + # Events + # ---------------------------------------------------------------------- + @ Log recevied feedback switch status + event receivedFeedbackSwitchStatus(target_component: string, switch_state: string) \ + severity activity low \ + format "Deployable {} feedback switch status is: {}" + + @ Log when a burnwire is toggled + event burnwireToggled(target_component: string, toggle_state: string) \ + severity activity low \ + format "Deployable {} burnwire toggled to: {}" + + @ Log deployable successfully deployed (feedback switch reading is high) + event deployableDeployed(target_component: string) \ + severity activity low \ + format "Deployable: {} deployment successful!" + + @ Log deployable deployment is pending (burnwire set but awaiting switch reading) + event deployablePending(target_component: string) \ + severity activity low \ + format "Deployable: {} deployment is pending..." + + @ Log deployable is stowed (feedback switch reading is low) + event deployableStowed(target_component: string) \ + severity activity low \ + format "Deployable: {} is stowed [not deployed]" + + + @ Log deployment sequence started + event deploymentSequenceStarted () \ + severity activity high \ + format "Deployment sequence started" + + @ Log deployment sequence ended + event deploymentSequenceEnded() \ + severity activity high \ + format "Deployment sequence ended" + + + + + + + # ---------------------------------------------------------------------- + # Parameters + # ---------------------------------------------------------------------- + + + ############################################################################### + # Standard AC Ports: Required for Channels, Events, Commands, and Parameters # + ############################################################################### + @ Port for requesting the current time + time get port timeCaller + + @ Port for sending command registrations + command reg port cmdRegOut + + @ Port for receiving commands + command recv port cmdIn + + @ Port for sending command responses + command resp port cmdResponseOut + + @ Port for sending textual representation of events + text event port logTextOut + + @ Port for sending events to downlink + event port logOut + + @ Port for sending telemetry channels to downlink + telemetry port tlmOut + + @ Port to return the value of a parameter + param get port prmGetOut + + @Port to set the value of a parameter + param set port prmSetOut + + } +} \ No newline at end of file diff --git a/cubics-obc-test/Components/DeployablesService/DeployablesService.hpp b/cubics-obc-test/Components/DeployablesService/DeployablesService.hpp new file mode 100644 index 00000000..6c646242 --- /dev/null +++ b/cubics-obc-test/Components/DeployablesService/DeployablesService.hpp @@ -0,0 +1,105 @@ +// ====================================================================== +// \title DeployablesService.hpp +// \author fprime-dev +// \brief hpp file for DeployablesService component implementation class +// ====================================================================== + +#ifndef DeployablesService_HPP +#define DeployablesService_HPP + +#include "Components/DeployablesService/DeployablesServiceComponentAc.hpp" + +#include +#include +#include +#include + +#define NAME_CHAR_MAX 32 +#define NUMBER_OF_DEPLOYABLES 7 + +#define BURNWIRE_WAIT_TIME_SECONDS 20 +#define SWITCH_QUERY_RESPONSE_WAIT_TIME_SECONDS 2 + +#define NUMBER_OF_DEPLOYMENT_ATTEMPTS 3 + +namespace Components { + +enum class DeployableStatus { + STOWED = 0, + PENDING = 1, + DEPLOYED = 2, + FAILED_ALL_ATTEMPTS = + 3, // If the deployment was attempted N times and still failed... +}; + +struct DeployableInfo { + DeployableStatus status; + int numAttempts; +}; + +class DeployablesService : public DeployablesServiceComponentBase { + + public: + // ---------------------------------------------------------------------- + // Construction, initialization, and destruction + // ---------------------------------------------------------------------- + + //! Construct object DeployablesService + //! + DeployablesService(const char *const compName /*!< The component name*/ + ); + + //! Destroy object DeployablesService + //! + ~DeployablesService(); + + PRIVATE : + + // Holds a reference to each component and its associated deployment + // status + std::map + deployablesMap; + + // Holds the order in which the deployables will be attempted deployment + std::array LEOPDeploymentSequence; + + void attemptDeployment(const std::string &deployableName); + + void updateDeployableStatus(const std::string &deployableName, + DeployableStatus status); + + void updateDeployableAttempts(const std::string &deployableName, + int numAttempts); + + std::string getStatusString(DeployableStatus status); + + void printAllDeployablesStatus(); + + // ---------------------------------------------------------------------- + // Handler implementations for user-defined typed input ports + // ---------------------------------------------------------------------- + + //! Handler implementation for deployableFeedbackSwitchPort + //! + void deployableFeedbackSwitchPort_handler( + const NATIVE_INT_TYPE portNum, /*!< The port number*/ + const aString &a); + + PRIVATE : + + // ---------------------------------------------------------------------- + // Command handler implementations + // ---------------------------------------------------------------------- + + //! Implementation for ActivateDeploymentSequence command handler + //! + void + ActivateDeploymentSequence_cmdHandler( + const FwOpcodeType opCode, /*!< The opcode*/ + const U32 cmdSeq /*!< The command sequence number*/ + ); +}; + +} // end namespace Components + +#endif diff --git a/cubics-obc-test/Components/DeployablesService/docs/sdd.md b/cubics-obc-test/Components/DeployablesService/docs/sdd.md new file mode 100644 index 00000000..841831b8 --- /dev/null +++ b/cubics-obc-test/Components/DeployablesService/docs/sdd.md @@ -0,0 +1,66 @@ +# Components::DeployablesService + +Service specifically for deployables subsystem + +## Usage Examples +Add usage examples here + +### Diagrams +Add diagrams here + +### Typical Usage +And the typical usage of the component here + +## Class Diagram +Add a class diagram here + +## Port Descriptions +| Name | Description | +|---|---| +|---|---| + +## Component States +Add component states in the chart below +| Name | Description | +|---|---| +|---|---| + +## Sequence Diagrams +Add sequence diagrams here + +## Parameters +| Name | Description | +|---|---| +|---|---| + +## Commands +| Name | Description | +|---|---| +|---|---| + +## Events +| Name | Description | +|---|---| +|---|---| + +## Telemetry +| Name | Description | +|---|---| +|---|---| + +## Unit Tests +Add unit test descriptions in the chart below +| Name | Description | Output | Coverage | +|---|---|---|---| +|---|---|---|---| + +## Requirements +Add requirements in the chart below +| Name | Description | Validation | +|---|---|---| +|---|---|---| + +## Change Log +| Date | Description | +|---|---| +|---| Initial Draft | \ No newline at end of file diff --git a/cubics-obc-test/SimulationDeployment/CMakeLists.txt b/cubics-obc-test/SimulationDeployment/CMakeLists.txt new file mode 100644 index 00000000..73e077d4 --- /dev/null +++ b/cubics-obc-test/SimulationDeployment/CMakeLists.txt @@ -0,0 +1,25 @@ +##### +# 'SimulationDeployment' Deployment: +# +# This registers the 'SimulationDeployment' deployment to the build system. +# Custom components that have not been added at the project-level should be added to +# the list below. +# +##### + +# Compile with all debug symbols +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") + + +### +# Topology and Components +### +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Top/") + +# Add custom components to this specific deployment here +# add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/MyComponent/") + +set(SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/Main.cpp") +set(MOD_DEPS ${FPRIME_CURRENT_MODULE}/Top) + +register_fprime_deployment() diff --git a/cubics-obc-test/SimulationDeployment/Main.cpp b/cubics-obc-test/SimulationDeployment/Main.cpp new file mode 100644 index 00000000..e5218049 --- /dev/null +++ b/cubics-obc-test/SimulationDeployment/Main.cpp @@ -0,0 +1,90 @@ +// ====================================================================== +// \title Main.cpp +// \brief main program for the F' application. Intended for CLI-based systems (Linux, macOS) +// +// ====================================================================== +// Used to access topology functions +#include +// Used for signal handling shutdown +#include +// Used for command line argument processing +#include +// Used for printf functions +#include + +/** + * \brief print command line help message + * + * This will print a command line help message including the available command line arguments. + * + * @param app: name of application + */ +void print_usage(const char* app) { + (void)printf("Usage: ./%s [options]\n-a\thostname/IP address\n-p\tport_number\n", app); +} + +/** + * \brief shutdown topology cycling on signal + * + * The reference topology allows for a simulated cycling of the rate groups. This simulated cycling needs to be stopped + * in order for the program to shutdown. This is done via handling signals such that it is performed via Ctrl-C + * + * @param signum + */ +static void signalHandler(int signum) { + SimulationDeployment::stopSimulatedCycle(); +} + +/** + * \brief execute the program + * + * This F´ program is designed to run in standard environments (e.g. Linux/macOs running on a laptop). Thus it uses + * command line inputs to specify how to connect. + * + * @param argc: argument count supplied to program + * @param argv: argument values supplied to program + * @return: 0 on success, something else on failure + */ +int main(int argc, char* argv[]) { + U32 port_number = 0; + I32 option = 0; + char* hostname = nullptr; + + // Loop while reading the getopt supplied options + while ((option = getopt(argc, argv, "hp:a:")) != -1) { + switch (option) { + // Handle the -a argument for address/hostname + case 'a': + hostname = optarg; + break; + // Handle the -p port number argument + case 'p': + port_number = static_cast(atoi(optarg)); + break; + // Cascade intended: help output + case 'h': + // Cascade intended: help output + case '?': + // Default case: output help and exit + default: + print_usage(argv[0]); + return (option == 'h') ? 0 : 1; + } + } + // Object for communicating state to the reference topology + SimulationDeployment::TopologyState inputs; + inputs.hostname = hostname; + inputs.port = port_number; + + // Setup program shutdown via Ctrl-C + signal(SIGINT, signalHandler); + signal(SIGTERM, signalHandler); + (void)printf("Hit Ctrl-C to quit\n"); + + // Setup, cycle, and teardown topology + SimulationDeployment::setupTopology(inputs); + SimulationDeployment::startSimulatedCycle(1000); // Program loop cycling rate groups at 1Hz + SimulationDeployment::teardownTopology(inputs); + (void)printf("Exiting...\n"); + return 0; +} diff --git a/cubics-obc-test/SimulationDeployment/README.md b/cubics-obc-test/SimulationDeployment/README.md new file mode 100644 index 00000000..1a860710 --- /dev/null +++ b/cubics-obc-test/SimulationDeployment/README.md @@ -0,0 +1,39 @@ +# SimulationDeployment Application + +This deployment was auto-generated by the F' utility tool. + +## Building and Running the SimulationDeployment Application + +In order to build the SimulationDeployment application, or any other F´ application, we first need to generate a build directory. This can be done with the following commands: + +``` +cd SimulationDeployment +fprime-util generate +``` + +The next step is to build the SimulationDeployment application's code. +``` +fprime-util build +``` + +## Running the application and F' GDS + +The following command will spin up the F' GDS as well as run the application binary and the components necessary for the GDS and application to communicate. + +``` +cd SimulationDeployment +fprime-gds +``` + +To run the ground system without starting the SimulationDeployment app: +``` +cd SimulationDeployment +fprime-gds --no-app +``` + +The application binary may then be run independently from the created 'bin' directory. + +``` +cd SimulationDeployment/build-artifacts//bin/ +./SimulationDeployment -a 127.0.0.1 -p 50000 +``` diff --git a/cubics-obc-test/SimulationDeployment/Top/CMakeLists.txt b/cubics-obc-test/SimulationDeployment/Top/CMakeLists.txt new file mode 100644 index 00000000..a01625dc --- /dev/null +++ b/cubics-obc-test/SimulationDeployment/Top/CMakeLists.txt @@ -0,0 +1,22 @@ +#### +# F prime CMakeLists.txt: +# +# SOURCE_FILES: combined list of source and autocoding files +# MOD_DEPS: (optional) module dependencies +#### + +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/instances.fpp" + "${CMAKE_CURRENT_LIST_DIR}/SimulationDeploymentPackets.xml" + "${CMAKE_CURRENT_LIST_DIR}/topology.fpp" + "${CMAKE_CURRENT_LIST_DIR}/SimulationDeploymentTopology.cpp" +) +set(MOD_DEPS + Fw/Logger + Svc/LinuxTime + # Communication Implementations + Drv/Udp + Drv/TcpClient +) + +register_fprime_module() diff --git a/cubics-obc-test/SimulationDeployment/Top/SimulationDeploymentPackets.xml b/cubics-obc-test/SimulationDeployment/Top/SimulationDeploymentPackets.xml new file mode 100644 index 00000000..1b8a0712 --- /dev/null +++ b/cubics-obc-test/SimulationDeployment/Top/SimulationDeploymentPackets.xml @@ -0,0 +1,86 @@ + + + SimulationDeployment/Top/SimulationDeploymentTopologyAppAi.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cubics-obc-test/SimulationDeployment/Top/SimulationDeploymentTopology.cpp b/cubics-obc-test/SimulationDeployment/Top/SimulationDeploymentTopology.cpp new file mode 100644 index 00000000..95b34d2c --- /dev/null +++ b/cubics-obc-test/SimulationDeployment/Top/SimulationDeploymentTopology.cpp @@ -0,0 +1,202 @@ +// ====================================================================== +// \title SimulationDeploymentTopology.cpp +// \brief cpp file containing the topology instantiation code +// +// ====================================================================== +// Provides access to autocoded functions +#include +#include + +// Necessary project-specified types +#include +#include +#include + +// Used for 1Hz synthetic cycling +#include + +// Allows easy reference to objects in FPP/autocoder required namespaces +using namespace SimulationDeployment; + +// Instantiate a system logger that will handle Fw::Logger::logMsg calls +Os::Log logger; + +// The reference topology uses a malloc-based allocator for components that need to allocate memory during the +// initialization phase. +Fw::MallocAllocator mallocator; + +// The reference topology uses the F´ packet protocol when communicating with the ground and therefore uses the F´ +// framing and deframing implementations. +Svc::FprimeFraming framing; +Svc::FprimeDeframing deframing; + +Svc::ComQueue::QueueConfigurationTable configurationTable; + +// The reference topology divides the incoming clock signal (1Hz) into sub-signals: 1Hz, 1/2Hz, and 1/4Hz +NATIVE_INT_TYPE rateGroupDivisors[Svc::RateGroupDriver::DIVIDER_SIZE] = {1, 2, 4}; + +// Rate groups may supply a context token to each of the attached children whose purpose is set by the project. The +// reference topology sets each token to zero as these contexts are unused in this project. +NATIVE_INT_TYPE rateGroup1Context[Svc::ActiveRateGroup::CONNECTION_COUNT_MAX] = {}; +NATIVE_INT_TYPE rateGroup2Context[Svc::ActiveRateGroup::CONNECTION_COUNT_MAX] = {}; +NATIVE_INT_TYPE rateGroup3Context[Svc::ActiveRateGroup::CONNECTION_COUNT_MAX] = {}; + +// A number of constants are needed for construction of the topology. These are specified here. +enum TopologyConstants { + CMD_SEQ_BUFFER_SIZE = 5 * 1024, + FILE_DOWNLINK_TIMEOUT = 1000, + FILE_DOWNLINK_COOLDOWN = 1000, + FILE_DOWNLINK_CYCLE_TIME = 1000, + FILE_DOWNLINK_FILE_QUEUE_DEPTH = 10, + HEALTH_WATCHDOG_CODE = 0x123, + COMM_PRIORITY = 100, + // bufferManager constants + FRAMER_BUFFER_SIZE = FW_MAX(FW_COM_BUFFER_MAX_SIZE, FW_FILE_BUFFER_MAX_SIZE + sizeof(U32)) + HASH_DIGEST_LENGTH + Svc::FpFrameHeader::SIZE, + FRAMER_BUFFER_COUNT = 30, + DEFRAMER_BUFFER_SIZE = FW_MAX(FW_COM_BUFFER_MAX_SIZE, FW_FILE_BUFFER_MAX_SIZE + sizeof(U32)), + DEFRAMER_BUFFER_COUNT = 30, + COM_DRIVER_BUFFER_SIZE = 3000, + COM_DRIVER_BUFFER_COUNT = 30, + BUFFER_MANAGER_ID = 200 +}; + +// Ping entries are autocoded, however; this code is not properly exported. Thus, it is copied here. +Svc::Health::PingEntry pingEntries[] = { + {PingEntries::blockDrv::WARN, PingEntries::blockDrv::FATAL, "blockDrv"}, + {PingEntries::tlmSend::WARN, PingEntries::tlmSend::FATAL, "chanTlm"}, + {PingEntries::cmdDisp::WARN, PingEntries::cmdDisp::FATAL, "cmdDisp"}, + {PingEntries::cmdSeq::WARN, PingEntries::cmdSeq::FATAL, "cmdSeq"}, + {PingEntries::eventLogger::WARN, PingEntries::eventLogger::FATAL, "eventLogger"}, + {PingEntries::fileDownlink::WARN, PingEntries::fileDownlink::FATAL, "fileDownlink"}, + {PingEntries::fileManager::WARN, PingEntries::fileManager::FATAL, "fileManager"}, + {PingEntries::fileUplink::WARN, PingEntries::fileUplink::FATAL, "fileUplink"}, + {PingEntries::prmDb::WARN, PingEntries::prmDb::FATAL, "prmDb"}, + {PingEntries::rateGroup1::WARN, PingEntries::rateGroup1::FATAL, "rateGroup1"}, + {PingEntries::rateGroup2::WARN, PingEntries::rateGroup2::FATAL, "rateGroup2"}, + {PingEntries::rateGroup3::WARN, PingEntries::rateGroup3::FATAL, "rateGroup3"}, +}; + +/** + * \brief configure/setup components in project-specific way + * + * This is a *helper* function which configures/sets up each component requiring project specific input. This includes + * allocating resources, passing-in arguments, etc. This function may be inlined into the topology setup function if + * desired, but is extracted here for clarity. + */ +void configureTopology() { + // Command sequencer needs to allocate memory to hold contents of command sequences + cmdSeq.allocateBuffer(0, mallocator, CMD_SEQ_BUFFER_SIZE); + + // Rate group driver needs a divisor list + rateGroupDriver.configure(rateGroupDivisors, FW_NUM_ARRAY_ELEMENTS(rateGroupDivisors)); + + // Rate groups require context arrays. + rateGroup1.configure(rateGroup1Context, FW_NUM_ARRAY_ELEMENTS(rateGroup1Context)); + rateGroup2.configure(rateGroup2Context, FW_NUM_ARRAY_ELEMENTS(rateGroup2Context)); + rateGroup3.configure(rateGroup3Context, FW_NUM_ARRAY_ELEMENTS(rateGroup3Context)); + + // File downlink requires some project-derived properties. + fileDownlink.configure(FILE_DOWNLINK_TIMEOUT, FILE_DOWNLINK_COOLDOWN, FILE_DOWNLINK_CYCLE_TIME, + FILE_DOWNLINK_FILE_QUEUE_DEPTH); + + // Parameter database is configured with a database file name, and that file must be initially read. + prmDb.configure("PrmDb.dat"); + prmDb.readParamFile(); + + // Health is supplied a set of ping entires. + health.setPingEntries(pingEntries, FW_NUM_ARRAY_ELEMENTS(pingEntries), HEALTH_WATCHDOG_CODE); + + // Buffer managers need a configured set of buckets and an allocator used to allocate memory for those buckets. + Svc::BufferManager::BufferBins upBuffMgrBins; + memset(&upBuffMgrBins, 0, sizeof(upBuffMgrBins)); + upBuffMgrBins.bins[0].bufferSize = FRAMER_BUFFER_SIZE; + upBuffMgrBins.bins[0].numBuffers = FRAMER_BUFFER_COUNT; + upBuffMgrBins.bins[1].bufferSize = DEFRAMER_BUFFER_SIZE; + upBuffMgrBins.bins[1].numBuffers = DEFRAMER_BUFFER_COUNT; + upBuffMgrBins.bins[2].bufferSize = COM_DRIVER_BUFFER_SIZE; + upBuffMgrBins.bins[2].numBuffers = COM_DRIVER_BUFFER_COUNT; + bufferManager.setup(BUFFER_MANAGER_ID, 0, mallocator, upBuffMgrBins); + + // Framer and Deframer components need to be passed a protocol handler + framer.setup(framing); + deframer.setup(deframing); + + // Note: Uncomment when using Svc:TlmPacketizer + //tlmSend.setPacketList(SimulationDeploymentPacketsPkts, SimulationDeploymentPacketsIgnore, 1); + + // Events (highest-priority) + configurationTable.entries[0] = {.depth = 100, .priority = 0}; + // Telemetry + configurationTable.entries[1] = {.depth = 500, .priority = 2}; + // File Downlink + configurationTable.entries[2] = {.depth = 100, .priority = 1}; + // Allocation identifier is 0 as the MallocAllocator discards it + comQueue.configure(configurationTable, 0, mallocator); +} + +// Public functions for use in main program are namespaced with deployment name SimulationDeployment +namespace SimulationDeployment { +void setupTopology(const TopologyState& state) { + // Autocoded initialization. Function provided by autocoder. + initComponents(state); + // Autocoded id setup. Function provided by autocoder. + setBaseIds(); + // Autocoded connection wiring. Function provided by autocoder. + connectComponents(); + // Autocoded command registration. Function provided by autocoder. + regCommands(); + // Project-specific component configuration. Function provided above. May be inlined, if desired. + configureTopology(); + // Autocoded parameter loading. Function provided by autocoder. + // loadParameters(); + // Autocoded task kick-off (active components). Function provided by autocoder. + startTasks(state); + // Initialize socket client communication if and only if there is a valid specification + if (state.hostname != nullptr && state.port != 0) { + Os::TaskString name("ReceiveTask"); + // Uplink is configured for receive so a socket task is started + comDriver.configure(state.hostname, state.port); + comDriver.startSocketTask(name, true, COMM_PRIORITY, Default::STACK_SIZE); + } +} + +// Variables used for cycle simulation +Os::Mutex cycleLock; +volatile bool cycleFlag = true; + +void startSimulatedCycle(U32 milliseconds) { + cycleLock.lock(); + bool cycling = cycleFlag; + cycleLock.unLock(); + + // Main loop + while (cycling) { + SimulationDeployment::blockDrv.callIsr(); + Os::Task::delay(milliseconds); + + cycleLock.lock(); + cycling = cycleFlag; + cycleLock.unLock(); + } +} + +void stopSimulatedCycle() { + cycleLock.lock(); + cycleFlag = false; + cycleLock.unLock(); +} + +void teardownTopology(const TopologyState& state) { + // Autocoded (active component) task clean-up. Functions provided by topology autocoder. + stopTasks(state); + freeThreads(state); + + // Other task clean-up. + comDriver.stopSocketTask(); + (void)comDriver.joinSocketTask(nullptr); + + // Resource deallocation + cmdSeq.deallocateBuffer(mallocator); + bufferManager.cleanup(); +} +}; // namespace SimulationDeployment diff --git a/cubics-obc-test/SimulationDeployment/Top/SimulationDeploymentTopology.hpp b/cubics-obc-test/SimulationDeployment/Top/SimulationDeploymentTopology.hpp new file mode 100644 index 00000000..47524e30 --- /dev/null +++ b/cubics-obc-test/SimulationDeployment/Top/SimulationDeploymentTopology.hpp @@ -0,0 +1,86 @@ +// ====================================================================== +// \title SimulationDeploymentTopology.hpp +// \brief header file containing the topology instantiation definitions +// +// ====================================================================== +#ifndef SIMULATIONDEPLOYMENT_SIMULATIONDEPLOYMENTTOPOLOGY_HPP +#define SIMULATIONDEPLOYMENT_SIMULATIONDEPLOYMENTTOPOLOGY_HPP +// Included for access to SimulationDeployment::TopologyState and SimulationDeployment::ConfigObjects::pingEntries. These definitions are required by the +// autocoder, but are also used in this hand-coded topology. +#include + +// Remove unnecessary SimulationDeployment:: qualifications +using namespace SimulationDeployment; +namespace SimulationDeployment { +/** + * \brief initialize and run the F´ topology + * + * Initializes, configures, and runs the F´ topology. This is performed through a series of steps, some provided via + * autocoded functions, and others provided via the functions implementation. These steps are: + * + * 1. Call the autocoded `initComponents()` function initializing each component via the `component.init` method + * 2. Call the autocoded `setBaseIds()` function to set the base IDs (offset) for each component instance + * 3. Call the autocoded `connectComponents()` function to wire-together the topology of components + * 4. Configure components requiring custom configuration + * 5. Call the autocoded `loadParameters()` function to cause each component to load initial parameter values + * 6. Call the autocoded `startTasks()` function to start the active component tasks + * 7. Start tasks not owned by active components + * + * Step 4 and step 7 are custom and supplied by the project. The ordering of steps 1, 2, 3, 5, and 6 are critical for + * F´ topologies to function. Configuration (step 4) typically assumes a connect but not started topology and is thus + * inserted between step 3 and 5. Step 7 may come before or after the active component initializations. Since these + * custom tasks often start radio communication it is convenient to start them last. + * + * The state argument carries command line inputs used to setup the topology. For an explanation of the required type + * SimulationDeployment::TopologyState see: SimulationDeploymentTopologyDefs.hpp. + * + * \param state: object shuttling CLI arguments (hostname, port) needed to construct the topology + */ +void setupTopology(const TopologyState& state); + +/** + * \brief teardown the F´ topology + * + * Tears down the F´ topology in preparation for shutdown. This is done via a series of steps, some provided by + * autocoded functions, and others provided via the function implementation. These steps are: + * + * 1. Call the autocoded `stopTasks()` function to stop the tasks started by `startTasks()` (active components) + * 2. Call the autocoded `freeThreads()` function to join to the tasks started by `startTasks()` + * 3. Stop the tasks not owned by active components + * 4. Join to the tasks not owned by active components + * 5. Deallocate other resources + * + * Step 1, 2, 3, and 4 must occur in-order as the tasks must be stopped before being joined. These tasks must be stopped + * and joined before any active resources may be deallocated. + * + * For an explanation of the required type SimulationDeployment::TopologyState see: SimulationDeploymentTopologyDefs.hpp. + * + * \param state: state object provided to setupTopology + */ +void teardownTopology(const TopologyState& state); + +/** + * \brief cycle the rate group driver at a crude rate + * + * The reference topology does not have a true 1Hz input clock for the rate group driver because it is designed to + * operate across various computing endpoints (e.g. laptops) where a clear 1Hz source may not be easily and generically + * achieved. This function mimics the cycling via a Task::delay(milliseconds) loop that manually invokes the ISR call + * to the example block driver. + * + * This loop is stopped via a startSimulatedCycle call. + * + * Note: projects should replace this with a component that produces an output port call at the appropriate frequency. + * + * \param milliseconds: milliseconds to delay for each cycle. Default: 1000 or 1Hz. + */ +void startSimulatedCycle(U32 milliseconds = 1000); + +/** + * \brief stop the simulated cycle started by startSimulatedCycle + * + * This stops the cycle started by startSimulatedCycle. + */ +void stopSimulatedCycle(); + +} // namespace SimulationDeployment +#endif diff --git a/cubics-obc-test/SimulationDeployment/Top/SimulationDeploymentTopologyDefs.hpp b/cubics-obc-test/SimulationDeployment/Top/SimulationDeploymentTopologyDefs.hpp new file mode 100644 index 00000000..b9374ef7 --- /dev/null +++ b/cubics-obc-test/SimulationDeployment/Top/SimulationDeploymentTopologyDefs.hpp @@ -0,0 +1,89 @@ +// ====================================================================== +// \title SimulationDeploymentTopologyDefs.hpp +// \brief required header file containing the required definitions for the topology autocoder +// +// ====================================================================== +#ifndef SIMULATIONDEPLOYMENT_SIMULATIONDEPLOYMENTTOPOLOGYDEFS_HPP +#define SIMULATIONDEPLOYMENT_SIMULATIONDEPLOYMENTTOPOLOGYDEFS_HPP + +#include "Drv/BlockDriver/BlockDriver.hpp" +#include "Fw/Types/MallocAllocator.hpp" +#include "SimulationDeployment/Top/FppConstantsAc.hpp" +#include "Svc/FramingProtocol/FprimeProtocol.hpp" +#include "Svc/Health/Health.hpp" + +// Definitions are placed within a namespace named after the deployment +namespace SimulationDeployment { + +/** + * \brief required type definition to carry state + * + * The topology autocoder requires an object that carries state with the name `SimulationDeployment::TopologyState`. Only the type + * definition is required by the autocoder and the contents of this object are otherwise opaque to the autocoder. The + * contents are entirely up to the definition of the project. This reference application specifies hostname and port + * fields, which are derived by command line inputs. + */ +struct TopologyState { + const char* hostname; + U32 port; +}; + +/** + * \brief required ping constants + * + * The topology autocoder requires a WARN and FATAL constant definition for each component that supports the health-ping + * interface. These are expressed as enum constants placed in a namespace named for the component instance. These + * are all placed in the PingEntries namespace. + * + * Each constant specifies how many missed pings are allowed before a WARNING_HI/FATAL event is triggered. In the + * following example, the health component will emit a WARNING_HI event if the component instance cmdDisp does not + * respond for 3 pings and will FATAL if responses are not received after a total of 5 pings. + * + * ```c++ + * namespace PingEntries { + * namespace cmdDisp { + * enum { WARN = 3, FATAL = 5 }; + * } + * } + * ``` + */ +namespace PingEntries { +namespace blockDrv { +enum { WARN = 3, FATAL = 5 }; +} +namespace tlmSend { +enum { WARN = 3, FATAL = 5 }; +} +namespace cmdDisp { +enum { WARN = 3, FATAL = 5 }; +} +namespace cmdSeq { +enum { WARN = 3, FATAL = 5 }; +} +namespace eventLogger { +enum { WARN = 3, FATAL = 5 }; +} +namespace fileDownlink { +enum { WARN = 3, FATAL = 5 }; +} +namespace fileManager { +enum { WARN = 3, FATAL = 5 }; +} +namespace fileUplink { +enum { WARN = 3, FATAL = 5 }; +} +namespace prmDb { +enum { WARN = 3, FATAL = 5 }; +} +namespace rateGroup1 { +enum { WARN = 3, FATAL = 5 }; +} +namespace rateGroup2 { +enum { WARN = 3, FATAL = 5 }; +} +namespace rateGroup3 { +enum { WARN = 3, FATAL = 5 }; +} +} // namespace PingEntries +} // namespace SimulationDeployment +#endif diff --git a/cubics-obc-test/SimulationDeployment/Top/instances.fpp b/cubics-obc-test/SimulationDeployment/Top/instances.fpp new file mode 100644 index 00000000..bd1e87ea --- /dev/null +++ b/cubics-obc-test/SimulationDeployment/Top/instances.fpp @@ -0,0 +1,139 @@ +module SimulationDeployment { + + # ---------------------------------------------------------------------- + # Defaults + # ---------------------------------------------------------------------- + + module Default { + constant QUEUE_SIZE = 10 + constant STACK_SIZE = 64 * 1024 + } + + # ---------------------------------------------------------------------- + # Active component instances + # ---------------------------------------------------------------------- + + instance blockDrv: Drv.BlockDriver base id 0x0100 \ + queue size Default.QUEUE_SIZE \ + stack size Default.STACK_SIZE \ + priority 140 + + instance rateGroup1: Svc.ActiveRateGroup base id 0x0200 \ + queue size Default.QUEUE_SIZE \ + stack size Default.STACK_SIZE \ + priority 120 + + instance rateGroup2: Svc.ActiveRateGroup base id 0x0300 \ + queue size Default.QUEUE_SIZE \ + stack size Default.STACK_SIZE \ + priority 119 + + instance rateGroup3: Svc.ActiveRateGroup base id 0x0400 \ + queue size Default.QUEUE_SIZE \ + stack size Default.STACK_SIZE \ + priority 118 + + instance cmdDisp: Svc.CommandDispatcher base id 0x0500 \ + queue size 20 \ + stack size Default.STACK_SIZE \ + priority 101 + + instance cmdSeq: Svc.CmdSequencer base id 0x0600 \ + queue size Default.QUEUE_SIZE \ + stack size Default.STACK_SIZE \ + priority 100 + + instance comQueue: Svc.ComQueue base id 0x0700 \ + queue size Default.QUEUE_SIZE \ + stack size Default.STACK_SIZE \ + priority 100 \ + + instance fileDownlink: Svc.FileDownlink base id 0x0800 \ + queue size 30 \ + stack size Default.STACK_SIZE \ + priority 100 + + instance fileManager: Svc.FileManager base id 0x0900 \ + queue size 30 \ + stack size Default.STACK_SIZE \ + priority 100 + + instance fileUplink: Svc.FileUplink base id 0x0A00 \ + queue size 30 \ + stack size Default.STACK_SIZE \ + priority 100 + + instance eventLogger: Svc.ActiveLogger base id 0x0B00 \ + queue size Default.QUEUE_SIZE \ + stack size Default.STACK_SIZE \ + priority 98 + + # comment in Svc.TlmChan or Svc.TlmPacketizer + # depending on which form of telemetry downlink + # you wish to use + + instance tlmSend: Svc.TlmChan base id 0x0C00 \ + queue size Default.QUEUE_SIZE \ + stack size Default.STACK_SIZE \ + priority 97 + + #instance tlmSend: Svc.TlmPacketizer base id 0x0C00 \ + # queue size Default.QUEUE_SIZE \ + # stack size Default.STACK_SIZE \ + # priority 97 + + instance prmDb: Svc.PrmDb base id 0x0D00 \ + queue size Default.QUEUE_SIZE \ + stack size Default.STACK_SIZE \ + priority 96 + + instance deployablesService: Components.DeployablesService base id 0x1D00 \ + queue size Default.QUEUE_SIZE \ + stack size Default.STACK_SIZE \ + priority 96 + + + + # ---------------------------------------------------------------------- + # Queued component instances + # ---------------------------------------------------------------------- + + instance $health: Svc.Health base id 0x2000 \ + queue size 25 + + # ---------------------------------------------------------------------- + # Passive component instances + # ---------------------------------------------------------------------- + + @ Communications driver. May be swapped with other comm drivers like UART + @ Note: Here we have TCP reliable uplink and UDP (low latency) downlink + instance comDriver: Drv.ByteStreamDriverModel base id 0x4000 \ + type "Drv::TcpClient" \ # type specified to select implementor of ByteStreamDriverModel + at "../../Drv/TcpClient/TcpClient.hpp" # location of above implementor must also be specified + + instance framer: Svc.Framer base id 0x4100 + + instance fatalAdapter: Svc.AssertFatalAdapter base id 0x4200 + + instance fatalHandler: Svc.FatalHandler base id 0x4300 + + instance bufferManager: Svc.BufferManager base id 0x4400 + + instance linuxTime: Svc.Time base id 0x4500 \ + type "Svc::LinuxTime" \ + at "../../Svc/LinuxTime/LinuxTime.hpp" + + instance rateGroupDriver: Svc.RateGroupDriver base id 0x4600 + + instance textLogger: Svc.PassiveTextLogger base id 0x4800 + + instance deframer: Svc.Deframer base id 0x4900 + + instance systemResources: Svc.SystemResources base id 0x4A00 + + instance comStub: Svc.ComStub base id 0x4B00 + + instance deployablesInterface: Components.DeployablesInterface base id 0x0F00 \ + + +} diff --git a/cubics-obc-test/SimulationDeployment/Top/topology.fpp b/cubics-obc-test/SimulationDeployment/Top/topology.fpp new file mode 100644 index 00000000..93948086 --- /dev/null +++ b/cubics-obc-test/SimulationDeployment/Top/topology.fpp @@ -0,0 +1,148 @@ +module SimulationDeployment { + + # ---------------------------------------------------------------------- + # Symbolic constants for port numbers + # ---------------------------------------------------------------------- + + enum Ports_RateGroups { + rateGroup1 + rateGroup2 + rateGroup3 + } + + topology SimulationDeployment { + + # ---------------------------------------------------------------------- + # Instances used in the topology + # ---------------------------------------------------------------------- + + instance $health + instance blockDrv + instance tlmSend + instance cmdDisp + instance cmdSeq + instance comDriver + instance comQueue + instance comStub + instance deframer + instance eventLogger + instance fatalAdapter + instance fatalHandler + instance fileDownlink + instance fileManager + instance fileUplink + instance bufferManager + instance framer + instance linuxTime + instance prmDb + instance rateGroup1 + instance rateGroup2 + instance rateGroup3 + instance rateGroupDriver + instance textLogger + instance systemResources + instance deployablesInterface + instance deployablesService + + # ---------------------------------------------------------------------- + # Pattern graph specifiers + # ---------------------------------------------------------------------- + + command connections instance cmdDisp + + event connections instance eventLogger + + param connections instance prmDb + + telemetry connections instance tlmSend + + text event connections instance textLogger + + time connections instance linuxTime + + health connections instance $health + + # ---------------------------------------------------------------------- + # Direct graph specifiers + # ---------------------------------------------------------------------- + + connections Downlink { + + eventLogger.PktSend -> comQueue.comQueueIn[0] + tlmSend.PktSend -> comQueue.comQueueIn[1] + fileDownlink.bufferSendOut -> comQueue.buffQueueIn[0] + + comQueue.comQueueSend -> framer.comIn + comQueue.buffQueueSend -> framer.bufferIn + + framer.framedAllocate -> bufferManager.bufferGetCallee + framer.framedOut -> comStub.comDataIn + framer.bufferDeallocate -> fileDownlink.bufferReturn + + comDriver.deallocate -> bufferManager.bufferSendIn + comDriver.ready -> comStub.drvConnected + + comStub.comStatus -> framer.comStatusIn + framer.comStatusOut -> comQueue.comStatusIn + comStub.drvDataOut -> comDriver.send + + } + + connections FaultProtection { + eventLogger.FatalAnnounce -> fatalHandler.FatalReceive + } + + connections RateGroups { + # Block driver + blockDrv.CycleOut -> rateGroupDriver.CycleIn + + # Rate group 1 + rateGroupDriver.CycleOut[Ports_RateGroups.rateGroup1] -> rateGroup1.CycleIn + rateGroup1.RateGroupMemberOut[0] -> tlmSend.Run + rateGroup1.RateGroupMemberOut[1] -> fileDownlink.Run + rateGroup1.RateGroupMemberOut[2] -> systemResources.run + + # Rate group 2 + rateGroupDriver.CycleOut[Ports_RateGroups.rateGroup2] -> rateGroup2.CycleIn + rateGroup2.RateGroupMemberOut[0] -> cmdSeq.schedIn + + # Rate group 3 + rateGroupDriver.CycleOut[Ports_RateGroups.rateGroup3] -> rateGroup3.CycleIn + rateGroup3.RateGroupMemberOut[0] -> $health.Run + rateGroup3.RateGroupMemberOut[1] -> blockDrv.Sched + rateGroup3.RateGroupMemberOut[2] -> bufferManager.schedIn + } + + connections Sequencer { + cmdSeq.comCmdOut -> cmdDisp.seqCmdBuff + cmdDisp.seqCmdStatus -> cmdSeq.cmdResponseIn + } + + connections Uplink { + + comDriver.allocate -> bufferManager.bufferGetCallee + comDriver.$recv -> comStub.drvDataIn + comStub.comDataOut -> deframer.framedIn + + deframer.framedDeallocate -> bufferManager.bufferSendIn + deframer.comOut -> cmdDisp.seqCmdBuff + + cmdDisp.seqCmdStatus -> deframer.cmdResponseIn + + deframer.bufferAllocate -> bufferManager.bufferGetCallee + deframer.bufferOut -> fileUplink.bufferSendIn + deframer.bufferDeallocate -> bufferManager.bufferSendIn + fileUplink.bufferSendOut -> bufferManager.bufferSendIn + } + + connections SimulationDeployment { + # Add here connections to user-defined components + + deployablesService.deployableToggleBurnwirePort -> deployablesInterface.deployableDataOutgoing + deployablesInterface.deployableDataIncoming -> deployablesService.deployableFeedbackSwitchPort + + } + + } + +} diff --git a/cubics-obc-test/project.cmake b/cubics-obc-test/project.cmake index ae5b7153..f964bada 100644 --- a/cubics-obc-test/project.cmake +++ b/cubics-obc-test/project.cmake @@ -1,3 +1,6 @@ # This CMake file is intended to register project-wide objects so they can be # reused easily between deployments, but also by other projects. add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Components/Burnwire/") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/SimulationDeployment/") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Components/DeployablesInterface/") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Components/DeployablesService/")