diff --git a/CMakeLists.txt b/CMakeLists.txt index 976bc6ee..03b2b512 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -119,6 +119,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 @@ -380,20 +383,20 @@ 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) +# 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() +# 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/InitMission.cpp b/bsp_linux_board/InitMission.cpp index 808fc8be..ec77f668 100644 --- a/bsp_linux_board/InitMission.cpp +++ b/bsp_linux_board/InitMission.cpp @@ -1,5 +1,6 @@ #include "InitMission.h" +#include #include #include #include @@ -77,6 +78,36 @@ void initmission::initTasks() { sif::error << "Add component TMTC Polling failed" << std::endl; } +#if OBSW_ADD_SCEX == 1 + PeriodicTaskIF* scexDevHandler = factory->createPeriodicTask( + "SCEX_DEV", 35, PeriodicTaskIF::MINIMUM_STACK_SIZE * 2, 0.5, missedDeadlineFunc); + result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::PERFORM_OPERATION); + if (result != HasReturnvaluesIF::RETURN_OK) { + initmission::printAddObjectError("SCEX_DEV", objects::SCEX); + } + result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::SEND_WRITE); + if (result != HasReturnvaluesIF::RETURN_OK) { + initmission::printAddObjectError("SCEX_DEV", objects::SCEX); + } + result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::GET_WRITE); + if (result != HasReturnvaluesIF::RETURN_OK) { + initmission::printAddObjectError("SCEX_DEV", objects::SCEX); + } + result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::SEND_READ); + if (result != HasReturnvaluesIF::RETURN_OK) { + initmission::printAddObjectError("SCEX_DEV", objects::SCEX); + } + result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::GET_READ); + + result = HasReturnvaluesIF::RETURN_OK; + PeriodicTaskIF* scexReaderTask = factory->createPeriodicTask( + "SCEX_UART_READER", 20, PeriodicTaskIF::MINIMUM_STACK_SIZE, 2.0, missedDeadlineFunc); + result = scexReaderTask->addComponent(objects::SCEX_UART_READER); + if (result != HasReturnvaluesIF::RETURN_OK) { + initmission::printAddObjectError("SCEX_UART_READER", objects::SCEX_UART_READER); + } +#endif + /* PUS Services */ std::vector pusTasks; createPusTasks(*factory, missedDeadlineFunc, pusTasks); @@ -109,6 +140,10 @@ void initmission::initTasks() { #endif /* OBSW_ADD_TEST_CODE == 1 */ taskStarter(pstTasks, "PST Tasks"); +#if OBSW_ADD_SCEX == 1 + scexDevHandler->startTask(); + scexReaderTask->startTask(); +#endif #if OBSW_ADD_TEST_PST == 1 if (startTestPst) { pstTestTask->startTask(); @@ -194,10 +229,13 @@ void initmission::createPstTasks(TaskFactory& factory, FixedTimeslotTaskIF* spiPst = factory.createFixedTimeslotTask( "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 != HasReturnvaluesIF::RETURN_OK) { + if (result != FixedTimeslotTaskIF::SLOT_LIST_EMPTY) { + sif::error << "InitMission::initTasks: Creating PST failed!" << std::endl; + } + } else { + taskVec.push_back(spiPst); } - taskVec.push_back(spiPst); #endif } @@ -228,6 +266,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 != HasReturnvaluesIF::RETURN_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..398cf19e 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 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..19a00647 100644 --- a/bsp_linux_board/ObjectFactory.cpp +++ b/bsp_linux_board/ObjectFactory.cpp @@ -1,5 +1,7 @@ #include "ObjectFactory.h" +#include + #include "OBSWConfig.h" #include "devConf.h" #include "devices/addresses.h" @@ -18,7 +20,7 @@ #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" @@ -76,12 +78,16 @@ 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 == 1 + createScexComponents(uart::DEV, pwrSwitcher); +#endif + #if OBSW_ADD_SUN_SENSORS == 1 createSunSensorComponents(gpioIF, spiComIF, pwrSwitcher, spi::DEV); #endif @@ -196,7 +202,8 @@ void ObjectFactory::createTestTasks() { #endif #if OBSW_ADD_UART_TEST_CODE == 1 - new UartTestClass(objects::UART_TEST); + auto scexReader = new ScexUartReader(objects::SCEX_UART_READER); + new UartTestClass(objects::UART_TEST, scexReader); #else new UartComIF(objects::UART_COM_IF); #endif 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 b9c27840..db73b4bc 100644 --- a/bsp_q7s/OBSWConfig.h.in +++ b/bsp_q7s/OBSWConfig.h.in @@ -33,6 +33,7 @@ #define OBSW_ADD_ACS_HANDLERS @OBSW_ADD_ACS_HANDLERS@ #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/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/commonObjects.h b/common/config/commonObjects.h index e0efbce7..f59ad8af 100644 --- a/common/config/commonObjects.h +++ b/common/config/commonObjects.h @@ -57,6 +57,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/common/config/commonSubsystemIds.h b/common/config/commonSubsystemIds.h index 001f6d50..5eb379b5 100644 --- a/common/config/commonSubsystemIds.h +++ b/common/config/commonSubsystemIds.h @@ -32,6 +32,7 @@ enum : uint8_t { ACU_HANDLER = 135, PLOC_SUPV_HELPER = 136, SYRLINKS = 137, + SCEX_HANDLER = 138, COMMON_SUBSYSTEM_ID_END }; } 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/generators/bsp_q7s_events.csv b/generators/bsp_q7s_events.csv index d4258731..c7001a82 100644 --- a/generators/bsp_q7s_events.csv +++ b/generators/bsp_q7s_events.csv @@ -215,3 +215,4 @@ 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 +13800;0x35e8;EXPERIMENT_TIMEDOUT;LOW;;mission/devices/devicedefinitions/ScexDefinitions.h diff --git a/generators/bsp_q7s_objects.csv b/generators/bsp_q7s_objects.csv index 53ddb3a6..04f39106 100644 --- a/generators/bsp_q7s_objects.csv +++ b/generators/bsp_q7s_objects.csv @@ -46,6 +46,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 @@ -69,6 +70,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 21ba77b3..dda8b717 100644 --- a/generators/events/translateEvents.cpp +++ b/generators/events/translateEvents.cpp @@ -1,7 +1,7 @@ /** - * @brief Auto-generated event translation file. Contains 216 translations. + * @brief Auto-generated event translation file. Contains 217 translations. * @details - * Generated on: 2022-08-24 16:44:18 + * Generated on: 2022-08-24 16:53:50 */ #include "translateEvents.h" @@ -217,6 +217,7 @@ 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 *EXPERIMENT_TIMEDOUT_STRING = "EXPERIMENT_TIMEDOUT"; const char *translateEvents(Event event) { switch ((event & 0xFFFF)) { @@ -644,6 +645,8 @@ const char *translateEvents(Event event) { return REBOOT_MECHANISM_TRIGGERED_STRING; case (13703): return REBOOT_HW_STRING; + case (13800): + return EXPERIMENT_TIMEDOUT_STRING; default: return "UNKNOWN_EVENT"; } diff --git a/generators/objects/translateObjects.cpp b/generators/objects/translateObjects.cpp index 12e74bfe..215d47d8 100644 --- a/generators/objects/translateObjects.cpp +++ b/generators/objects/translateObjects.cpp @@ -1,8 +1,8 @@ /** * @brief Auto-generated object translation file. * @details - * Contains 133 translations. - * Generated on: 2022-08-24 16:44:18 + * Contains 135 translations. + * Generated on: 2022-08-24 16:53:50 */ #include "translateObjects.h" @@ -54,6 +54,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"; @@ -77,6 +78,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"; @@ -238,6 +240,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: @@ -284,6 +288,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/ObjectFactory.cpp b/linux/ObjectFactory.cpp index a979e4b2..0398bd7e 100644 --- a/linux/ObjectFactory.cpp +++ b/linux/ObjectFactory.cpp @@ -6,12 +6,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -321,6 +323,16 @@ void ObjectFactory::createRtdComponents(std::string spiDev, GpioIF* gpioComIF, #endif // OBSW_ADD_RTD_DEVICES == 1 } +void ObjectFactory::createScexComponents(std::string uartDev, PowerSwitchIF* pwrSwitcher) { + // objekte anlegen + SdCardMountedIF* sdcMan = nullptr; + auto* cookie = new UartCookie(objects::SCEX, uartDev, uart::SCEX_BAUD, 4096); + + auto scexUartReader = new ScexUartReader(objects::SCEX_UART_READER); + auto scexHandler = new ScexDeviceHandler(objects::SCEX, *scexUartReader, cookie, sdcMan); + scexHandler->setStartUpImmediately(); +} + void ObjectFactory::createThermalController() { new ThermalController(objects::THERMAL_CONTROLLER, objects::NO_OBJECT); } diff --git a/linux/ObjectFactory.h b/linux/ObjectFactory.h index 56a5664b..99bef0e7 100644 --- a/linux/ObjectFactory.h +++ b/linux/ObjectFactory.h @@ -16,6 +16,8 @@ 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); + void gpioChecker(ReturnValue_t result, std::string output); void createThermalController(); diff --git a/linux/boardtest/UartTestClass.cpp b/linux/boardtest/UartTestClass.cpp index 891959da..9843baa5 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,23 @@ #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, ScexUartReader* reader) + : TestTask(objectId), reader(reader) { + mode = TestModes::SCEX; + scexMode = ScexModes::READER_TASK; + // No one-cell and all-cell support implemented yet + currCmd = scex::Cmds::FRAM; + 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()}; + dleParser = new ScexDleParser(*(new SimpleRingBuffer(4096, true)), dleEncoder, encodingBufPair, + decodingBufPair, &foundDlePacketHandler, this); + } +} ReturnValue_t UartTestClass::initialize() { if (mode == TestModes::GPS) { @@ -44,14 +67,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 +102,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 +118,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 +132,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 +150,119 @@ void UartTestClass::gpsPeriodic() { } void UartTestClass::scexInit() { + if (reader == nullptr) { + sif::warning << "UartTestClass::scexInit: Reader invalid" << std::endl; + return; + } + if (scexMode == ScexModes::SIMPLE) { + scexSimpleInit(); + } else { +#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 (reader == nullptr) { + return; + } + + if (scexMode == ScexModes::SIMPLE) { + scexSimplePeriodic(); + } else { + 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 + // packetcounter eins h�her, wenn mehr packet verloren -> merkt sich welches packet fehlt + // was wenn erstes packet fehlt; mit boolean var (firstpacketarrived=false) die immer mit + // finish false wird? + // countdown (max 2min), wenn nicht if (helper.getPacketCounter() == + // helper.getTotalPacketCounter()) { nach 2min reader->finish(); + 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 +271,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 +288,125 @@ 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; return 0; } + +void UartTestClass::foundDlePacketHandler(const DleParser::Context& ctx) { + UartTestClass* obj = reinterpret_cast(ctx.userArgs); + if (ctx.getType() == DleParser::ContextType::PACKET_FOUND) { + obj->handleFoundDlePacket(ctx.decodedPacket.first, ctx.decodedPacket.second); + } else { + DleParser::defaultErrorHandler(ctx.error.first, ctx.error.second); + } +} + +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..1c1b9988 100644 --- a/linux/boardtest/UartTestClass.h +++ b/linux/boardtest/UartTestClass.h @@ -1,17 +1,25 @@ #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); + UartTestClass(object_id_t objectId, ScexUartReader* reader); ReturnValue_t initialize() override; ReturnValue_t performOneShotAction() override; @@ -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..ac1828dc --- /dev/null +++ b/linux/devices/ScexDleParser.cpp @@ -0,0 +1,7 @@ +#include "ScexDleParser.h" + +ScexDleParser::ScexDleParser(SimpleRingBuffer &decodeRingBuf, DleEncoder &decoder, + BufPair encodedBuf, BufPair decodedBuf, UserHandler handler, + void *args) + : DleParser(decodeRingBuf, decoder, encodedBuf, decodedBuf, handler, args){}; +ScexDleParser::~ScexDleParser(){}; diff --git a/linux/devices/ScexDleParser.h b/linux/devices/ScexDleParser.h new file mode 100644 index 00000000..a8b04ca7 --- /dev/null +++ b/linux/devices/ScexDleParser.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +class ScexDleParser : public DleParser { + public: + ScexDleParser(SimpleRingBuffer &decodeRingBuf, DleEncoder &decoder, BufPair encodedBuf, + BufPair decodedBuf, UserHandler handler, void *args); + // ScexDleParser(SimpleRingBuffer &decodeRingBuf, DleEncoder &decoder, + // BufPair encodedBuf, BufPair decodedBuf, UserHandler handler, + // void *args) : DleParser(decodeRingBuf, decoder, encodedBuf, decodedBuf, handler, + // args){} + + 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..38286a7a --- /dev/null +++ b/linux/devices/ScexUartReader.cpp @@ -0,0 +1,206 @@ +#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()}, &foundDlePacketHandler, (void *)this) { + 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(); + sif::info << "task was started" << std::endl; + int bytesRead = 0; + while (true) { + bytesRead = read(serialPort, reinterpret_cast(recBuf.data()), + static_cast(recBuf.size())); + if (bytesRead == 0) { + MutexGuard mg(lock); + if (state == States::FINISH) { + sif::debug << "finish detected" << std::endl; + state = States::IDLE; + break; + } + 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) { + ReturnValue_t result = dleParser.passData(recBuf.data(), bytesRead); + if (debugMode) { + sif::info << "Received " << bytesRead + << " bytes from the Solar Cell Experiment:" << std::endl; + } + if (result != OK) { + sif::warning << "ScexUartReader::performOperation: Passing data to DLE parser failed" + << std::endl; + } + } + }; + // task block comes here + sif::info << "task was stopped" << std::endl; + } + 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 + tty.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication + 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; + + // 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 << "ScexUartReader::initializeInterface: Setting baud rate failed" << std::endl; + } +#endif + + 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) { + if (sendData == nullptr or sendLen == 0) { + return FAILED; + } + lock->lockMutex(); + if (state == States::NOT_READY or state == States::RUNNING) { + lock->unlockMutex(); + return FAILED; + } + state = States::RUNNING; + lock->unlockMutex(); + size_t encodedLen = 0; + ReturnValue_t result = + dleEncoder.encode(sendData, sendLen, cmdbuf.data(), cmdbuf.size(), &encodedLen, true); + if (result != OK) { + sif::warning << "ScexUartReader::sendMessage: Encoding failed" << std::endl; + return FAILED; + } + arrayprinter::print(cmdbuf.data(), encodedLen); + 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; + } + result = semaphore->release(); + if (result != OK) { + std::cout << "ScexUartReader::sendMessage: Releasing semaphore failed" << std::endl; + } + 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::foundDlePacketHandler(const ScexDleParser::Context &ctx) { + ScexUartReader *obj = reinterpret_cast(ctx.userArgs); + if (ctx.getType() == ScexDleParser::ContextType::PACKET_FOUND) { + obj->handleFoundDlePacket(ctx.decodedPacket.first, ctx.decodedPacket.second); + } else { + ScexDleParser::defaultErrorHandler(ctx.error.first, ctx.error.second); + } +} + +void ScexUartReader::handleFoundDlePacket(uint8_t *packet, size_t len) { + // TODO: insert data into IPC ring buffer here + // sif::info << "Detected DLE encoded packet with decoded size " << len << std::endl; + 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::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..bdce5b65 --- /dev/null +++ b/linux/devices/ScexUartReader.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include // Contains POSIX terminal control definitions + +class SemaphoreIF; +class MutexIF; + +class ScexUartReader : public SystemObject, // strg+shift+n + public ExecutableObjectIF, + public DeviceCommunicationIF { + friend class UartTestClass; + + public: + enum class States { NOT_READY, IDLE, RUNNING, FINISH }; + ScexUartReader(object_id_t objectId); + + 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 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 21ba77b3..dda8b717 100644 --- a/linux/fsfwconfig/events/translateEvents.cpp +++ b/linux/fsfwconfig/events/translateEvents.cpp @@ -1,7 +1,7 @@ /** - * @brief Auto-generated event translation file. Contains 216 translations. + * @brief Auto-generated event translation file. Contains 217 translations. * @details - * Generated on: 2022-08-24 16:44:18 + * Generated on: 2022-08-24 16:53:50 */ #include "translateEvents.h" @@ -217,6 +217,7 @@ 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 *EXPERIMENT_TIMEDOUT_STRING = "EXPERIMENT_TIMEDOUT"; const char *translateEvents(Event event) { switch ((event & 0xFFFF)) { @@ -644,6 +645,8 @@ const char *translateEvents(Event event) { return REBOOT_MECHANISM_TRIGGERED_STRING; case (13703): return REBOOT_HW_STRING; + case (13800): + return EXPERIMENT_TIMEDOUT_STRING; default: return "UNKNOWN_EVENT"; } diff --git a/linux/fsfwconfig/objects/systemObjectList.h b/linux/fsfwconfig/objects/systemObjectList.h index 669fcc9f..c44c4bbc 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, SPI_RTD_COM_IF = 0x49020006, diff --git a/linux/fsfwconfig/objects/translateObjects.cpp b/linux/fsfwconfig/objects/translateObjects.cpp index 12e74bfe..215d47d8 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 133 translations. - * Generated on: 2022-08-24 16:44:18 + * Contains 135 translations. + * Generated on: 2022-08-24 16:53:50 */ #include "translateObjects.h" @@ -54,6 +54,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"; @@ -77,6 +78,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"; @@ -238,6 +240,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: @@ -284,6 +288,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/misc/eclipse/.cproject b/misc/eclipse/.cproject index d30ef1ec..19c18545 100644 --- a/misc/eclipse/.cproject +++ b/misc/eclipse/.cproject @@ -57,7 +57,7 @@ - + @@ -119,7 +119,7 @@ - + @@ -187,7 +187,7 @@ - + @@ -255,7 +255,7 @@ - + @@ -418,7 +418,7 @@ - + @@ -580,7 +580,7 @@ - + @@ -750,7 +750,7 @@ - + @@ -917,7 +917,7 @@ - + @@ -1084,7 +1084,7 @@ - + @@ -1149,7 +1149,7 @@ - + @@ -1317,7 +1317,7 @@ - + @@ -1386,7 +1386,7 @@ - + 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/ScexDeviceHandler.cpp b/mission/devices/ScexDeviceHandler.cpp new file mode 100644 index 00000000..15dd05cb --- /dev/null +++ b/mission/devices/ScexDeviceHandler.cpp @@ -0,0 +1,284 @@ +#include "ScexDeviceHandler.h" + +#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() { + // mode on + setMode(MODE_ON); +} + +void ScexDeviceHandler::doShutDown() { 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(); + prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen, + {commandData + 1, commandDataLen - 1}, tempCheck); + break; + } + case (ONE_CELL): { + finishCountdown.setTimeout(LONG_CD); + // countdown starts + finishCountdown.resetTimer(); + prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen, + {commandData + 1, commandDataLen - 1}, tempCheck); + 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); + break; + } + default: { + return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED; + } + } + commandActive = true; + rawPacket = cmdBuf.data(); + return OK; +} + +void ScexDeviceHandler::fillCommandAndReplyMap() { + insertInCommandAndReplyMap(scex::Cmds::PING, 5); + insertInCommandAndReplyMap(scex::Cmds::ION_CMD, 3); + insertInCommandAndReplyMap(scex::Cmds::TEMP_CMD, 3); + insertInCommandAndReplyMap(scex::Cmds::EXP_STATUS_CMD, 3); + + insertInCommandAndReplyMap(scex::Cmds::ALL_CELLS_CMD, 0, nullptr, 0, false, false, + scex::Cmds::ALL_CELLS_CMD, &finishCountdown); + insertInCommandAndReplyMap(scex::Cmds::ONE_CELL, 0, nullptr, 0, false, false, + scex::Cmds::ONE_CELL, &finishCountdown); + insertInCommandAndReplyMap(scex::Cmds::FRAM, 0, nullptr, 0, false, 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; + return result; + } + *foundId = helper.getCmd(); + *foundLen = remainingSize; + + return OK; +} + +ReturnValue_t ScexDeviceHandler::interpretDeviceReply(DeviceCommandId_t id, const uint8_t* packet) { + // cmd auswertung (in file reinschreiben) + using namespace scex; + + ReturnValue_t status = OK; + auto oneFileHandler = [&](std::string cmdName) { + fileId = random_string(6); + std::ostringstream oss("/tmp/scex-", std::ostringstream::ate); + oss << cmdName << fileId << ".bin"; + fileName = oss.str(); + std::cout << fileName << std::endl; + ofstream out(fileName, ofstream::binary); + if (out.bad()) { + sif::error << "ScexDeviceHandler::interpretDeviceReply: Could not open file " << fileName + << std::endl; + return FAILED; + } + if (debugMode) { + out << helper; + } + return OK; + }; + auto multiFileHandler = [&](std::string cmdName) { + if ((helper.getPacketCounter() == 1) or (not fileNameSet)) { + + fileId = random_string(6); + std::ostringstream oss("/tmp/scex-", std::ostringstream::ate); + oss << cmdName << fileId << ".bin"; + fileName = oss.str(); + fileNameSet = true; + ofstream out(fileName, ofstream::binary); + if (out.bad()) { + sif::error << "ScexDeviceHandler::interpretDeviceReply: Could not open file " << fileName + << std::endl; + return FAILED; + } + } else { + ofstream out(fileName, + ofstream::binary | ofstream::app); // append + if (debugMode) { + 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("all_cell_"); + break; + } + default: + // Unknown DeviceCommand + return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED; + } + if (helper.getPacketCounter() == helper.getTotalPacketCounter()) { + reader.finish(); + commandActive = false; + if (id != PING) { + sif::info << "Reader is finished" << std::endl; + fileNameSet = false; + } + } + + return status; +} + +void ScexDeviceHandler::performOperationHook() { + if (commandActive and finishCountdown.hasTimedOut()) { + triggerEvent(scex::EXPERIMENT_TIMEDOUT, currCmd, 0); + reader.finish(); + sif::warning << "ScexDeviceHandler::performOperationHook: Reader timeout" << 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) { + return OK; +} + +ReturnValue_t ScexDeviceHandler::initializeLocalDataPool(localpool::DataPool& localDataPoolMap, + LocalDataPoolManager& poolManager) { + return OK; +} + +std::string ScexDeviceHandler::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; +} + +void ScexDeviceHandler::modeChanged() {} diff --git a/mission/devices/ScexDeviceHandler.h b/mission/devices/ScexDeviceHandler.h new file mode 100644 index 00000000..eeaac755 --- /dev/null +++ b/mission/devices/ScexDeviceHandler.h @@ -0,0 +1,56 @@ +#ifndef MISSION_DEVICES_SCEXDEVICEHANDLER_H_ +#define MISSION_DEVICES_SCEXDEVICEHANDLER_H_ + +#include +#include +#include + +class SdCardMountedIF; + +class ScexDeviceHandler : public DeviceHandlerBase { + public: + ScexDeviceHandler(object_id_t objectId, ScexUartReader &reader, CookieIF *cookie, + SdCardMountedIF *sdcMan); + virtual ~ScexDeviceHandler(); + + private: + static constexpr uint32_t LONG_CD = 180 * 1000; + static constexpr uint32_t SHORT_CD = 7000; + std::array cmdBuf = {}; + + std::string fileId = ""; + std::string fileName = ""; + bool fileNameSet = false; + bool commandActive = false; + bool debugMode = true; + + scex::Cmds currCmd = scex::Cmds::PING; + SdCardMountedIF *sdcMan = nullptr; + Countdown finishCountdown = Countdown(LONG_CD); + + std::string random_string(std::string::size_type length); + + // 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; + 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; + 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..685f0cc0 --- /dev/null +++ b/mission/devices/devicedefinitions/ScexDefinitions.h @@ -0,0 +1,48 @@ +#ifndef MISSION_DEVICES_DEVICEDEFINITIONS_SCEXDEFINITIONS_H_ +#define MISSION_DEVICES_DEVICEDEFINITIONS_SCEXDEFINITIONS_H_ + +#include +#include +#include + +#include +#include + +// 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, 0, severity::LOW); + +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/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'