diff --git a/CHANGELOG.md b/CHANGELOG.md index 12c91f21..917bf8c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,8 @@ list yields a list of all related PRs for each release. - Increase number of allowed consescutive action commands from 3 to 16 PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/294 - Fix for EM SW: Always create ACS Task +- Added Scex device handler and Scex uart reader + PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/303 - Added first version of ACS Controller with gathers MGM data in a set - Some tweaks for IMTQ handler diff --git a/CMakeLists.txt b/CMakeLists.txt index 28bbfd39..9200010b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,6 +125,9 @@ set(OBSW_ADD_GOMSPACE_PCDU set(OBSW_ADD_RW ${INIT_VAL} CACHE STRING "Add RW modules") +set(OBSW_ADD_SCEX_DEVICE + ${INIT_VAL} + CACHE STRING "Add Solar Cell Experiment module") # ############################################################################## # Pre-Sources preparation @@ -389,21 +392,19 @@ add_subdirectory(${TEST_PATH}) add_subdirectory(${UNITTEST_PATH}) # This should have already been downloaded by the FSFW Still include it to be -# safe -find_package(etl ${FSFW_ETL_LIB_MAJOR_VERSION} CONFIG QUIET) -# Not installed, so use FetchContent to download and provide etl -if(NOT etl_FOUND) - message( - STATUS - "No ETL installation was found with find_package. Installing and providing " - "etl with FindPackage") - include(FetchContent) - FetchContent_Declare( - etl - GIT_REPOSITORY https://github.com/ETLCPP/etl - GIT_TAG ${FSFW_ETL_LIB_VERSION}) - list(APPEND FSFW_FETCH_CONTENT_TARGETS etl) -endif() +# safe find_package(etl ${FSFW_ETL_LIB_MAJOR_VERSION} CONFIG QUIET) Not +# installed, so use FetchContent to download and provide etl if(NOT etl_FOUND) +message( + STATUS + "No ETL installation was found with find_package. Installing and providing " + "etl with FindPackage") +include(FetchContent) +FetchContent_Declare( + etl + GIT_REPOSITORY https://github.com/ETLCPP/etl + GIT_TAG ${FSFW_ETL_LIB_VERSION}) +list(APPEND FSFW_FETCH_CONTENT_TARGETS etl) +# endif() # Use same Catch2 version as framework if(NOT (TGT_BSP MATCHES "arm/te0720-1cfa") diff --git a/bsp_linux_board/CMakeLists.txt b/bsp_linux_board/CMakeLists.txt index 24e81b42..c1817ac1 100644 --- a/bsp_linux_board/CMakeLists.txt +++ b/bsp_linux_board/CMakeLists.txt @@ -1,5 +1,5 @@ target_sources(${OBSW_NAME} PUBLIC InitMission.cpp main.cpp gpioInit.cpp - ObjectFactory.cpp) + ObjectFactory.cpp RPiSdCardManager.cpp) add_subdirectory(boardconfig) add_subdirectory(boardtest) diff --git a/bsp_linux_board/InitMission.cpp b/bsp_linux_board/InitMission.cpp index 808fc8be..21cc5c23 100644 --- a/bsp_linux_board/InitMission.cpp +++ b/bsp_linux_board/InitMission.cpp @@ -1,5 +1,6 @@ #include "InitMission.h" +#include #include #include #include @@ -7,6 +8,7 @@ #include #include #include +#include #include #include @@ -77,6 +79,12 @@ void initmission::initTasks() { sif::error << "Add component TMTC Polling failed" << std::endl; } +#if OBSW_ADD_SCEX_DEVICE == 1 + PeriodicTaskIF* scexDevHandler; + PeriodicTaskIF* scexReaderTask; + scheduling::schedulingScex(*factory, scexDevHandler, scexReaderTask); +#endif + /* PUS Services */ std::vector pusTasks; createPusTasks(*factory, missedDeadlineFunc, pusTasks); @@ -109,6 +117,10 @@ void initmission::initTasks() { #endif /* OBSW_ADD_TEST_CODE == 1 */ taskStarter(pstTasks, "PST Tasks"); +#if OBSW_ADD_SCEX_DEVICE == 1 + scexDevHandler->startTask(); + scexReaderTask->startTask(); +#endif #if OBSW_ADD_TEST_PST == 1 if (startTestPst) { pstTestTask->startTask(); @@ -195,9 +207,12 @@ void initmission::createPstTasks(TaskFactory& factory, "SPI_PST", 70, PeriodicTaskIF::MINIMUM_STACK_SIZE * 4, 1.0, missedDeadlineFunc); result = pst::pstSpi(spiPst); if (result != returnvalue::OK) { - sif::error << "InitMission::initTasks: Creating PST failed!" << std::endl; + if (result != FixedTimeslotTaskIF::SLOT_LIST_EMPTY) { + sif::error << "InitMission::createPstTasks: Creating PST failed!" << std::endl; + } + } else { + taskVec.push_back(spiPst); } - taskVec.push_back(spiPst); #endif } @@ -228,6 +243,13 @@ void initmission::createTestTasks(TaskFactory& factory, if (result != returnvalue::OK) { initmission::printAddObjectError("UART_TEST", objects::UART_TEST); } + PeriodicTaskIF* scexReaderTask = factory.createPeriodicTask( + "SCEX_UART_READER", 20, PeriodicTaskIF::MINIMUM_STACK_SIZE, 2.0, missedDeadlineFunc); + result = scexReaderTask->addComponent(objects::SCEX_UART_READER); + if (result != returnvalue::OK) { + initmission::printAddObjectError("SCEX_UART_READER", objects::SCEX_UART_READER); + } + taskVec.push_back(scexReaderTask); #endif /* RPI_ADD_GPIO_TEST == 1 */ taskVec.push_back(testTask); diff --git a/bsp_linux_board/OBSWConfig.h.in b/bsp_linux_board/OBSWConfig.h.in index d8a981bd..f17c3d48 100644 --- a/bsp_linux_board/OBSWConfig.h.in +++ b/bsp_linux_board/OBSWConfig.h.in @@ -28,6 +28,7 @@ #define OBSW_ADD_RTD_DEVICES 0 #define OBSW_ADD_PL_PCDU 0 #define OBSW_ADD_TMP_DEVICES 0 +#define OBSW_ADD_SCEX_DEVICE 1 #define OBSW_ADD_RAD_SENSORS 0 #define OBSW_ADD_SYRLINKS 0 #define OBSW_STAR_TRACKER_GROUND_CONFIG 1 @@ -102,6 +103,12 @@ /*******************************************************************/ #cmakedefine EIVE_BUILD_GPSD_GPS_HANDLER +#define OBSW_USE_CCSDS_IP_CORE 0 +// Set to 1 if all telemetry should be sent to the PTME IP Core +#define OBSW_TM_TO_PTME 0 +// Set to 1 if telecommands are received via the PDEC IP Core +#define OBSW_TC_FROM_PDEC 0 + #cmakedefine LIBGPS_VERSION_MAJOR @LIBGPS_VERSION_MAJOR@ #cmakedefine LIBGPS_VERSION_MINOR @LIBGPS_VERSION_MINOR@ diff --git a/bsp_linux_board/ObjectFactory.cpp b/bsp_linux_board/ObjectFactory.cpp index 47f80936..8ebcdc8c 100644 --- a/bsp_linux_board/ObjectFactory.cpp +++ b/bsp_linux_board/ObjectFactory.cpp @@ -1,5 +1,8 @@ #include "ObjectFactory.h" +#include +#include + #include "OBSWConfig.h" #include "devConf.h" #include "devices/addresses.h" @@ -18,9 +21,8 @@ #include "mission/core/GenericFactory.h" #include "mission/devices/GPSHyperionHandler.h" #include "mission/devices/GyroADIS1650XHandler.h" -#include "mission/utility/TmFunnel.h" +#include "mission/tmtc/TmFunnel.h" #include "objects/systemObjectList.h" -#include "tmtc/apid.h" #include "tmtc/pusIds.h" /* UDP server includes */ @@ -45,8 +47,8 @@ #include "fsfw_hal/linux/spi/SpiCookie.h" void Factory::setStaticFrameworkObjectIds() { - PusServiceBase::packetSource = objects::PUS_PACKET_DISTRIBUTOR; - PusServiceBase::packetDestination = objects::TM_FUNNEL; + PusServiceBase::PUS_DISTRIBUTOR = objects::PUS_PACKET_DISTRIBUTOR; + PusServiceBase::PACKET_DESTINATION = objects::TM_FUNNEL; CommandingServiceBase::defaultPacketSource = objects::PUS_PACKET_DISTRIBUTOR; CommandingServiceBase::defaultPacketDestination = objects::TM_FUNNEL; @@ -54,9 +56,6 @@ void Factory::setStaticFrameworkObjectIds() { TmFunnel::downlinkDestination = objects::TMTC_BRIDGE; // No storage object for now. TmFunnel::storageDestination = objects::NO_OBJECT; - - VerificationReporter::messageReceiver = objects::PUS_SERVICE_1_VERIFICATION; - TmPacketBase::timeStamperId = objects::TIME_STAMPER; } void ObjectFactory::produce(void* args) { @@ -76,12 +75,17 @@ void ObjectFactory::produce(void* args) { createRpiAcsBoard(gpioIF, spiDev); #endif -#if OBSW_ADD_SUN_SENSORS == 1 || defined(OBSW_ADD_RTD_DEVICES) +#if OBSW_ADD_SUN_SENSORS == 1 || OBSW_ADD_RTD_DEVICES == 1 #ifdef RASPBERRY_PI rpi::gpio::initSpiCsDecoder(gpioIF); #endif #endif +#if OBSW_ADD_SCEX_DEVICE == 1 + auto* sdcMan = new RPiSdCardManager("/tmp"); + createScexComponents(uart::DEV, pwrSwitcher, *sdcMan, true, std::nullopt); +#endif + #if OBSW_ADD_SUN_SENSORS == 1 createSunSensorComponents(gpioIF, spiComIF, pwrSwitcher, spi::DEV); #endif diff --git a/bsp_linux_board/RPiSdCardManager.cpp b/bsp_linux_board/RPiSdCardManager.cpp new file mode 100644 index 00000000..dfcae8da --- /dev/null +++ b/bsp_linux_board/RPiSdCardManager.cpp @@ -0,0 +1,13 @@ +#include "RPiSdCardManager.h" + +RPiSdCardManager::RPiSdCardManager(std::string prefix) : prefix(std::move(prefix)) {} + +const std::string& RPiSdCardManager::getCurrentMountPrefix() const { return prefix; } + +bool RPiSdCardManager::isSdCardUsable(sd::SdCard sdCard) { return true; } + +std::optional RPiSdCardManager::getPreferredSdCard() const { return std::nullopt; } + +void RPiSdCardManager::setActiveSdCard(sd::SdCard sdCard) {} + +std::optional RPiSdCardManager::getActiveSdCard() const { return std::nullopt; } diff --git a/bsp_linux_board/RPiSdCardManager.h b/bsp_linux_board/RPiSdCardManager.h new file mode 100644 index 00000000..068471c1 --- /dev/null +++ b/bsp_linux_board/RPiSdCardManager.h @@ -0,0 +1,18 @@ +#ifndef BSP_LINUX_BOARD_RPISDCARDMANAGER_H_ +#define BSP_LINUX_BOARD_RPISDCARDMANAGER_H_ +#include + +class RPiSdCardManager : public SdCardMountedIF { + public: + RPiSdCardManager(std::string prefix); + const std::string& getCurrentMountPrefix() const override; + bool isSdCardUsable(sd::SdCard sdCard) override; + std::optional getPreferredSdCard() const override; + void setActiveSdCard(sd::SdCard sdCard) override; + std::optional getActiveSdCard() const override; + + private: + std::string prefix; +}; + +#endif /* BSP_LINUX_BOARD_RPISDCARDMANAGER_H_ */ diff --git a/bsp_linux_board/definitions.h b/bsp_linux_board/definitions.h index 1b6814f4..83aeb847 100644 --- a/bsp_linux_board/definitions.h +++ b/bsp_linux_board/definitions.h @@ -13,6 +13,12 @@ static constexpr char DEV[] = "/dev/spidev0.1"; } +namespace uart { + +static constexpr char DEV[] = "/dev/serial0"; + +} + /* Adapt these values accordingly */ namespace gpio { static constexpr uint8_t MGM_0_BCM_PIN = 17; diff --git a/bsp_q7s/OBSWConfig.h.in b/bsp_q7s/OBSWConfig.h.in index 9ff67209..260ccc2d 100644 --- a/bsp_q7s/OBSWConfig.h.in +++ b/bsp_q7s/OBSWConfig.h.in @@ -35,6 +35,7 @@ #define OBSW_ADD_TCS_CTRL @OBSW_ADD_TCS_CTRL@ #define OBSW_ADD_RW @OBSW_ADD_RW@ #define OBSW_ADD_RTD_DEVICES @OBSW_ADD_RTD_DEVICES@ +#define OBSW_ADD_SCEX_DEVICE @OBSW_ADD_SCEX_DEVICE@ #define OBSW_ADD_TMP_DEVICES @OBSW_ADD_TMP_DEVICES@ #define OBSW_ADD_RAD_SENSORS @OBSW_ADD_RAD_SENSORS@ #define OBSW_ADD_PL_PCDU @OBSW_ADD_PL_PCDU@ diff --git a/bsp_q7s/boardconfig/busConf.h b/bsp_q7s/boardconfig/busConf.h index 2402938f..4ccdc78d 100644 --- a/bsp_q7s/boardconfig/busConf.h +++ b/bsp_q7s/boardconfig/busConf.h @@ -15,6 +15,7 @@ static constexpr char UART_PLOC_MPSOC_DEV[] = "/dev/ul-plmpsoc"; static constexpr char UART_PLOC_SUPERVSIOR_DEV[] = "/dev/ul-plsv"; static constexpr char UART_SYRLINKS_DEV[] = "/dev/ul-syrlinks"; static constexpr char UART_STAR_TRACKER_DEV[] = "/dev/ul-str"; +static constexpr char UART_SCEX_DEV[] = "/dev/ttyS-SCEX"; static constexpr char UIO_PDEC_REGISTERS[] = "/dev/uio0"; static constexpr char UIO_PTME[] = "/dev/uio1"; diff --git a/bsp_q7s/core/CoreController.cpp b/bsp_q7s/core/CoreController.cpp index 31f81fb9..9ddd1a39 100644 --- a/bsp_q7s/core/CoreController.cpp +++ b/bsp_q7s/core/CoreController.cpp @@ -139,7 +139,11 @@ ReturnValue_t CoreController::initialize() { ReturnValue_t CoreController::initializeAfterTaskCreation() { ReturnValue_t result = returnvalue::OK; - sdInfo.active = sdcMan->getPreferredSdCard(); + auto sdCard = sdcMan->getPreferredSdCard(); + if (not sdCard) { + return returnvalue::FAILED; + } + sdInfo.active = sdCard.value(); if (sdInfo.active == sd::SdCard::NONE) { sif::error << "CoreController::initializeAfterTaskCreation: " "Issues getting preferred SD card, setting to 0" diff --git a/bsp_q7s/core/InitMission.cpp b/bsp_q7s/core/InitMission.cpp index 886c7661..ab16e5b3 100644 --- a/bsp_q7s/core/InitMission.cpp +++ b/bsp_q7s/core/InitMission.cpp @@ -1,6 +1,7 @@ #include "bsp_q7s/core/InitMission.h" #include +#include #include #include @@ -257,6 +258,11 @@ void initmission::initTasks() { initmission::printAddObjectError("PTME_TEST", objects::CCSDS_IP_CORE_BRIDGE); } #endif +#if OBSW_ADD_SCEX_DEVICE == 1 + PeriodicTaskIF* scexDevHandler; + PeriodicTaskIF* scexReaderTask; + scheduling::schedulingScex(*factory, scexDevHandler, scexReaderTask); +#endif std::vector pusTasks; createPusTasks(*factory, missedDeadlineFunc, pusTasks); @@ -295,8 +301,9 @@ void initmission::initTasks() { taskStarter(pstTasks, "PST task vector"); taskStarter(pusTasks, "PUS task vector"); -#if OBSW_ADD_TEST_CODE == 1 - taskStarter(testTasks, "Test task vector"); +#if OBSW_ADD_SCEX_DEVICE == 1 + scexDevHandler->startTask(); + scexReaderTask->startTask(); #endif #if OBSW_TEST_CCSDS_BRIDGE == 1 @@ -320,6 +327,11 @@ void initmission::initTasks() { #if OBSW_ADD_PLOC_SUPERVISOR == 1 supvHelperTask->startTask(); #endif /* OBSW_ADD_PLOC_SUPERVISOR == 1 */ + +#if OBSW_ADD_TEST_CODE == 1 + taskStarter(testTasks, "Test task vector"); +#endif + sif::info << "Tasks started.." << std::endl; } diff --git a/bsp_q7s/core/ObjectFactory.cpp b/bsp_q7s/core/ObjectFactory.cpp index 2d1934df..a30f9c37 100644 --- a/bsp_q7s/core/ObjectFactory.cpp +++ b/bsp_q7s/core/ObjectFactory.cpp @@ -1,6 +1,7 @@ #include "ObjectFactory.h" -#include +#include "mission/system/objects/AcsSubsystem.h" +#include "linux/devices/ScexUartReader.h" #include "OBSWConfig.h" #include "bsp_q7s/boardtest/Q7STestTask.h" @@ -881,6 +882,7 @@ void ObjectFactory::createTestComponents(LinuxLibgpioIF* gpioComIF) { new I2cTestClass(objects::I2C_TEST, q7s::I2C_DEFAULT_DEV); #endif #if OBSW_ADD_UART_TEST_CODE == 1 + // auto* reader= new ScexUartReader(objects::SCEX_UART_READER); new UartTestClass(objects::UART_TEST); #endif } diff --git a/bsp_q7s/fmObjectFactory.cpp b/bsp_q7s/fmObjectFactory.cpp index 0ce612cf..a8b9000d 100644 --- a/bsp_q7s/fmObjectFactory.cpp +++ b/bsp_q7s/fmObjectFactory.cpp @@ -1,3 +1,5 @@ +#include + #include "OBSWConfig.h" #include "bsp_q7s/core/CoreController.h" #include "bsp_q7s/core/ObjectFactory.h" @@ -57,6 +59,11 @@ void ObjectFactory::produce(void* args) { #if OBSW_USE_CCSDS_IP_CORE == 1 createCcsdsComponents(gpioComIF); #endif /* OBSW_USE_CCSDS_IP_CORE == 1 */ + +#if OBSW_ADD_SCEX_DEVICE == 1 + createScexComponents(q7s::UART_SCEX_DEV, pwrSwitcher, *SdCardManager::instance(), false, + pcdu::Switches::PDU1_CH5_SOLAR_CELL_EXP_5V); +#endif /* Test Task */ #if OBSW_ADD_TEST_CODE == 1 createTestComponents(gpioComIF); diff --git a/bsp_q7s/fs/SdCardManager.cpp b/bsp_q7s/fs/SdCardManager.cpp index ea238225..87d2a440 100644 --- a/bsp_q7s/fs/SdCardManager.cpp +++ b/bsp_q7s/fs/SdCardManager.cpp @@ -304,11 +304,6 @@ ReturnValue_t SdCardManager::sanitizeState(SdStatePair* statusPair, sd::SdCard p blocking = true; resetNonBlockingState = true; } - if (prefSdCard == sd::SdCard::NONE) { - result = getPreferredSdCard(); - if (result != returnvalue::OK) { - } - } if (statusPair == nullptr) { sdStatusPtr = std::make_unique(); statusPair = sdStatusPtr.get(); @@ -383,7 +378,7 @@ void SdCardManager::processSdStatusLine(std::pair& act idx++; } -sd::SdCard SdCardManager::getPreferredSdCard() const { +std::optional SdCardManager::getPreferredSdCard() const { MutexGuard mg(mutex); auto res = mg.getLockResult(); if (res != returnvalue::OK) { @@ -416,13 +411,9 @@ ReturnValue_t SdCardManager::updateSdCardStateFile() { return result; } -std::string SdCardManager::getCurrentMountPrefix() const { +const std::string& SdCardManager::getCurrentMountPrefix() const { MutexGuard mg(mutex); - if (sdInfo.active == sd::SdCard::SLOT_0) { - return config::SD_0_MOUNT_POINT; - } else { - return config::SD_1_MOUNT_POINT; - } + return currentPrefix; } SdCardManager::OpStatus SdCardManager::checkCurrentOp(Operations& currentOp) { @@ -558,6 +549,17 @@ ReturnValue_t SdCardManager::performFsck(sd::SdCard sdcard, bool printOutput, in return result; } -void SdCardManager::setActiveSdCard(sd::SdCard sdCard) { sdInfo.active = sdCard; } +void SdCardManager::setActiveSdCard(sd::SdCard sdCard) { + MutexGuard mg(mutex); + sdInfo.active = sdCard; + if (sdInfo.active == sd::SdCard::SLOT_0) { + currentPrefix = config::SD_0_MOUNT_POINT; + } else { + currentPrefix = config::SD_1_MOUNT_POINT; + } +} -sd::SdCard SdCardManager::getActiveSdCard() const { return sdInfo.active; } +std::optional SdCardManager::getActiveSdCard() const { + MutexGuard mg(mutex); + return sdInfo.active; +} diff --git a/bsp_q7s/fs/SdCardManager.h b/bsp_q7s/fs/SdCardManager.h index ad222687..52c8ed9b 100644 --- a/bsp_q7s/fs/SdCardManager.h +++ b/bsp_q7s/fs/SdCardManager.h @@ -91,7 +91,7 @@ class SdCardManager : public SystemObject, public SdCardMountedIF { * @param sdCard * @return */ - sd::SdCard getPreferredSdCard() const override; + std::optional getPreferredSdCard() const override; /** * Switch on the specified SD card. @@ -158,7 +158,7 @@ class SdCardManager : public SystemObject, public SdCardMountedIF { * mounted * @return */ - sd::SdCard getActiveSdCard() const override; + std::optional getActiveSdCard() const override; /** * Unmount the specified SD card. This is recommended before switching it off. The SD card @@ -187,7 +187,7 @@ class SdCardManager : public SystemObject, public SdCardMountedIF { * @param prefSdCardPtr * @return */ - std::string getCurrentMountPrefix() const override; + const std::string& getCurrentMountPrefix() const override; OpStatus checkCurrentOp(Operations& currentOp); diff --git a/bsp_q7s/memory/scratchApi.cpp b/bsp_q7s/memory/scratchApi.cpp index 78f93541..b5fa08f2 100644 --- a/bsp_q7s/memory/scratchApi.cpp +++ b/bsp_q7s/memory/scratchApi.cpp @@ -1,8 +1,8 @@ #include "scratchApi.h" ReturnValue_t scratch::writeString(std::string name, std::string string) { - std::ostringstream oss; - oss << "xsc_scratch write " << name << " \"" << string << "\""; + std::ostringstream oss("xsc_scratch write ", std::ostringstream::ate); + oss << name << " \"" << string << "\""; int result = std::system(oss.str().c_str()); if (result != 0) { utility::handleSystemError(result, "scratch::writeString"); @@ -39,8 +39,8 @@ ReturnValue_t scratch::readString(std::string key, std::string &string) { } ReturnValue_t scratch::clearValue(std::string key) { - std::ostringstream oss; - oss << "xsc_scratch clear " << key; + std::ostringstream oss("xsc_scratch clear ", std::ostringstream::ate); + oss << key; int result = std::system(oss.str().c_str()); if (result != 0) { utility::handleSystemError(result, "scratch::clearValue"); diff --git a/bsp_q7s/memory/scratchApi.h b/bsp_q7s/memory/scratchApi.h index a6b99fa0..b3ea4a6a 100644 --- a/bsp_q7s/memory/scratchApi.h +++ b/bsp_q7s/memory/scratchApi.h @@ -94,8 +94,8 @@ ReturnValue_t readToFile(std::string name, std::ifstream& file, std::string& fil template ::value>::type> inline ReturnValue_t writeNumber(std::string key, T num) noexcept { - std::ostringstream oss; - oss << "xsc_scratch write " << key << " " << std::to_string(num); + std::ostringstream oss("xsc_scratch write ", std::ostringstream::ate); + oss << key << " " << std::to_string(num); int result = std::system(oss.str().c_str()); if (result != 0) { utility::handleSystemError(result, "scratch::writeNumber"); diff --git a/common/config/devConf.h b/common/config/devConf.h index 51da9011..68a3e199 100644 --- a/common/config/devConf.h +++ b/common/config/devConf.h @@ -55,6 +55,7 @@ namespace uart { static constexpr size_t HYPERION_GPS_REPLY_MAX_BUFFER = 1024; static constexpr UartBaudRate SYRLINKS_BAUD = UartBaudRate::RATE_38400; +static constexpr UartBaudRate SCEX_BAUD = UartBaudRate::RATE_57600; static constexpr UartBaudRate GNSS_BAUD = UartBaudRate::RATE_9600; static constexpr UartBaudRate PLOC_MPSOC_BAUD = UartBaudRate::RATE_115200; static constexpr UartBaudRate PLOC_SUPV_BAUD = UartBaudRate::RATE_115200; diff --git a/common/config/eive/eventSubsystemIds.h b/common/config/eive/eventSubsystemIds.h index 55c55e88..8785f599 100644 --- a/common/config/eive/eventSubsystemIds.h +++ b/common/config/eive/eventSubsystemIds.h @@ -33,7 +33,8 @@ enum : uint8_t { ACU_HANDLER = 135, PLOC_SUPV_HELPER = 136, SYRLINKS = 137, - CONFIGHANDLER = 138, + SCEX_HANDLER = 138, + CONFIGHANDLER = 139, COMMON_SUBSYSTEM_ID_END }; diff --git a/common/config/eive/objects.h b/common/config/eive/objects.h index eeb39c5c..49b32858 100644 --- a/common/config/eive/objects.h +++ b/common/config/eive/objects.h @@ -58,6 +58,7 @@ enum commonObjects : uint32_t { PLOC_MPSOC_HANDLER = 0x44330015, PLOC_SUPERVISOR_HANDLER = 0x44330016, PLOC_SUPERVISOR_HELPER = 0x44330017, + SCEX = 0x44330032, SOLAR_ARRAY_DEPL_HANDLER = 0x444100A2, HEATER_HANDLER = 0x444100A4, diff --git a/fsfw b/fsfw index 1d6ccfe5..a8fb83df 160000 --- a/fsfw +++ b/fsfw @@ -1 +1 @@ -Subproject commit 1d6ccfe5ab63c268e1b62a731eb55d35eac88492 +Subproject commit a8fb83dfcedb5391ac942d6bcffa64f0cbca38c5 diff --git a/generators/bsp_q7s_events.csv b/generators/bsp_q7s_events.csv index 89f016f1..ab418c1f 100644 --- a/generators/bsp_q7s_events.csv +++ b/generators/bsp_q7s_events.csv @@ -216,8 +216,11 @@ Event ID (dec); Event ID (hex); Name; Severity; Description; File Path 13701;0x3585;REBOOT_SW;MEDIUM; Software reboot occurred. Can also be a systemd reboot. P1: Current Chip, P2: Current Copy;bsp_q7s/core/CoreController.h 13702;0x3586;REBOOT_MECHANISM_TRIGGERED;MEDIUM;The reboot mechanism was triggered. P1: First 16 bits: Last Chip, Last 16 bits: Last Copy, P2: Each byte is the respective reboot count for the slots;bsp_q7s/core/CoreController.h 13703;0x3587;REBOOT_HW;MEDIUM;;bsp_q7s/core/CoreController.h -13801;0x35e9;SET_CONFIGFILEVALUE_FAILED;MEDIUM;;mission/utility/GlobalConfigHandler.h -13802;0x35ea;GET_CONFIGFILEVALUE_FAILED;MEDIUM;;mission/utility/GlobalConfigHandler.h -13803;0x35eb;INSERT_CONFIGFILEVALUE_FAILED;MEDIUM;;mission/utility/GlobalConfigHandler.h -13804;0x35ec;WRITE_CONFIGFILE_FAILED;MEDIUM;;mission/utility/GlobalConfigHandler.h -13805;0x35ed;READ_CONFIGFILE_FAILED;MEDIUM;;mission/utility/GlobalConfigHandler.h +13800;0x35e8;MISSING_PACKET;LOW;;mission/devices/devicedefinitions/ScexDefinitions.h +13801;0x35e9;EXPERIMENT_TIMEDOUT;LOW;;mission/devices/devicedefinitions/ScexDefinitions.h +13802;0x35ea;MULTI_PACKET_COMMAND_DONE;INFO;;mission/devices/devicedefinitions/ScexDefinitions.h +13901;0x364d;SET_CONFIGFILEVALUE_FAILED;MEDIUM;;mission/utility/GlobalConfigHandler.h +13902;0x364e;GET_CONFIGFILEVALUE_FAILED;MEDIUM;;mission/utility/GlobalConfigHandler.h +13903;0x364f;INSERT_CONFIGFILEVALUE_FAILED;MEDIUM;;mission/utility/GlobalConfigHandler.h +13904;0x3650;WRITE_CONFIGFILE_FAILED;MEDIUM;;mission/utility/GlobalConfigHandler.h +13905;0x3651;READ_CONFIGFILE_FAILED;MEDIUM;;mission/utility/GlobalConfigHandler.h diff --git a/generators/bsp_q7s_objects.csv b/generators/bsp_q7s_objects.csv index 56da69a1..46dddd47 100644 --- a/generators/bsp_q7s_objects.csv +++ b/generators/bsp_q7s_objects.csv @@ -47,6 +47,7 @@ 0x44330015;PLOC_MPSOC_HANDLER 0x44330016;PLOC_SUPERVISOR_HANDLER 0x44330017;PLOC_SUPERVISOR_HELPER +0x44330032;SCEX 0x444100A2;SOLAR_ARRAY_DEPL_HANDLER 0x444100A4;HEATER_HANDLER 0x44420004;TMP1075_HANDLER_1 @@ -70,6 +71,7 @@ 0x445300A3;SYRLINKS_HK_HANDLER 0x49000000;ARDUINO_COM_IF 0x49010005;GPIO_IF +0x49010006;SCEX_UART_READER 0x49020004;SPI_MAIN_COM_IF 0x49020005;SPI_RW_COM_IF 0x49020006;SPI_RTD_COM_IF diff --git a/generators/events/translateEvents.cpp b/generators/events/translateEvents.cpp index 882bbefd..18b536ce 100644 --- a/generators/events/translateEvents.cpp +++ b/generators/events/translateEvents.cpp @@ -1,7 +1,7 @@ /** - * @brief Auto-generated event translation file. Contains 222 translations. + * @brief Auto-generated event translation file. Contains 225 translations. * @details - * Generated on: 2022-10-10 11:10:02 + * Generated on: 2022-10-10 17:38:50 */ #include "translateEvents.h" @@ -218,6 +218,9 @@ const char *ALLOC_FAILURE_STRING = "ALLOC_FAILURE"; const char *REBOOT_SW_STRING = "REBOOT_SW"; const char *REBOOT_MECHANISM_TRIGGERED_STRING = "REBOOT_MECHANISM_TRIGGERED"; const char *REBOOT_HW_STRING = "REBOOT_HW"; +const char *MISSING_PACKET_STRING = "MISSING_PACKET"; +const char *EXPERIMENT_TIMEDOUT_STRING = "EXPERIMENT_TIMEDOUT"; +const char *MULTI_PACKET_COMMAND_DONE_STRING = "MULTI_PACKET_COMMAND_DONE"; const char *SET_CONFIGFILEVALUE_FAILED_STRING = "SET_CONFIGFILEVALUE_FAILED"; const char *GET_CONFIGFILEVALUE_FAILED_STRING = "GET_CONFIGFILEVALUE_FAILED"; const char *INSERT_CONFIGFILEVALUE_FAILED_STRING = "INSERT_CONFIGFILEVALUE_FAILED"; @@ -652,15 +655,21 @@ const char *translateEvents(Event event) { return REBOOT_MECHANISM_TRIGGERED_STRING; case (13703): return REBOOT_HW_STRING; + case (13800): + return MISSING_PACKET_STRING; case (13801): - return SET_CONFIGFILEVALUE_FAILED_STRING; + return EXPERIMENT_TIMEDOUT_STRING; case (13802): + return MULTI_PACKET_COMMAND_DONE_STRING; + case (13901): + return SET_CONFIGFILEVALUE_FAILED_STRING; + case (13902): return GET_CONFIGFILEVALUE_FAILED_STRING; - case (13803): + case (13903): return INSERT_CONFIGFILEVALUE_FAILED_STRING; - case (13804): + case (13904): return WRITE_CONFIGFILE_FAILED_STRING; - case (13805): + case (13905): return READ_CONFIGFILE_FAILED_STRING; default: return "UNKNOWN_EVENT"; diff --git a/generators/objects/translateObjects.cpp b/generators/objects/translateObjects.cpp index 0b2c93ab..582ddfc7 100644 --- a/generators/objects/translateObjects.cpp +++ b/generators/objects/translateObjects.cpp @@ -1,8 +1,8 @@ /** * @brief Auto-generated object translation file. * @details - * Contains 138 translations. - * Generated on: 2022-10-10 11:10:02 + * Contains 140 translations. + * Generated on: 2022-10-10 17:38:50 */ #include "translateObjects.h" @@ -55,6 +55,7 @@ const char *PTME_CONFIG_STRING = "PTME_CONFIG"; const char *PLOC_MPSOC_HANDLER_STRING = "PLOC_MPSOC_HANDLER"; const char *PLOC_SUPERVISOR_HANDLER_STRING = "PLOC_SUPERVISOR_HANDLER"; const char *PLOC_SUPERVISOR_HELPER_STRING = "PLOC_SUPERVISOR_HELPER"; +const char *SCEX_STRING = "SCEX"; const char *SOLAR_ARRAY_DEPL_HANDLER_STRING = "SOLAR_ARRAY_DEPL_HANDLER"; const char *HEATER_HANDLER_STRING = "HEATER_HANDLER"; const char *TMP1075_HANDLER_1_STRING = "TMP1075_HANDLER_1"; @@ -78,6 +79,7 @@ const char *RTD_15_IC18_IMTQ_STRING = "RTD_15_IC18_IMTQ"; const char *SYRLINKS_HK_HANDLER_STRING = "SYRLINKS_HK_HANDLER"; const char *ARDUINO_COM_IF_STRING = "ARDUINO_COM_IF"; const char *GPIO_IF_STRING = "GPIO_IF"; +const char *SCEX_UART_READER_STRING = "SCEX_UART_READER"; const char *SPI_MAIN_COM_IF_STRING = "SPI_MAIN_COM_IF"; const char *SPI_RW_COM_IF_STRING = "SPI_RW_COM_IF"; const char *SPI_RTD_COM_IF_STRING = "SPI_RTD_COM_IF"; @@ -245,6 +247,8 @@ const char *translateObject(object_id_t object) { return PLOC_SUPERVISOR_HANDLER_STRING; case 0x44330017: return PLOC_SUPERVISOR_HELPER_STRING; + case 0x44330032: + return SCEX_STRING; case 0x444100A2: return SOLAR_ARRAY_DEPL_HANDLER_STRING; case 0x444100A4: @@ -291,6 +295,8 @@ const char *translateObject(object_id_t object) { return ARDUINO_COM_IF_STRING; case 0x49010005: return GPIO_IF_STRING; + case 0x49010006: + return SCEX_UART_READER_STRING; case 0x49020004: return SPI_MAIN_COM_IF_STRING; case 0x49020005: diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index e1d80966..861dfb5c 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -6,4 +6,4 @@ add_subdirectory(devices) add_subdirectory(fsfwconfig) add_subdirectory(obc) -target_sources(${OBSW_NAME} PUBLIC ObjectFactory.cpp) +target_sources(${OBSW_NAME} PUBLIC ObjectFactory.cpp InitMission.cpp) diff --git a/linux/InitMission.cpp b/linux/InitMission.cpp new file mode 100644 index 00000000..eb8f677a --- /dev/null +++ b/linux/InitMission.cpp @@ -0,0 +1,47 @@ +#include "InitMission.h" + +#include +#include +#include + +#include "OBSWConfig.h" +#include "ObjectFactory.h" + +void scheduling::schedulingScex(TaskFactory& factory, PeriodicTaskIF*& scexDevHandler, + PeriodicTaskIF*& scexReaderTask) { + using namespace initmission; + ReturnValue_t result = returnvalue::OK; +#if OBSW_PRINT_MISSED_DEADLINES == 1 + void (*missedDeadlineFunc)(void) = TaskFactory::printMissedDeadline; +#else + void (*missedDeadlineFunc)(void) = nullptr; +#endif + scexDevHandler = factory.createPeriodicTask( + "SCEX_DEV", 35, PeriodicTaskIF::MINIMUM_STACK_SIZE * 2, 0.5, missedDeadlineFunc); + + result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::PERFORM_OPERATION); + if (result != returnvalue::OK) { + printAddObjectError("SCEX_DEV", objects::SCEX); + } + result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::SEND_WRITE); + if (result != returnvalue::OK) { + printAddObjectError("SCEX_DEV", objects::SCEX); + } + result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::GET_WRITE); + if (result != returnvalue::OK) { + printAddObjectError("SCEX_DEV", objects::SCEX); + } + result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::SEND_READ); + if (result != returnvalue::OK) { + printAddObjectError("SCEX_DEV", objects::SCEX); + } + result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::GET_READ); + + result = returnvalue::OK; + scexReaderTask = factory.createPeriodicTask( + "SCEX_UART_READER", 20, PeriodicTaskIF::MINIMUM_STACK_SIZE, 2.0, missedDeadlineFunc); + result = scexReaderTask->addComponent(objects::SCEX_UART_READER); + if (result != returnvalue::OK) { + printAddObjectError("SCEX_UART_READER", objects::SCEX_UART_READER); + } +} diff --git a/linux/InitMission.h b/linux/InitMission.h new file mode 100644 index 00000000..e5a3afff --- /dev/null +++ b/linux/InitMission.h @@ -0,0 +1,7 @@ +#pragma once +#include + +namespace scheduling { +void schedulingScex(TaskFactory& factory, PeriodicTaskIF*& scexDevHandler, + PeriodicTaskIF*& scexReaderTask); +} diff --git a/linux/ObjectFactory.cpp b/linux/ObjectFactory.cpp index ea909a9e..1d90ee0c 100644 --- a/linux/ObjectFactory.cpp +++ b/linux/ObjectFactory.cpp @@ -7,12 +7,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -319,6 +321,22 @@ void ObjectFactory::createRtdComponents(std::string spiDev, GpioIF* gpioComIF, #endif // OBSW_ADD_RTD_DEVICES == 1 } +void ObjectFactory::createScexComponents(std::string uartDev, PowerSwitchIF* pwrSwitcher, + SdCardMountedIF& mountedIF, bool onImmediately, + std::optional switchId) { + auto* cookie = new UartCookie(objects::SCEX, uartDev, uart::SCEX_BAUD, 4096); + cookie->setTwoStopBits(); + //cookie->setParityEven(); + auto scexUartReader = new ScexUartReader(objects::SCEX_UART_READER); + auto scexHandler = new ScexDeviceHandler(objects::SCEX, *scexUartReader, cookie, mountedIF); + if (onImmediately) { + scexHandler->setStartUpImmediately(); + } + if (switchId) { + scexHandler->setPowerSwitcher(*pwrSwitcher, switchId.value()); + } +} + void ObjectFactory::createThermalController() { new ThermalController(objects::THERMAL_CONTROLLER); } diff --git a/linux/ObjectFactory.h b/linux/ObjectFactory.h index f2e81ed9..9c4ac63e 100644 --- a/linux/ObjectFactory.h +++ b/linux/ObjectFactory.h @@ -1,8 +1,11 @@ #pragma once +#include #include #include +#include +#include #include class GpioIF; @@ -17,6 +20,10 @@ void createSunSensorComponents(GpioIF* gpioComIF, SpiComIF* spiComIF, PowerSwitc void createRtdComponents(std::string spiDev, GpioIF* gpioComIF, PowerSwitchIF* pwrSwitcher, SpiComIF* comIF); +void createScexComponents(std::string uartDev, PowerSwitchIF* pwrSwitcher, + SdCardMountedIF& mountedIF, bool onImmediately, + std::optional switchId); + void gpioChecker(ReturnValue_t result, std::string output); void createThermalController(); diff --git a/linux/boardtest/UartTestClass.cpp b/linux/boardtest/UartTestClass.cpp index 891959da..4f476d4b 100644 --- a/linux/boardtest/UartTestClass.cpp +++ b/linux/boardtest/UartTestClass.cpp @@ -3,14 +3,21 @@ #include // Error integer and strerror() function #include // Contains file controls like O_RDWR #include +#include +#include +#include +#include #include // write(), read(), close() +#include +#include + #include "OBSWConfig.h" #include "fsfw/globalfunctions/CRC.h" #include "fsfw/globalfunctions/DleEncoder.h" #include "fsfw/globalfunctions/arrayprinter.h" #include "fsfw/serviceinterface.h" -#include "mission/devices/devicedefinitions/SCEXDefinitions.h" +#include "mission/devices/devicedefinitions/ScexDefinitions.h" #define GPS_REPLY_WIRETAPPING 0 @@ -18,7 +25,25 @@ #define RPI_TEST_GPS_HANDLER 0 #endif -UartTestClass::UartTestClass(object_id_t objectId) : TestTask(objectId) { mode = TestModes::SCEX; } +using namespace returnvalue; + +UartTestClass::UartTestClass(object_id_t objectId) : TestTask(objectId) { + mode = TestModes::SCEX; + scexMode = ScexModes::SIMPLE; + // No one-cell and all-cell support implemented yet + currCmd = scex::Cmds::PING; + if (scexMode == ScexModes::SIMPLE) { + auto encodingBuf = new std::array; + DleParser::BufPair encodingBufPair{encodingBuf->data(), encodingBuf->size()}; + auto decodedBuf = new std::array; + DleParser::BufPair decodingBufPair{decodedBuf->data(), decodedBuf->size()}; + // TODO: Code changes but this test class has not, might not work like this anymore + dleParser = new ScexDleParser(*(new SimpleRingBuffer(4096, true)), dleEncoder, encodingBufPair, + decodingBufPair); + } else { + reader = new ScexUartReader(objects::SCEX_UART_READER); + } +} ReturnValue_t UartTestClass::initialize() { if (mode == TestModes::GPS) { @@ -44,14 +69,14 @@ void UartTestClass::gpsInit() { #if RPI_TEST_GPS_HANDLER == 1 int result = lwgps_init(&gpsData); if (result == 0) { - sif::warning << "lwgps_init error: " << result << std::endl; + sif::warning << "UartTestClass::gpsInit: lwgps_init error: " << result << std::endl; } /* Get file descriptor */ serialPort = open("/dev/serial0", O_RDWR); if (serialPort < 0) { - sif::warning << "open call failed with error [" << errno << ", " << strerror(errno) - << std::endl; + sif::warning << "UartTestClass::gpsInit: open call failed with error [" << errno << ", " + << strerror(errno) << std::endl; } /* Setting up UART parameters */ tty.c_cflag &= ~PARENB; // Clear parity bit @@ -79,8 +104,8 @@ void UartTestClass::gpsInit() { cfsetispeed(&tty, B9600); cfsetospeed(&tty, B9600); if (tcsetattr(serialPort, TCSANOW, &tty) != 0) { - sif::warning << "tcsetattr call failed with error [" << errno << ", " << strerror(errno) - << std::endl; + sif::warning << "UartTestClass::gpsInit: tcsetattr call failed with error [" << errno << ", " + << strerror(errno) << std::endl; ; } // Flush received and unread data. Those are old NMEA strings which are not relevant anymore @@ -95,11 +120,11 @@ void UartTestClass::gpsPeriodic() { bytesRead = read(serialPort, reinterpret_cast(recBuf.data()), static_cast(recBuf.size())); if (bytesRead < 0) { - sif::warning << "UartTestClass::performPeriodicAction: read call failed with error [" << errno - << ", " << strerror(errno) << "]" << std::endl; + sif::warning << "UartTestClass::gpsPeriodic: read call failed with error [" << errno << ", " + << strerror(errno) << "]" << std::endl; break; } else if (bytesRead >= static_cast(recBuf.size())) { - sif::debug << "UartTestClass::performPeriodicAction: " + sif::debug << "UartTestClass::gpsPeriodic: " "recv buffer might not be large enough" << std::endl; } else if (bytesRead > 0) { @@ -109,7 +134,7 @@ void UartTestClass::gpsPeriodic() { #endif int result = lwgps_process(&gpsData, recBuf.data(), bytesRead); if (result == 0) { - sif::warning << "UartTestClass::performPeriodicAction: lwgps_process error" << std::endl; + sif::warning << "UartTestClass::gpsPeriodic: lwgps_process error" << std::endl; } recvCnt++; if (recvCnt == 6) { @@ -127,6 +152,114 @@ void UartTestClass::gpsPeriodic() { } void UartTestClass::scexInit() { + if (scexMode == ScexModes::SIMPLE) { + scexSimpleInit(); + } else { + if (reader == nullptr) { + sif::warning << "UartTestClass::scexInit: Reader invalid" << std::endl; + return; + } +#if defined(RASPBERRY_PI) + std::string devname = "/dev/serial0"; +#else + std::string devname = "/dev/ul-scex"; +#endif + uartCookie = new UartCookie(this->getObjectId(), devname, UartBaudRate::RATE_57600, 4096); + reader->setDebugMode(false); + ReturnValue_t result = reader->initializeInterface(uartCookie); + if (result != OK) { + sif::warning << "UartTestClass::scexInit: Initializing SCEX reader " + "UART IF failed" + << std::endl; + } + } +} + +void UartTestClass::scexPeriodic() { + using namespace std; + using namespace scex; + + if (scexMode == ScexModes::SIMPLE) { + scexSimplePeriodic(); + } else { + if (reader == nullptr) { + return; + } + if (not cmdSent) { + size_t len = 0; + prepareScexCmd(currCmd, false, cmdBuf.data(), &len); + reader->sendMessage(uartCookie, cmdBuf.data(), len); + cmdSent = true; + cmdDone = false; + } + if (cmdSent and not cmdDone) { + uint8_t* decodedPacket = nullptr; + size_t len = 0; + do { + ReturnValue_t result = reader->readReceivedMessage(uartCookie, &decodedPacket, &len); + if (len == 0) { + break; + } + ScexHelper helper; + const uint8_t* helperPtr = decodedPacket; + result = helper.deSerialize(&helperPtr, &len); + if (result == ScexHelper::INVALID_CRC) { + sif::warning << "UartTestClass::scexPeriodic: CRC invalid" << std::endl; + } + sif::info << helper << endl; + + // ping + // if ping cmd + if (helper.getCmd() == PING) { + ofstream out("/tmp/scex-ping.bin", ofstream::binary); + if (out.bad()) { + sif::warning << "bad" << std::endl; + } + out << helper; + } + // fram + if (helper.getCmd() == FRAM) { + if (not fileNameSet) { + fileId = random_string(6); + fileName = "/tmp/scex-fram_" + fileId + ".bin"; + fileNameSet = true; + } + if (helper.getPacketCounter() == 1) { + // countdown starten + finishCountdown.resetTimer(); + ofstream out(fileName, + ofstream::binary); // neues file anlegen + } else { + ofstream out(fileName, + ofstream::binary | ofstream::app); // an bestehendes file appenden + out << helper; + } + + if (finishCountdown.hasTimedOut()) { + triggerEvent(scex::EXPERIMENT_TIMEDOUT, currCmd, 0); + reader->finish(); + sif::warning << "UartTestClass::scexPeriodic: Reader timeout" << endl; + cmdDone = true; + fileNameSet = false; + } + } + + if (helper.getPacketCounter() == helper.getTotalPacketCounter()) { + reader->finish(); + sif::info << "UartTestClass::scexPeriodic: Reader is finished" << endl; + cmdDone = true; + fileNameSet = false; + if (helper.getCmd() == scex::Cmds::PING) { + cmdSent = false; + fileNameSet = true; // to not generate everytime new file + } + } + } while (len > 0); + } + } +} + +void UartTestClass::scexSimpleInit() { #if defined(RASPBERRY_PI) std::string devname = "/dev/serial0"; #else @@ -135,8 +268,8 @@ void UartTestClass::scexInit() { /* Get file descriptor */ serialPort = open(devname.c_str(), O_RDWR); if (serialPort < 0) { - sif::warning << "open call failed with error [" << errno << ", " << strerror(errno) - << std::endl; + sif::warning << "UartTestClass::scexSimpleInit: Open call failed with error [" << errno << ", " + << strerror(errno) << std::endl; return; } // Setting up UART parameters @@ -152,74 +285,117 @@ void UartTestClass::scexInit() { // Non-blocking mode, read until either line is 0.1 second idle or maximum of 255 bytes are // received in one go - tty.c_cc[VTIME] = 1; // In units of 0.1 seconds - tty.c_cc[VMIN] = 255; // Read up to 255 bytes + tty.c_cc[VTIME] = 0; // In units of 0.1 seconds + tty.c_cc[VMIN] = 0; // Read up to 255 bytes // Q7S UART Lite has fixed baud rate. For other linux systems, set baud rate here. #if !defined(XIPHOS_Q7S) if (cfsetispeed(&tty, B57600) != 0) { - sif::warning << "UartTestClass::scexInit: Setting baud rate failed" << std::endl; + sif::warning << "UartTestClass::scexSimpleInit: Setting baud rate failed" << std::endl; } #endif if (tcsetattr(serialPort, TCSANOW, &tty) != 0) { - sif::warning << "tcsetattr call failed with error [" << errno << ", " << strerror(errno) - << std::endl; + sif::warning << "UartTestClass::scexSimpleInit: tcsetattr call failed with error [" << errno + << ", " << strerror(errno) << std::endl; } // Flush received and unread data - tcflush(serialPort, TCIFLUSH); + tcflush(serialPort, TCIOFLUSH); } -void UartTestClass::scexPeriodic() { - sif::info << "UartTestClass::scexInit: Sending ping command to SCEX" << std::endl; - int result = prepareScexPing(); - if (result != 0) { - return; - }; - size_t bytesWritten = write(serialPort, cmdBuf.data(), encodedLen); - if (bytesWritten != encodedLen) { - sif::warning << "Sending ping command to solar experiment failed" << std::endl; - } - - // Read back reply immediately - int bytesRead = 0; - do { - bytesRead = read(serialPort, reinterpret_cast(recBuf.data()), - static_cast(recBuf.size())); - if (bytesRead < 0) { - sif::warning << "UartTestClass::performPeriodicAction: read call failed with error [" << errno - << ", " << strerror(errno) << "]" << std::endl; - break; - } else if (bytesRead >= static_cast(recBuf.size())) { - sif::debug << "UartTestClass::performPeriodicAction: recv buffer might not be large enough" - << std::endl; - } else if (bytesRead > 0) { - sif::info << "Received " << bytesRead - << " bytes from the Solar Cell Experiment:" << std::endl; - arrayprinter::print(recBuf.data(), bytesRead, OutputType::HEX, false); +void UartTestClass::scexSimplePeriodic() { + using namespace scex; + ReturnValue_t result = OK; + if (not cmdSent) { + // Flush received and unread data + tcflush(serialPort, TCIFLUSH); + uint8_t tmpCmdBuf[32] = {}; + size_t len = 0; + sif::info << "UartTestClass::scexSimplePeriodic: Sending command to SCEX" << std::endl; + prepareScexCmd(currCmd, false, tmpCmdBuf, &len); + result = dleEncoder.encode(tmpCmdBuf, len, cmdBuf.data(), cmdBuf.size(), &encodedLen, true); + if (result != OK) { + sif::warning << "UartTestClass::scexSimplePeriodic: Encoding failed" << std::endl; + return; } - } while (bytesRead > 0); + if (result != 0) { + return; + }; + size_t bytesWritten = write(serialPort, cmdBuf.data(), encodedLen); + if (bytesWritten != encodedLen) { + sif::warning + << "UartTestClass::scexSimplePeriodic: Sending command to solar experiment failed" + << std::endl; + } + cmdSent = true; + cmdDone = false; + } + if (not cmdDone) { + // Read back reply immediately + int bytesRead = 0; + do { + bytesRead = read(serialPort, reinterpret_cast(recBuf.data()), + static_cast(recBuf.size())); + if (bytesRead == 0) { + sif::warning << "UartTestClass::scexSimplePeriodic: Reading SCEX: Timeout or no bytes read" + << std::endl; + } else if (bytesRead < 0) { + sif::warning << "UartTestClass::scexSimplePeriodic: read call failed with error [" << errno + << ", " << strerror(errno) << "]" << std::endl; + break; + } else if (bytesRead >= static_cast(recBuf.size())) { + sif::debug << "UartTestClass::scexSimplePeriodic: recv buffer might not be large " + "enough, bytes read:" + << bytesRead << std::endl; + } else if (bytesRead > 0) { + dleParser->passData(recBuf.data(), bytesRead); + if (currCmd == Cmds::PING) { + cmdDone = true; + cmdSent = false; + } + } + } while (bytesRead > 0); + } } -int UartTestClass::prepareScexPing() { - std::array tmpCmdBuf = {}; - // Send ping command - tmpCmdBuf[0] = scex::CMD_PING; +int UartTestClass::prepareScexCmd(scex::Cmds cmd, bool tempCheck, uint8_t* cmdBuf, size_t* len) { + using namespace scex; + // Send command + cmdBuf[0] = scex::createCmdByte(cmd, false); // These two fields are the packet counter and the total packet count. Those are 1 and 1 for each // telecommand so far - tmpCmdBuf[1] = 1; - tmpCmdBuf[2] = 1; + cmdBuf[1] = 1; + cmdBuf[2] = 1; uint16_t userDataLen = 0; - tmpCmdBuf[3] = (userDataLen >> 8) & 0xff; - tmpCmdBuf[4] = userDataLen & 0xff; - uint16_t crc = CRC::crc16ccitt(tmpCmdBuf.data(), 5); - tmpCmdBuf[5] = (crc >> 8) & 0xff; - tmpCmdBuf[6] = crc & 0xff; - ReturnValue_t result = - dleEncoder.encode(tmpCmdBuf.data(), 7, cmdBuf.data(), cmdBuf.size(), &encodedLen, true); - if (result != returnvalue::OK) { - sif::warning << "UartTestClass::scexInit: Encoding failed" << std::endl; - return -1; - } + cmdBuf[3] = (userDataLen >> 8) & 0xff; + cmdBuf[4] = userDataLen & 0xff; + uint16_t crc = CRC::crc16ccitt(cmdBuf, 5); + cmdBuf[5] = (crc >> 8) & 0xff; + cmdBuf[6] = crc & 0xff; + *len = 7; return 0; } + +void UartTestClass::handleFoundDlePacket(uint8_t* packet, size_t len) { + sif::info << "UartTestClass::handleFoundDlePacket: Detected DLE encoded packet with decoded size " + << len << std::endl; +} + +std::string UartTestClass::random_string(std::string::size_type length) { + static auto& chrs = + "0123456789" + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + thread_local static std::mt19937 rg{std::random_device{}()}; + thread_local static std::uniform_int_distribution pick(0, + sizeof(chrs) - 2); + + std::string s; + + s.reserve(length); + + while (length--) s += chrs[pick(rg)]; + + return s; +} diff --git a/linux/boardtest/UartTestClass.h b/linux/boardtest/UartTestClass.h index 786d01ce..14d74322 100644 --- a/linux/boardtest/UartTestClass.h +++ b/linux/boardtest/UartTestClass.h @@ -1,14 +1,22 @@ #ifndef LINUX_BOARDTEST_UARTTESTCLASS_H_ #define LINUX_BOARDTEST_UARTTESTCLASS_H_ +#include #include +#include +#include +#include #include // Contains POSIX terminal control definitions #include #include "lwgps/lwgps.h" +#include "mission/devices/devicedefinitions/ScexDefinitions.h" #include "test/testtasks/TestTask.h" +class ScexUartReader; +class ScexDleParser; + class UartTestClass : public TestTask { public: UartTestClass(object_id_t objectId); @@ -24,20 +32,42 @@ class UartTestClass : public TestTask { SCEX }; + enum ScexModes { SIMPLE, READER_TASK } scexMode; + void gpsInit(); void gpsPeriodic(); void scexInit(); void scexPeriodic(); - int prepareScexPing(); + int prepareScexCmd(scex::Cmds cmd, bool tempCheck, uint8_t* cmdBuf, size_t* len); + + void scexSimplePeriodic(); + void scexSimpleInit(); + + static void foundDlePacketHandler(const DleParser::Context& ctx); + void handleFoundDlePacket(uint8_t* packet, size_t len); + std::string random_string(std::string::size_type length); + + std::string fileId = ""; + std::string fileName = ""; + bool fileNameSet = false; + Countdown finishCountdown = Countdown(180 * 1000); + bool cmdSent = false; + bool cmdDone = false; + scex::Cmds currCmd = scex::Cmds::PING; TestModes mode = TestModes::GPS; DleEncoder dleEncoder = DleEncoder(); + UartCookie* uartCookie = nullptr; size_t encodedLen = 0; lwgps_t gpsData = {}; struct termios tty = {}; int serialPort = 0; + bool startFound = false; + ScexUartReader* reader = nullptr; std::array cmdBuf = {}; std::array recBuf = {}; + ScexDleParser* dleParser; + scex::Cmds cmdHelper = scex::Cmds::INVALID; uint8_t recvCnt = 0; }; diff --git a/linux/devices/CMakeLists.txt b/linux/devices/CMakeLists.txt index b68d1c4e..de0ea1da 100644 --- a/linux/devices/CMakeLists.txt +++ b/linux/devices/CMakeLists.txt @@ -2,7 +2,9 @@ if(EIVE_BUILD_GPSD_GPS_HANDLER) target_sources(${OBSW_NAME} PRIVATE GPSHyperionLinuxController.cpp) endif() -target_sources(${OBSW_NAME} PRIVATE Max31865RtdLowlevelHandler.cpp) +target_sources( + ${OBSW_NAME} PRIVATE Max31865RtdLowlevelHandler.cpp ScexUartReader.cpp + ScexDleParser.cpp ScexHelper.cpp) add_subdirectory(ploc) add_subdirectory(startracker) diff --git a/linux/devices/ScexDleParser.cpp b/linux/devices/ScexDleParser.cpp new file mode 100644 index 00000000..a2f01396 --- /dev/null +++ b/linux/devices/ScexDleParser.cpp @@ -0,0 +1,7 @@ +#include "ScexDleParser.h" + +ScexDleParser::ScexDleParser(SimpleRingBuffer &decodeRingBuf, DleEncoder &decoder, + BufPair encodedBuf, BufPair decodedBuf) + : DleParser(decodeRingBuf, decoder, encodedBuf, decodedBuf){}; + +ScexDleParser::~ScexDleParser(){}; diff --git a/linux/devices/ScexDleParser.h b/linux/devices/ScexDleParser.h new file mode 100644 index 00000000..c91a44a3 --- /dev/null +++ b/linux/devices/ScexDleParser.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +class ScexDleParser : public DleParser { + public: + ScexDleParser(SimpleRingBuffer &decodeRingBuf, DleEncoder &decoder, BufPair encodedBuf, + BufPair decodedBuf); + + virtual ~ScexDleParser(); + + private: +}; diff --git a/linux/devices/ScexHelper.cpp b/linux/devices/ScexHelper.cpp new file mode 100644 index 00000000..bce5fbda --- /dev/null +++ b/linux/devices/ScexHelper.cpp @@ -0,0 +1,86 @@ +#include "ScexHelper.h" + +#include + +#include "fsfw/serviceinterface.h" + +using namespace returnvalue; + +ScexHelper::ScexHelper() {} + +ReturnValue_t ScexHelper::serialize(uint8_t** buffer, size_t* size, size_t maxSize, + Endianness streamEndianness) const { + return FAILED; +} + +size_t ScexHelper::getSerializedSize() const { return totalPacketLen; } + +ReturnValue_t ScexHelper::deSerialize(const uint8_t** buffer, size_t* size, + Endianness streamEndianness) { + if (buffer == nullptr or size == nullptr) { + return FAILED; + } + if (*size < 7) { + return STREAM_TOO_SHORT; + } + start = *buffer; + cmdByteRaw = **buffer; + cmd = static_cast((cmdByteRaw >> 1) & 0b11111); + + *buffer += 1; + packetCounter = **buffer; + + *buffer += 1; + totalPacketCounter = **buffer; + + *buffer += 1; + payloadLen = (**buffer << 8) | *(*buffer + 1); + + *buffer += 2; + payloadStart = *buffer; + totalPacketLen = payloadLen + scex::HEADER_LEN + scex::CRC_LEN; + if (totalPacketLen >= *size) { + return STREAM_TOO_SHORT; + } + *buffer += payloadLen; + crc = (**buffer << 8) | *(*buffer + 1); + if (CRC::crc16ccitt(start, totalPacketLen) != 0) { + return INVALID_CRC; + } + return OK; +} + +scex::Cmds ScexHelper::getCmd() const { return cmd; } + +uint8_t ScexHelper::getCmdByteRaw() const { return cmdByteRaw; } + +uint16_t ScexHelper::getCrc() const { return crc; } + +size_t ScexHelper::getExpectedPacketLen() const { return totalPacketLen; } + +uint8_t ScexHelper::getPacketCounter() const { return packetCounter; } + +uint16_t ScexHelper::getPayloadLen() const { return payloadLen; } + +const uint8_t* ScexHelper::getStart() const { return start; } + +uint8_t ScexHelper::getTotalPacketCounter() const { return totalPacketCounter; } + +std::ostream& operator<<(std::ostream& os, const ScexHelper& h) { + using namespace std; + sif::info << "Command Byte Raw: 0x" << std::setw(2) << std::setfill('0') << std::hex + << (int)h.cmdByteRaw << " | Command: 0x" << std::setw(2) << std::setfill('0') + << std::hex << static_cast(h.cmd) << std::dec << std::endl; + sif::info << "PacketCounter: " << h.packetCounter << endl; + sif::info << "TotalPacketCount: " << h.totalPacketCounter << endl; + sif::info << "PayloadLength: " << h.payloadLen << endl; + sif::info << "TotalPacketLength: " << h.totalPacketLen; + + return os; +} + +std::ofstream& operator<<(std::ofstream& of, const ScexHelper& h) { + of.write(reinterpret_cast(h.start), h.getSerializedSize()); + + return of; +} diff --git a/linux/devices/ScexHelper.h b/linux/devices/ScexHelper.h new file mode 100644 index 00000000..f2adf617 --- /dev/null +++ b/linux/devices/ScexHelper.h @@ -0,0 +1,46 @@ +#ifndef LINUX_DEVICES_SCEXHELPER_H_ +#define LINUX_DEVICES_SCEXHELPER_H_ +#include +#include + +#include +#include +#include +#include + +class ScexHelper : public SerializeIF { + public: + static const ReturnValue_t INVALID_CRC = returnvalue::makeCode(0, 2); + + ScexHelper(); + ReturnValue_t serialize(uint8_t **buffer, size_t *size, size_t maxSize, + Endianness streamEndianness) const override; + + size_t getSerializedSize() const override; + ReturnValue_t deSerialize(const uint8_t **buffer, size_t *size, + Endianness streamEndianness = Endianness::BIG) override; + friend std::ostream &operator<<(std::ostream &os, const ScexHelper &h); + friend std::ofstream &operator<<(std::ofstream &os, const ScexHelper &h); + + scex::Cmds getCmd() const; + uint8_t getCmdByteRaw() const; + uint16_t getCrc() const; + size_t getExpectedPacketLen() const; + uint8_t getPacketCounter() const; + uint16_t getPayloadLen() const; + const uint8_t *getStart() const; + uint8_t getTotalPacketCounter() const; + + private: + const uint8_t *start = nullptr; + uint16_t crc = 0; + uint8_t cmdByteRaw = 0; + scex::Cmds cmd = scex::Cmds::INVALID; + int packetCounter = 0; + int totalPacketCounter = 0; + uint16_t payloadLen = 0; + const uint8_t *payloadStart = 0; + size_t totalPacketLen = 0; +}; + +#endif /* LINUX_DEVICES_SCEXHELPER_H_ */ diff --git a/linux/devices/ScexUartReader.cpp b/linux/devices/ScexUartReader.cpp new file mode 100644 index 00000000..24461395 --- /dev/null +++ b/linux/devices/ScexUartReader.cpp @@ -0,0 +1,236 @@ +#include "ScexUartReader.h" + +#include // Contains file controls like O_RDWR +#include +#include +#include +#include +#include +#include +#include // write(), read(), close() + +#include // Error integer and strerror() function +#include + +#include "OBSWConfig.h" + +using namespace returnvalue; + +ScexUartReader::ScexUartReader(object_id_t objectId) + : SystemObject(objectId), + decodeRingBuf(4096, true), + ipcRingBuf(200 * 2048, true), + ipcQueue(200), + dleParser(decodeRingBuf, dleEncoder, {encodedBuf.data(), encodedBuf.size()}, + {decodedBuf.data(), decodedBuf.size()}) { + semaphore = SemaphoreFactory::instance()->createBinarySemaphore(); + semaphore->acquire(); + lock = MutexFactory::instance()->createMutex(); +} + +ReturnValue_t ScexUartReader::performOperation(uint8_t operationCode) { + lock->lockMutex(); + state = States::IDLE; + lock->unlockMutex(); + while (true) { + semaphore->acquire(); + int bytesRead = 0; + // debugMode = true; + while (true) { + bytesRead = read(serialPort, reinterpret_cast(recBuf.data()), + static_cast(recBuf.size())); + if (bytesRead == 0) { + { + MutexGuard mg(lock); + if (state == States::FINISH) { + dleParser.reset(); + // Flush received and unread data + tcflush(serialPort, TCIOFLUSH); + state = States::IDLE; + break; + } + } + ReturnValue_t result = returnvalue::OK; + // Can be used to read frame, parity and overrun errors + // serial_icounter_struct icounter{}; + // uart::readCountersAndErrors(serialPort, icounter); + while (result != DleParser::NO_PACKET_FOUND) { + result = tryDleParsing(); + } + + TaskFactory::delayTask(400); + } else if (bytesRead < 0) { + sif::warning << "ScexUartReader::performOperation: read call failed with error [" << errno + << ", " << strerror(errno) << "]" << std::endl; + break; + } else if (bytesRead >= static_cast(recBuf.size())) { + sif::error << "ScexUartReader::performOperation: Receive buffer too small for " << bytesRead + << " bytes" << std::endl; + } else if (bytesRead > 0) { + if (debugMode) { + sif::info << "Received " << bytesRead + << " bytes from the Solar Cell Experiment:" << std::endl; + } + ReturnValue_t result = dleParser.passData(recBuf.data(), bytesRead); + if (result != OK) { + sif::warning << "ScexUartReader::performOperation: Passing data to DLE parser failed" + << std::endl; + } + result = tryDleParsing(); + } + }; + } + return OK; +} + +ReturnValue_t ScexUartReader::initializeInterface(CookieIF *cookie) { + UartCookie *uartCookie = dynamic_cast(cookie); + if (uartCookie == nullptr) { + return FAILED; + } + std::string devname = uartCookie->getDeviceFile(); + /* Get file descriptor */ + serialPort = open(devname.c_str(), O_RDWR); + if (serialPort < 0) { + sif::warning << "ScexUartReader::initializeInterface: open call failed with error [" << errno + << ", " << strerror(errno) << std::endl; + return FAILED; + } + // Setting up UART parameters + tty.c_cflag &= ~PARENB; // Clear parity bit + if (uartCookie->getStopBits() == StopBits::TWO_STOP_BITS) { + // Use two stop bits + tty.c_cflag |= CSTOPB; + } else { + // Clear stop field, only one stop bit used in communication + tty.c_cflag &= ~CSTOPB; + } + + tty.c_cflag &= ~CSIZE; // Clear all the size bits + tty.c_cflag |= CS8; // 8 bits per byte + tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control + tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1) + + // Use non-canonical mode and clear echo flag + tty.c_lflag &= ~(ICANON | ECHO); + + // Non-blocking mode, use polling + tty.c_cc[VTIME] = 0; + tty.c_cc[VMIN] = 0; + + // The SCEX experiment has a fixed baud rate. + if (cfsetispeed(&tty, B38400) != 0) { + sif::warning << "ScexUartReader::initializeInterface: Setting baud rate failed" << std::endl; + } + if (tcsetattr(serialPort, TCSANOW, &tty) != 0) { + sif::warning << "ScexUartReader::initializeInterface: tcsetattr call failed with error [" + << errno << ", " << strerror(errno) << std::endl; + } + // Flush received and unread data + tcflush(serialPort, TCIOFLUSH); + return OK; +} + +ReturnValue_t ScexUartReader::sendMessage(CookieIF *cookie, const uint8_t *sendData, + size_t sendLen) { + ReturnValue_t result; + if (sendData == nullptr or sendLen == 0) { + return FAILED; + } + lock->lockMutex(); + if (state == States::NOT_READY or state == States::RUNNING) { + lock->unlockMutex(); + return FAILED; + } + tcflush(serialPort, TCIFLUSH); + state = States::RUNNING; + lock->unlockMutex(); + + result = semaphore->release(); + if (result != OK) { + std::cout << "ScexUartReader::sendMessage: Releasing semaphore failed" << std::endl; + } + size_t encodedLen = 0; + result = dleEncoder.encode(sendData, sendLen, cmdbuf.data(), cmdbuf.size(), &encodedLen, true); + if (result != OK) { + sif::warning << "ScexUartReader::sendMessage: Encoding failed" << std::endl; + return FAILED; + } + size_t bytesWritten = write(serialPort, cmdbuf.data(), encodedLen); + if (bytesWritten != encodedLen) { + sif::warning << "ScexUartReader::sendMessage: Sending ping command to solar experiment failed" + << std::endl; + return FAILED; + } + + return OK; +} + +ReturnValue_t ScexUartReader::getSendSuccess(CookieIF *cookie) { return OK; } + +ReturnValue_t ScexUartReader::requestReceiveMessage(CookieIF *cookie, size_t requestLen) { + return OK; +} + +void ScexUartReader::setDebugMode(bool enable) { this->debugMode = enable; } + +ReturnValue_t ScexUartReader::finish() { + MutexGuard mg(lock); + if (state == States::IDLE) { + return FAILED; + } + state = States::FINISH; + return OK; +} + +void ScexUartReader::handleFoundDlePacket(uint8_t *packet, size_t len) { + MutexGuard mg(lock); + ReturnValue_t result = ipcQueue.insert(len); + if (result != OK) { + sif::warning << "ScexUartReader::handleFoundDlePacket: IPCQueue error" << std::endl; + } + result = ipcRingBuf.writeData(packet, len); + if (result != OK) { + sif::warning << "ScexUartReader::handleFoundDlePacket: IPCRingBuf error" << std::endl; + } +} + +ReturnValue_t ScexUartReader::tryDleParsing() { + size_t bytesRead = 0; + ReturnValue_t result = dleParser.parseRingBuf(bytesRead); + if (result == returnvalue::OK) { + // Packet found, advance read pointer. + auto &decodedPacket = dleParser.getContext().decodedPacket; + handleFoundDlePacket(decodedPacket.first, decodedPacket.second); + dleParser.confirmBytesRead(bytesRead); + } else if (result != DleParser::NO_PACKET_FOUND) { + sif::warning << "ScexUartReader::performOperation: Possible packet loss" << std::endl; + // Markers found at wrong place + // which might be a hint for a possibly lost packet. + dleParser.defaultErrorHandler(); + dleParser.confirmBytesRead(bytesRead); + } + return result; +} + +void ScexUartReader::reset() { + lock->lockMutex(); + state = States::FINISH; + lock->unlockMutex(); +} + +ReturnValue_t ScexUartReader::readReceivedMessage(CookieIF *cookie, uint8_t **buffer, + size_t *size) { + MutexGuard mg(lock); + if (ipcQueue.empty()) { + *size = 0; + return OK; + } + ipcQueue.retrieve(size); + *buffer = ipcBuffer.data(); + ReturnValue_t result = ipcRingBuf.readData(ipcBuffer.data(), *size, true); + if (result != OK) { + sif::warning << "ScexUartReader::readReceivedMessage: Reading RingBuffer failed" << std::endl; + } + return OK; +} diff --git a/linux/devices/ScexUartReader.h b/linux/devices/ScexUartReader.h new file mode 100644 index 00000000..8f5454b2 --- /dev/null +++ b/linux/devices/ScexUartReader.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include // Contains POSIX terminal control definitions + +class SemaphoreIF; +class MutexIF; + +class ScexUartReader : public SystemObject, + public ExecutableObjectIF, + public DeviceCommunicationIF { + friend class UartTestClass; + + public: + enum class States { NOT_READY, IDLE, RUNNING, FINISH }; + ScexUartReader(object_id_t objectId); + + void reset(); + ReturnValue_t finish(); + void setDebugMode(bool enable); + + private: + SemaphoreIF *semaphore; + bool debugMode = false; + MutexIF *lock; + int serialPort = 0; + States state = States::IDLE; + struct termios tty = {}; + bool doFinish = false; + DleEncoder dleEncoder = DleEncoder(); + SimpleRingBuffer decodeRingBuf; + + std::array cmdbuf = {}; + std::array recBuf = {}; + std::array encodedBuf = {}; + std::array decodedBuf = {}; + std::array ipcBuffer = {}; + SimpleRingBuffer ipcRingBuf; + DynamicFIFO ipcQueue; + ScexDleParser dleParser; + + static void foundDlePacketHandler(const DleParser::Context &ctx); + void handleFoundDlePacket(uint8_t *packet, size_t len); + ReturnValue_t tryDleParsing(); + + ReturnValue_t performOperation(uint8_t operationCode = 0) override; + + // DeviceCommunicationIF implementation + ReturnValue_t initializeInterface(CookieIF *cookie) override; + ReturnValue_t sendMessage(CookieIF *cookie, const uint8_t *sendData, size_t sendLen) override; + ReturnValue_t getSendSuccess(CookieIF *cookie) override; + ReturnValue_t requestReceiveMessage(CookieIF *cookie, size_t requestLen) override; + ReturnValue_t readReceivedMessage(CookieIF *cookie, uint8_t **buffer, size_t *size) override; +}; diff --git a/linux/fsfwconfig/events/translateEvents.cpp b/linux/fsfwconfig/events/translateEvents.cpp index 882bbefd..18b536ce 100644 --- a/linux/fsfwconfig/events/translateEvents.cpp +++ b/linux/fsfwconfig/events/translateEvents.cpp @@ -1,7 +1,7 @@ /** - * @brief Auto-generated event translation file. Contains 222 translations. + * @brief Auto-generated event translation file. Contains 225 translations. * @details - * Generated on: 2022-10-10 11:10:02 + * Generated on: 2022-10-10 17:38:50 */ #include "translateEvents.h" @@ -218,6 +218,9 @@ const char *ALLOC_FAILURE_STRING = "ALLOC_FAILURE"; const char *REBOOT_SW_STRING = "REBOOT_SW"; const char *REBOOT_MECHANISM_TRIGGERED_STRING = "REBOOT_MECHANISM_TRIGGERED"; const char *REBOOT_HW_STRING = "REBOOT_HW"; +const char *MISSING_PACKET_STRING = "MISSING_PACKET"; +const char *EXPERIMENT_TIMEDOUT_STRING = "EXPERIMENT_TIMEDOUT"; +const char *MULTI_PACKET_COMMAND_DONE_STRING = "MULTI_PACKET_COMMAND_DONE"; const char *SET_CONFIGFILEVALUE_FAILED_STRING = "SET_CONFIGFILEVALUE_FAILED"; const char *GET_CONFIGFILEVALUE_FAILED_STRING = "GET_CONFIGFILEVALUE_FAILED"; const char *INSERT_CONFIGFILEVALUE_FAILED_STRING = "INSERT_CONFIGFILEVALUE_FAILED"; @@ -652,15 +655,21 @@ const char *translateEvents(Event event) { return REBOOT_MECHANISM_TRIGGERED_STRING; case (13703): return REBOOT_HW_STRING; + case (13800): + return MISSING_PACKET_STRING; case (13801): - return SET_CONFIGFILEVALUE_FAILED_STRING; + return EXPERIMENT_TIMEDOUT_STRING; case (13802): + return MULTI_PACKET_COMMAND_DONE_STRING; + case (13901): + return SET_CONFIGFILEVALUE_FAILED_STRING; + case (13902): return GET_CONFIGFILEVALUE_FAILED_STRING; - case (13803): + case (13903): return INSERT_CONFIGFILEVALUE_FAILED_STRING; - case (13804): + case (13904): return WRITE_CONFIGFILE_FAILED_STRING; - case (13805): + case (13905): return READ_CONFIGFILE_FAILED_STRING; default: return "UNKNOWN_EVENT"; diff --git a/linux/fsfwconfig/objects/systemObjectList.h b/linux/fsfwconfig/objects/systemObjectList.h index 4ba303a8..51f6b919 100644 --- a/linux/fsfwconfig/objects/systemObjectList.h +++ b/linux/fsfwconfig/objects/systemObjectList.h @@ -49,6 +49,9 @@ enum sourceObjects : uint32_t { UART_COM_IF = 0x49030003, SPI_MAIN_COM_IF = 0x49020004, GPIO_IF = 0x49010005, + SCEX_UART_READER = 0x49010006, + + /* Custom device handler */ SPI_RW_COM_IF = 0x49020005, /* 0x54 ('T') for test handlers */ diff --git a/linux/fsfwconfig/objects/translateObjects.cpp b/linux/fsfwconfig/objects/translateObjects.cpp index 0b2c93ab..582ddfc7 100644 --- a/linux/fsfwconfig/objects/translateObjects.cpp +++ b/linux/fsfwconfig/objects/translateObjects.cpp @@ -1,8 +1,8 @@ /** * @brief Auto-generated object translation file. * @details - * Contains 138 translations. - * Generated on: 2022-10-10 11:10:02 + * Contains 140 translations. + * Generated on: 2022-10-10 17:38:50 */ #include "translateObjects.h" @@ -55,6 +55,7 @@ const char *PTME_CONFIG_STRING = "PTME_CONFIG"; const char *PLOC_MPSOC_HANDLER_STRING = "PLOC_MPSOC_HANDLER"; const char *PLOC_SUPERVISOR_HANDLER_STRING = "PLOC_SUPERVISOR_HANDLER"; const char *PLOC_SUPERVISOR_HELPER_STRING = "PLOC_SUPERVISOR_HELPER"; +const char *SCEX_STRING = "SCEX"; const char *SOLAR_ARRAY_DEPL_HANDLER_STRING = "SOLAR_ARRAY_DEPL_HANDLER"; const char *HEATER_HANDLER_STRING = "HEATER_HANDLER"; const char *TMP1075_HANDLER_1_STRING = "TMP1075_HANDLER_1"; @@ -78,6 +79,7 @@ const char *RTD_15_IC18_IMTQ_STRING = "RTD_15_IC18_IMTQ"; const char *SYRLINKS_HK_HANDLER_STRING = "SYRLINKS_HK_HANDLER"; const char *ARDUINO_COM_IF_STRING = "ARDUINO_COM_IF"; const char *GPIO_IF_STRING = "GPIO_IF"; +const char *SCEX_UART_READER_STRING = "SCEX_UART_READER"; const char *SPI_MAIN_COM_IF_STRING = "SPI_MAIN_COM_IF"; const char *SPI_RW_COM_IF_STRING = "SPI_RW_COM_IF"; const char *SPI_RTD_COM_IF_STRING = "SPI_RTD_COM_IF"; @@ -245,6 +247,8 @@ const char *translateObject(object_id_t object) { return PLOC_SUPERVISOR_HANDLER_STRING; case 0x44330017: return PLOC_SUPERVISOR_HELPER_STRING; + case 0x44330032: + return SCEX_STRING; case 0x444100A2: return SOLAR_ARRAY_DEPL_HANDLER_STRING; case 0x444100A4: @@ -291,6 +295,8 @@ const char *translateObject(object_id_t object) { return ARDUINO_COM_IF_STRING; case 0x49010005: return GPIO_IF_STRING; + case 0x49010006: + return SCEX_UART_READER_STRING; case 0x49020004: return SPI_MAIN_COM_IF_STRING; case 0x49020005: diff --git a/linux/obc/PdecHandler.cpp b/linux/obc/PdecHandler.cpp index fcfdd2f4..6199853f 100644 --- a/linux/obc/PdecHandler.cpp +++ b/linux/obc/PdecHandler.cpp @@ -6,6 +6,7 @@ #include #include +#include "OBSWConfig.h" #include "fsfw/ipc/QueueFactory.h" #include "fsfw/objectmanager/ObjectManager.h" #include "fsfw/serviceinterface/ServiceInterface.h" diff --git a/mission/devices/CMakeLists.txt b/mission/devices/CMakeLists.txt index 1a9b2efd..56184ea4 100644 --- a/mission/devices/CMakeLists.txt +++ b/mission/devices/CMakeLists.txt @@ -19,4 +19,7 @@ target_sources( max1227.cpp SusHandler.cpp PayloadPcduHandler.cpp - SolarArrayDeploymentHandler.cpp) + SolarArrayDeploymentHandler.cpp + ScexDeviceHandler.cpp) + +add_subdirectory(devicedefinitions) diff --git a/mission/devices/IMTQHandler.cpp b/mission/devices/IMTQHandler.cpp index 52df8c97..6ab5ed48 100644 --- a/mission/devices/IMTQHandler.cpp +++ b/mission/devices/IMTQHandler.cpp @@ -20,7 +20,7 @@ IMTQHandler::IMTQHandler(object_id_t objectId, object_id_t comIF, CookieIF* comC posZselfTestDataset(this), negZselfTestDataset(this), switcher(pwrSwitcher) { - if (comCookie == NULL) { + if (comCookie == nullptr) { sif::error << "IMTQHandler: Invalid com cookie" << std::endl; } } diff --git a/mission/devices/PayloadPcduHandler.cpp b/mission/devices/PayloadPcduHandler.cpp index 3a027b9a..56822bd5 100644 --- a/mission/devices/PayloadPcduHandler.cpp +++ b/mission/devices/PayloadPcduHandler.cpp @@ -491,8 +491,8 @@ void PayloadPcduHandler::checkAdcValues() { void PayloadPcduHandler::checkJsonFileInit() { if (not jsonFileInitComplete) { - sd::SdCard activeSd = sdcMan->getActiveSdCard(); - if (sdcMan->isSdCardUsable(activeSd)) { + auto activeSd = sdcMan->getActiveSdCard(); + if (activeSd and sdcMan->isSdCardUsable(activeSd.value())) { params.initialize(sdcMan->getCurrentMountPrefix()); jsonFileInitComplete = true; } diff --git a/mission/devices/ScexDeviceHandler.cpp b/mission/devices/ScexDeviceHandler.cpp new file mode 100644 index 00000000..c49192a7 --- /dev/null +++ b/mission/devices/ScexDeviceHandler.cpp @@ -0,0 +1,369 @@ +#include "ScexDeviceHandler.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "fsfw/globalfunctions/CRC.h" +#include "mission/devices/devicedefinitions/ScexDefinitions.h" + +using std::ofstream; +using namespace returnvalue; + +ScexDeviceHandler::ScexDeviceHandler(object_id_t objectId, ScexUartReader& reader, CookieIF* cookie, + SdCardMountedIF& sdcMan) + : DeviceHandlerBase(objectId, reader.getObjectId(), cookie), sdcMan(sdcMan), reader(reader) {} + +ScexDeviceHandler::~ScexDeviceHandler() {} + +void ScexDeviceHandler::doStartUp() { setMode(MODE_ON); } + +void ScexDeviceHandler::doShutDown() { + reader.reset(); + commandActive = false; + setMode(_MODE_POWER_DOWN); +} + +ReturnValue_t ScexDeviceHandler::buildNormalDeviceCommand(DeviceCommandId_t* id) { return OK; } + +ReturnValue_t ScexDeviceHandler::buildTransitionDeviceCommand(DeviceCommandId_t* id) { return OK; } + +ReturnValue_t ScexDeviceHandler::buildCommandFromCommand(DeviceCommandId_t deviceCommand, + const uint8_t* commandData, + size_t commandDataLen) { + using namespace scex; + + auto cmdTyped = static_cast(deviceCommand); + if (std::find(VALID_CMDS.begin(), VALID_CMDS.end(), deviceCommand) == VALID_CMDS.end()) { + return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED; + } + bool tempCheck = false; + if (commandDataLen == 1) { + tempCheck = commandData[0]; + } + if (commandActive) { + return DeviceHandlerIF::BUSY; + } + + switch (deviceCommand) { + case (PING): { + finishCountdown.setTimeout(SHORT_CD); + // countdown starten + finishCountdown.resetTimer(); + prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen, {nullptr, 0}, + tempCheck); + break; + } + case (EXP_STATUS_CMD): { + finishCountdown.setTimeout(SHORT_CD); + // countdown starten + finishCountdown.resetTimer(); + prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen, {nullptr, 0}, + tempCheck); + break; + } + case (ION_CMD): { + finishCountdown.setTimeout(SHORT_CD); + // countdown starten + finishCountdown.resetTimer(); + prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen, {nullptr, 0}, + tempCheck); + break; + } + case (TEMP_CMD): { + finishCountdown.setTimeout(SHORT_CD); + // countdown starten + finishCountdown.resetTimer(); + prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen, {nullptr, 0}, + tempCheck); + break; + } + case (FRAM): { + finishCountdown.setTimeout(LONG_CD); + // countdown starten + finishCountdown.resetTimer(); + if (debugMode) { + uint32_t remainingMillis = finishCountdown.getRemainingMillis(); + + sif::info << "ScexDeviceHandler::buildCommandFromCommand: RemainingMillis: " + << remainingMillis << std::endl; + } + + prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen, + {commandData + 1, commandDataLen - 1}, tempCheck); + updatePeriodicReply(true, deviceCommand); + finishAction(true, deviceCommand, OK); + break; + } + case (ONE_CELL): { + finishCountdown.setTimeout(LONG_CD); + // countdown starts + finishCountdown.resetTimer(); + prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen, + {commandData + 1, commandDataLen - 1}, tempCheck); + updatePeriodicReply(true, deviceCommand); + finishAction(true, deviceCommand, OK); + break; + } + case (ALL_CELLS_CMD): { + finishCountdown.setTimeout(LONG_CD); + // countdown starts + finishCountdown.resetTimer(); + prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen, + {commandData + 1, commandDataLen - 1}, tempCheck); + finishAction(true, deviceCommand, OK); + updatePeriodicReply(true, deviceCommand); + break; + } + default: { + return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED; + } + } + commandActive = true; + rawPacket = cmdBuf.data(); + return OK; +} + +void ScexDeviceHandler::fillCommandAndReplyMap() { + insertInCommandAndReplyMap(scex::Cmds::PING, 5, nullptr, 0, false, false, 0, &finishCountdown); + insertInCommandAndReplyMap(scex::Cmds::ION_CMD, 3, nullptr, 0, false, false, 0, &finishCountdown); + insertInCommandAndReplyMap(scex::Cmds::TEMP_CMD, 3, nullptr, 0, false, false, 0, + &finishCountdown); + insertInCommandAndReplyMap(scex::Cmds::EXP_STATUS_CMD, 3, nullptr, 0, false, false, 0, + &finishCountdown); + + insertInCommandAndReplyMap(scex::Cmds::ALL_CELLS_CMD, 0, nullptr, 0, true, false, + scex::Cmds::ALL_CELLS_CMD, &finishCountdown); + insertInCommandAndReplyMap(scex::Cmds::ONE_CELL, 0, nullptr, 0, true, false, scex::Cmds::ONE_CELL, + &finishCountdown); + insertInCommandAndReplyMap(scex::Cmds::FRAM, 0, nullptr, 0, true, false, scex::Cmds::FRAM, + &finishCountdown); + + insertInReplyMap(scex::Cmds::ERROR_REPLY, 3); +} + +ReturnValue_t ScexDeviceHandler::scanForReply(const uint8_t* start, size_t remainingSize, + DeviceCommandId_t* foundId, size_t* foundLen) { + size_t len = remainingSize; + ReturnValue_t result = helper.deSerialize(&start, &len); + + if (result == ScexHelper::INVALID_CRC) { + sif::warning << "ScexDeviceHandler::scanForReply: CRC invalid" << std::endl; + *foundLen = remainingSize; + } else { + result = handleValidReply(remainingSize, foundId, foundLen); + } + return result; +} + +ReturnValue_t ScexDeviceHandler::handleValidReply(size_t remSize, DeviceCommandId_t* foundId, + size_t* foundLen) { + using namespace scex; + ReturnValue_t result = OK; + + switch (helper.getCmd()) { + case (FRAM): { + if (debugMode) { + uint32_t remainingMillis = finishCountdown.getRemainingMillis(); + + sif::info << "ScexDeviceHandler::handleValidReply: RemMillis: " << remainingMillis + << std::endl; + } + result = APERIODIC_REPLY; + break; + } + case (ONE_CELL): { + result = APERIODIC_REPLY; + break; + } + case (ALL_CELLS_CMD): { + result = APERIODIC_REPLY; + break; + } + default: { + break; + } + } + *foundId = helper.getCmd(); + *foundLen = remSize; + return result; +} + +ReturnValue_t ScexDeviceHandler::interpretDeviceReply(DeviceCommandId_t id, const uint8_t* packet) { + using namespace scex; + + ReturnValue_t status = OK; + auto oneFileHandler = [&](std::string cmdName) { + fileId = date_time_string(); + std::ostringstream oss; + auto prefix = sdcMan.getCurrentMountPrefix(); + oss << prefix << "/scex/scex-" << cmdName << fileId << ".bin"; + fileName = oss.str(); + ofstream out(fileName, ofstream::binary); + if (out.bad()) { + sif::error << "ScexDeviceHandler::interpretDeviceReply: Could not open file " << fileName + << std::endl; + return FAILED; + } + out << helper; + return OK; + }; + auto multiFileHandler = [&](std::string cmdName) { + if ((helper.getPacketCounter() == 1) or (not fileNameSet)) { + fileId = date_time_string(); + std::ostringstream oss; + auto prefix = sdcMan.getCurrentMountPrefix(); + oss << prefix << "/scex/scex-" << cmdName << fileId << ".bin"; + fileName = oss.str(); + fileNameSet = true; + ofstream out(fileName, ofstream::binary); + if (out.bad()) { + sif::error << "ScexDeviceHandler::handleValidReply: Could not open file " << fileName + << std::endl; + return FAILED; + } + out << helper; + } else { + ofstream out(fileName, + ofstream::binary | ofstream::app); // append + if (out.bad()) { + sif::error << "ScexDeviceHandler::handleValidReply: Could not open file " << fileName + << std::endl; + return FAILED; + } + out << helper; + } + return OK; + }; + switch (id) { + case (PING): { + status = oneFileHandler("ping_"); + break; + } + case (ION_CMD): { + status = oneFileHandler("ion_"); + break; + } + case (TEMP_CMD): { + status = oneFileHandler("temp_"); + break; + } + case (EXP_STATUS_CMD): { + status = oneFileHandler("exp_status_"); + break; + } + case (FRAM): { + status = multiFileHandler("fram_"); + break; + } + case (ONE_CELL): { + status = multiFileHandler("one_cell_"); + break; + } + case (ALL_CELLS_CMD): { + status = multiFileHandler("multi_cell_"); + break; + } + default: + // Unknown DeviceCommand + return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED; + } + if (helper.getPacketCounter() == helper.getTotalPacketCounter()) { + reader.finish(); + commandActive = false; + if (id != PING) { + fileNameSet = false; + } + if (id == FRAM or id == ALL_CELLS_CMD or id == ONE_CELL) { + triggerEvent(MULTI_PACKET_COMMAND_DONE, id); + updatePeriodicReply(false, id); + } + } + if (debugMode) { + uint32_t remainingMillis = finishCountdown.getRemainingMillis(); + sif::info << __FILE__ << __func__ << "(" << __LINE__ << ") RemMillis: " << remainingMillis + << std::endl; + } + return status; +} + +void ScexDeviceHandler::performOperationHook() { + uint32_t remainingMillis = finishCountdown.getRemainingMillis(); + if (commandActive and finishCountdown.hasTimedOut()) { + triggerEvent(scex::EXPERIMENT_TIMEDOUT, currCmd, 0); + reader.finish(); + sif::warning << "ScexDeviceHandler::scanForReply: Reader timeout; RemMillis: " + << remainingMillis << std::endl; + fileNameSet = false; + commandActive = false; + } +} + +uint32_t ScexDeviceHandler::getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) { return OK; } + +ReturnValue_t ScexDeviceHandler::getSwitches(const uint8_t** switches, uint8_t* numberOfSwitches) { + if (switchId) { + *numberOfSwitches = 1; + *switches = &switchId.value(); + } + return OK; +} + +ReturnValue_t ScexDeviceHandler::initializeLocalDataPool(localpool::DataPool& localDataPoolMap, + LocalDataPoolManager& poolManager) { + return OK; +} + +std::string ScexDeviceHandler::date_time_string() { + using namespace std; + string date_time; + Clock::TimeOfDay_t tod; + Clock::getDateAndTime(&tod); + ostringstream oss(std::ostringstream::ate); + + if (tod.hour < 10) { + oss << tod.year << tod.month << tod.day << "_0" << tod.hour; + } else { + oss << tod.year << tod.month << tod.day << "_" << tod.hour; + } + if (tod.minute < 10) { + oss << 0 << tod.minute; + + } else { + oss << tod.minute; + } + if (tod.second < 10) { + oss << 0 << tod.second; + } else { + oss << tod.second; + } + + date_time = oss.str(); + + return date_time; +} + +void ScexDeviceHandler::modeChanged() {} + +void ScexDeviceHandler::setPowerSwitcher(PowerSwitchIF& powerSwitcher, power::Switch_t switchId) { + DeviceHandlerBase::setPowerSwitcher(&powerSwitcher); + this->switchId = switchId; +} + +ReturnValue_t ScexDeviceHandler::initializeAfterTaskCreation() { + auto mntPrefix = sdcMan.getCurrentMountPrefix(); + std::filesystem::path fullFilePath = mntPrefix; + fullFilePath /= "scex"; + bool fileExists = std::filesystem::exists(fullFilePath); + + if (not fileExists) { + std::filesystem::create_directory(fullFilePath); + } + return DeviceHandlerBase::initializeAfterTaskCreation(); +} diff --git a/mission/devices/ScexDeviceHandler.h b/mission/devices/ScexDeviceHandler.h new file mode 100644 index 00000000..1c96618e --- /dev/null +++ b/mission/devices/ScexDeviceHandler.h @@ -0,0 +1,64 @@ +#ifndef MISSION_DEVICES_SCEXDEVICEHANDLER_H_ +#define MISSION_DEVICES_SCEXDEVICEHANDLER_H_ + +#include +#include +#include + +#include + +#include "eive/eventSubsystemIds.h" + +class SdCardMountedIF; + +class ScexDeviceHandler : public DeviceHandlerBase { + public: + ScexDeviceHandler(object_id_t objectId, ScexUartReader &reader, CookieIF *cookie, + SdCardMountedIF &sdcMan); + void setPowerSwitcher(PowerSwitchIF &powerSwitcher, power::Switch_t switchId); + virtual ~ScexDeviceHandler(); + + private: + static constexpr uint32_t LONG_CD = 180 * 1000; + static constexpr uint32_t SHORT_CD = 12000; + std::array cmdBuf = {}; + + std::optional switchId; + std::string fileId = ""; + std::string fileName = ""; + bool fileNameSet = false; + bool commandActive = false; + bool debugMode = false; + + scex::Cmds currCmd = scex::Cmds::PING; + SdCardMountedIF &sdcMan; + Countdown finishCountdown = Countdown(LONG_CD); + + std::string date_time_string(); + + // DeviceHandlerBase private function implementation + void doStartUp() override; + void doShutDown() override; + ScexHelper helper; + ScexUartReader &reader; + + void performOperationHook() override; + ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t *id) override; + ReturnValue_t buildTransitionDeviceCommand(DeviceCommandId_t *id) override; + ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand, const uint8_t *commandData, + size_t commandDataLen) override; + void fillCommandAndReplyMap() override; + ReturnValue_t scanForReply(const uint8_t *start, size_t remainingSize, DeviceCommandId_t *foundId, + size_t *foundLen) override; + ReturnValue_t interpretDeviceReply(DeviceCommandId_t id, const uint8_t *packet) override; + ReturnValue_t handleValidReply(size_t remSize, DeviceCommandId_t *foundId, size_t *foundLen); + uint32_t getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) override; + ReturnValue_t getSwitches(const uint8_t **switches, uint8_t *numberOfSwitches) override; + + ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap, + LocalDataPoolManager &poolManager) override; + ReturnValue_t initializeAfterTaskCreation() override; + void modeChanged() override; +}; + +#endif /* MISSION_DEVICES_SCEXDEVICEHANDLER_H_ */ diff --git a/mission/devices/devicedefinitions/CMakeLists.txt b/mission/devices/devicedefinitions/CMakeLists.txt new file mode 100644 index 00000000..0ecd64e6 --- /dev/null +++ b/mission/devices/devicedefinitions/CMakeLists.txt @@ -0,0 +1 @@ +target_sources(${LIB_EIVE_MISSION} PRIVATE ScexDefinitions.cpp) diff --git a/mission/devices/devicedefinitions/SCEXDefinitions.h b/mission/devices/devicedefinitions/SCEXDefinitions.h deleted file mode 100644 index 8becabc8..00000000 --- a/mission/devices/devicedefinitions/SCEXDefinitions.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef MISSION_DEVICES_DEVICEDEFINITIONS_SCEXDEFINITIONS_H_ -#define MISSION_DEVICES_DEVICEDEFINITIONS_SCEXDEFINITIONS_H_ - -#include - -// Definitions for the Solar Cell Experiment -namespace scex { - -static constexpr uint8_t CMD_PING = 0x4e; - -} - -#endif /* MISSION_DEVICES_DEVICEDEFINITIONS_SCEXDEFINITIONS_H_ */ diff --git a/mission/devices/devicedefinitions/ScexDefinitions.cpp b/mission/devices/devicedefinitions/ScexDefinitions.cpp new file mode 100644 index 00000000..c4319876 --- /dev/null +++ b/mission/devices/devicedefinitions/ScexDefinitions.cpp @@ -0,0 +1,35 @@ +#include "ScexDefinitions.h" + +#include + +#include + +uint8_t scex::createCmdByte(Cmds cmd, bool tempCheck) { + return (IDLE_BIT_0_DEF_STATE << 7) | (IDLE_BIT_1_DEF_STATE << 6) | (cmd << 1) | tempCheck; +} + +ReturnValue_t scex::prepareScexCmd(Cmds cmd, std::pair cmdBufPair, size_t& cmdLen, + std::pair usrDataPair, bool tempCheck) { + using namespace scex; + uint8_t* cmdBuf = cmdBufPair.first; + const uint8_t* userData = usrDataPair.first; + // Send command + if (cmdBuf == nullptr or (cmdBufPair.second < usrDataPair.second + HEADER_LEN + CRC_LEN) or + (usrDataPair.second > 0 and userData == nullptr)) { + cmdLen = 0; + return returnvalue::FAILED; + } + cmdBuf[0] = createCmdByte(cmd, tempCheck); + // These two fields are the packet counter and the total packet count. Those are 1 and 1 for each + // telecommand so far + cmdBuf[1] = 1; + cmdBuf[2] = 1; + cmdBuf[3] = (usrDataPair.second >> 8) & 0xff; + cmdBuf[4] = usrDataPair.second & 0xff; + std::memcpy(cmdBuf + HEADER_LEN, userData, usrDataPair.second); + uint16_t crc = CRC::crc16ccitt(cmdBuf, usrDataPair.second + HEADER_LEN); + cmdBuf[usrDataPair.second + HEADER_LEN] = (crc >> 8) & 0xff; + cmdBuf[usrDataPair.second + HEADER_LEN + 1] = crc & 0xff; + cmdLen = usrDataPair.second + HEADER_LEN + CRC_LEN; + return returnvalue::OK; +} diff --git a/mission/devices/devicedefinitions/ScexDefinitions.h b/mission/devices/devicedefinitions/ScexDefinitions.h new file mode 100644 index 00000000..22e3878b --- /dev/null +++ b/mission/devices/devicedefinitions/ScexDefinitions.h @@ -0,0 +1,52 @@ +#ifndef MISSION_DEVICES_DEVICEDEFINITIONS_SCEXDEFINITIONS_H_ +#define MISSION_DEVICES_DEVICEDEFINITIONS_SCEXDEFINITIONS_H_ + +#include +#include + +#include +#include + +#include "eive/eventSubsystemIds.h" +#include "eive/objects.h" + +// Definitions for the Solar Cell Experiment +namespace scex { + +static constexpr uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::SCEX_HANDLER; + +static constexpr Event MISSING_PACKET = event::makeEvent(SUBSYSTEM_ID, 0, severity::LOW); +static constexpr Event EXPERIMENT_TIMEDOUT = event::makeEvent(SUBSYSTEM_ID, 1, severity::LOW); +//! FRAM, One Cell or All cells command finished. P1: Command ID +static constexpr Event MULTI_PACKET_COMMAND_DONE = + event::makeEvent(SUBSYSTEM_ID, 2, severity::INFO); + +enum Cmds : DeviceCommandId_t { + PING = 0b00111, + ALL_CELLS_CMD = 0b00101, + ONE_CELL = 0b00110, + FRAM = 0b00001, + EXP_STATUS_CMD = 0b00010, + TEMP_CMD = 0b00011, + ION_CMD = 0b00100, + ERROR_REPLY = 0b01000, + INVALID = 255 +}; + +static const std::vector VALID_CMDS = { + PING, ALL_CELLS_CMD, ONE_CELL, FRAM, EXP_STATUS_CMD, TEMP_CMD, ION_CMD}; + +static constexpr uint8_t HEADER_LEN = 5; +static constexpr uint8_t CRC_LEN = 2; + +static constexpr uint8_t IDLE_BIT_0_DEF_STATE = 0; +static constexpr uint8_t IDLE_BIT_1_DEF_STATE = 1; + +uint8_t createCmdByte(Cmds cmd, bool tempCheck = false); + +ReturnValue_t prepareScexCmd(Cmds cmd, std::pair cmdBufPair, size_t& cmdLen, + std::pair usrDataPair, bool tempCheck = false); + +} // namespace scex + +#endif /* MISSION_DEVICES_DEVICEDEFINITIONS_SCEXDEFINITIONS_H_ */ diff --git a/mission/memory/SdCardMountedIF.h b/mission/memory/SdCardMountedIF.h index 453a44d7..d88bb57a 100644 --- a/mission/memory/SdCardMountedIF.h +++ b/mission/memory/SdCardMountedIF.h @@ -1,6 +1,7 @@ #ifndef MISSION_MEMORY_SDCARDMOUNTERIF_H_ #define MISSION_MEMORY_SDCARDMOUNTERIF_H_ +#include #include #include "definitions.h" @@ -8,11 +9,11 @@ class SdCardMountedIF { public: virtual ~SdCardMountedIF(){}; - virtual std::string getCurrentMountPrefix() const = 0; + virtual const std::string& getCurrentMountPrefix() const = 0; virtual bool isSdCardUsable(sd::SdCard sdCard) = 0; - virtual sd::SdCard getPreferredSdCard() const = 0; + virtual std::optional getPreferredSdCard() const = 0; virtual void setActiveSdCard(sd::SdCard sdCard) = 0; - virtual sd::SdCard getActiveSdCard() const = 0; + virtual std::optional getActiveSdCard() const = 0; private: }; diff --git a/mission/utility/InitMission.h b/mission/utility/InitMission.h index dc131981..5529f749 100644 --- a/mission/utility/InitMission.h +++ b/mission/utility/InitMission.h @@ -1,12 +1,11 @@ -#ifndef MISSION_UTILITY_INITMISSION_H_ -#define MISSION_UTILITY_INITMISSION_H_ +#pragma once #include #include namespace initmission { -void printAddObjectError(const char* name, object_id_t objectId) { +static void printAddObjectError(const char* name, object_id_t objectId) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "InitMission::printAddError: Adding object " << name << " with object ID 0x" << std::hex << std::setfill('0') << std::setw(8) << objectId << " failed!" << std::dec @@ -18,5 +17,3 @@ void printAddObjectError(const char* name, object_id_t objectId) { } } // namespace initmission - -#endif /* MISSION_UTILITY_INITMISSION_H_ */ diff --git a/scripts/rpi-port.sh b/scripts/rpi-port.sh index 5801fb50..e8832c89 100755 --- a/scripts/rpi-port.sh +++ b/scripts/rpi-port.sh @@ -1,8 +1,10 @@ #!/bin/bash echo "-L 1538:raspberrypi.local:1538 for Raspberry Pi connect with TCF agent" echo "-L 1539:raspberrypi.local:22 for Raspberry Pi file transfers" +echo "-L 7301:raspberrypi.local:7301 for Raspberry Pi TMTC Commands" ssh -L 1538:raspberrypi.local:1534 \ -L 1539:raspberrypi.local:22 \ - eive@2001:7c0:2018:1099:babe:0:e1fe:f1a5 \ + -L 7301:raspberrypi.local:7301 \ + eive@2001:7c0:2018:1099:babe:0:e1fe:f1a5 \ -t 'CONSOLE_PREFIX="[RPi Tunnel]" /bin/bash' diff --git a/tmtc b/tmtc index 72fa0ae1..a4a228c4 160000 --- a/tmtc +++ b/tmtc @@ -1 +1 @@ -Subproject commit 72fa0ae15a2312247e1d47cac1183382cc3e0c9c +Subproject commit a4a228c42a7a8385bd26f893a4fa0cffd4fa934a diff --git a/unittest/controller/testThermalController.cpp b/unittest/controller/testThermalController.cpp index 454f7bbe..3fac0f4e 100644 --- a/unittest/controller/testThermalController.cpp +++ b/unittest/controller/testThermalController.cpp @@ -15,7 +15,7 @@ TEST_CASE("Thermal Controller", "[ThermalController]") { new TemperatureSensorsDummy(); new SusDummy(); - //testEnvironment::initialize(); + // testEnvironment::initialize(); ThermalController controller(THERMAL_CONTROLLER_ID); ReturnValue_t result = controller.initialize();