diff --git a/CHANGELOG.md b/CHANGELOG.md index 537da611..e2f57d3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,13 +16,33 @@ will consitute of a breaking change warranting a new major release: # [unreleased] -- Bumped `eive-tmtc` to v5.7.0. -- Bumped `eive-fsfw` - ## Added - CFDP source handler, which allow file downlink using the standardized CFDP interface. + +## Fixed + +- If the PTME is driven in a way where it fills faster than it can be emptied, the interface + can become full during the process of a regular packet write. The interface of the PAPB VC + component was adapted to account for this partial success state. The caller must now check + for the `PARTIALLY_WRITTEN` state and must take care of finishing a write in some shape or + form before starting the next packet transfer. +- Host build fixes + +# [v7.1.0] 2023-10-11 + +- Bumped `eive-tmtc` to v5.8.0. +- Activate Xiphos WDT with a timeout period of 80 seconds using the `libxiphos` API. The WDT + calls are done by the new `XiphosWdtHandler` object. + +# [v7.0.0] 2023-10-11 + +- Bumped `eive-tmtc` to v5.7.1. +- Bumped `eive-fsfw` + +## Added + - EPS Subsystem has been added to EIVE System Tree - Power Controller for calculating the State of Charge and FDIR regarding low SoC has been introduced. @@ -36,7 +56,7 @@ will consitute of a breaking change warranting a new major release: ## Fixed - Missing `nullptr` checks for PLOC Supervisor handler, which could lead to crashes. -- Host build fixes +- SCEX bugfix for normal and transition commanding. # [v6.6.0] 2023-09-18 diff --git a/CMakeLists.txt b/CMakeLists.txt index 65038b76..65e85d35 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,8 +9,8 @@ # ############################################################################## cmake_minimum_required(VERSION 3.13) -set(OBSW_VERSION_MAJOR 6) -set(OBSW_VERSION_MINOR 6) +set(OBSW_VERSION_MAJOR 7) +set(OBSW_VERSION_MINOR 1) set(OBSW_VERSION_REVISION 0) # set(CMAKE_VERBOSE TRUE) diff --git a/bsp_hosted/fsfwconfig/events/translateEvents.cpp b/bsp_hosted/fsfwconfig/events/translateEvents.cpp index e42457ce..56112520 100644 --- a/bsp_hosted/fsfwconfig/events/translateEvents.cpp +++ b/bsp_hosted/fsfwconfig/events/translateEvents.cpp @@ -1,7 +1,7 @@ /** * @brief Auto-generated event translation file. Contains 314 translations. * @details - * Generated on: 2023-10-11 12:58:28 + * Generated on: 2023-10-13 09:44:05 */ #include "translateEvents.h" diff --git a/bsp_hosted/fsfwconfig/objects/translateObjects.cpp b/bsp_hosted/fsfwconfig/objects/translateObjects.cpp index 418e23bb..7916bfeb 100644 --- a/bsp_hosted/fsfwconfig/objects/translateObjects.cpp +++ b/bsp_hosted/fsfwconfig/objects/translateObjects.cpp @@ -1,8 +1,8 @@ /** * @brief Auto-generated object translation file. * @details - * Contains 173 translations. - * Generated on: 2023-10-11 12:58:28 + * Contains 174 translations. + * Generated on: 2023-10-13 09:44:05 */ #include "translateObjects.h" @@ -11,6 +11,7 @@ const char *ACS_CONTROLLER_STRING = "ACS_CONTROLLER"; const char *CORE_CONTROLLER_STRING = "CORE_CONTROLLER"; const char *POWER_CONTROLLER_STRING = "POWER_CONTROLLER"; const char *GLOBAL_JSON_CFG_STRING = "GLOBAL_JSON_CFG"; +const char *XIPHOS_WDT_STRING = "XIPHOS_WDT"; const char *THERMAL_CONTROLLER_STRING = "THERMAL_CONTROLLER"; const char *DUMMY_HANDLER_STRING = "DUMMY_HANDLER"; const char *MGM_0_LIS3_HANDLER_STRING = "MGM_0_LIS3_HANDLER"; @@ -192,6 +193,8 @@ const char *translateObject(object_id_t object) { return POWER_CONTROLLER_STRING; case 0x43000006: return GLOBAL_JSON_CFG_STRING; + case 0x43000007: + return XIPHOS_WDT_STRING; case 0x43400001: return THERMAL_CONTROLLER_STRING; case 0x44000001: diff --git a/bsp_q7s/core/CMakeLists.txt b/bsp_q7s/core/CMakeLists.txt index 33550144..530b53e9 100644 --- a/bsp_q7s/core/CMakeLists.txt +++ b/bsp_q7s/core/CMakeLists.txt @@ -1 +1,2 @@ -target_sources(${OBSW_NAME} PRIVATE CoreController.cpp WatchdogHandler.cpp) +target_sources(${OBSW_NAME} PRIVATE CoreController.cpp WatchdogHandler.cpp + XiphosWdtHandler.cpp) diff --git a/bsp_q7s/core/XiphosWdtHandler.cpp b/bsp_q7s/core/XiphosWdtHandler.cpp new file mode 100644 index 00000000..8444b65f --- /dev/null +++ b/bsp_q7s/core/XiphosWdtHandler.cpp @@ -0,0 +1,122 @@ +#include "XiphosWdtHandler.h" + +#include "fsfw/ipc/QueueFactory.h" + +XiphosWdtHandler::XiphosWdtHandler(object_id_t objectId) + : SystemObject(objectId), + requestQueue(QueueFactory::instance()->createMessageQueue()), + actionHelper(this, requestQueue) {} + +ReturnValue_t XiphosWdtHandler::initialize() { + ReturnValue_t result = actionHelper.initialize(); + if (result != returnvalue::OK) { + return result; + } + int retval = xsc_watchdog_init(&wdtHandle); + if (retval != 0) { + sif::error << "XiphosWdtHandler: Initiating watchdog failed with code " << retval << ": " + << strerror(retval) << std::endl; + return ObjectManagerIF::CHILD_INIT_FAILED; + } + if (wdtHandle == nullptr) { + sif::error << "XiphosWdtHandler: WDT handle is nullptr!" << std::endl; + return ObjectManagerIF::CHILD_INIT_FAILED; + } + retval = xsc_watchdog_set_timeout(wdtHandle, timeoutSeconds); + if (retval != 0) { + // This propably means that the default timeout is used. Still continue with task init. + sif::warning << "XiphosWdtHandler: Setting WDT timeout of " << timeoutSeconds + << " seconds failed with code " << result << ": " << strerror(retval) << std::endl; + } + return enableWdt(); +} + +ReturnValue_t XiphosWdtHandler::performOperation(uint8_t opCode) { + CommandMessage command; + ReturnValue_t result; + for (result = requestQueue->receiveMessage(&command); result == returnvalue::OK; + result = requestQueue->receiveMessage(&command)) { + result = actionHelper.handleActionMessage(&command); + if (result == returnvalue::OK) { + continue; + } + sif::warning << "Can not handle message with message type " << command.getMessageType() + << std::endl; + } + if (enabled) { + int retval = xsc_watchdog_keepalive(wdtHandle); + if (retval != 0) { + sif::warning << "XiphosWdtHandler: Feeding WDT failed with code " << retval << ": " + << strerror(retval) << std::endl; + return returnvalue::FAILED; + } + } + return returnvalue::OK; +} + +ReturnValue_t XiphosWdtHandler::executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, + const uint8_t *data, size_t size) { + switch (actionId) { + case (ActionId::ENABLE): { + ReturnValue_t result = enableWdt(); + if (result != returnvalue::OK) { + return result; + } + return EXECUTION_FINISHED; + } + case (ActionId::DISABLE): { + ReturnValue_t result = disableWdt(); + if (result != returnvalue::OK) { + return result; + } + return EXECUTION_FINISHED; + } + } + return HasActionsIF::INVALID_ACTION_ID; +} + +ReturnValue_t XiphosWdtHandler::enableWdt() { + int nowayout = 0; + int status = 0; + int retval = xsc_watchdog_get_status(&nowayout, &status); + // If this fails for whatever reason, just try enabling in any case. + if (retval != 0) { + sif::warning << "XiphosWdtHandler: Getting WDT status failed" << std::endl; + } + // Of course the enable API will fail if the device is already on, just perfect, love me some + // good C API... :))) + if (retval != 0 or status == 0) { + retval = xsc_watchdog_enable(wdtHandle); + if (retval != 0) { + sif::error << "XiphosWdtHandler: Enabling WDT failed with code " << retval << ": " + << strerror(retval) << std::endl; + return returnvalue::FAILED; + } + } + enabled = true; + return returnvalue::OK; +} + +ReturnValue_t XiphosWdtHandler::disableWdt() { + int nowayout = 0; + int status = 0; + int retval = xsc_watchdog_get_status(&nowayout, &status); + // If this fails for whatever reason, just try disabling in any case. + if (retval != 0) { + sif::warning << "XiphosWdtHandler: Getting WDT status failed" << std::endl; + } + // Of course the disable API will fail if the device is already off, just perfect, love me some + // good C API... :))) + if (retval != 0 or status == 1) { + retval = xsc_watchdog_disable(wdtHandle); + if (retval != 0) { + sif::error << "XiphosWdtHandler: Disabling WDT failed with code " << retval << ": " + << strerror(retval) << std::endl; + return returnvalue::FAILED; + } + } + enabled = false; + return returnvalue::OK; +} + +MessageQueueId_t XiphosWdtHandler::getCommandQueue() const { return requestQueue->getId(); } diff --git a/bsp_q7s/core/XiphosWdtHandler.h b/bsp_q7s/core/XiphosWdtHandler.h new file mode 100644 index 00000000..a8d73f48 --- /dev/null +++ b/bsp_q7s/core/XiphosWdtHandler.h @@ -0,0 +1,36 @@ +#ifndef BSP_Q7S_CORE_XIPHOSWDTHANDLER_H_ +#define BSP_Q7S_CORE_XIPHOSWDTHANDLER_H_ + +#include +#include +#include +#include + +class XiphosWdtHandler : public SystemObject, public ExecutableObjectIF, public HasActionsIF { + public: + enum ActionId { ENABLE = 0, DISABLE = 1 }; + + XiphosWdtHandler(object_id_t objectId); + ReturnValue_t performOperation(uint8_t opCode) override; + ReturnValue_t initialize() override; + + ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, + const uint8_t* data, size_t size) override; + [[nodiscard]] virtual MessageQueueId_t getCommandQueue() const override; + + private: + // Wrappers to ensure idempotency of trash C API. + ReturnValue_t enableWdt(); + ReturnValue_t disableWdt(); + // Timeout duration range specified by Xiphos: 0.001 seconds to 171 seconds. The libxiphos API + // expects an int, so I guess this translates to 1 to 171 seconds. + // WARNING: DO NOT SET THIS HIGHER THAN 80 SECONDS! + // Possible bug in Xiphos/Xilinx kernel driver for watchdog, related to overflow. + int timeoutSeconds = 80; + bool enabled = false; + struct watchdog_s* wdtHandle = nullptr; + MessageQueueIF* requestQueue = nullptr; + ActionHelper actionHelper; +}; + +#endif /* BSP_Q7S_CORE_XIPHOSWDTHANDLER_H_ */ diff --git a/bsp_q7s/em/emObjectFactory.cpp b/bsp_q7s/em/emObjectFactory.cpp index 1a787e04..ca75e123 100644 --- a/bsp_q7s/em/emObjectFactory.cpp +++ b/bsp_q7s/em/emObjectFactory.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -37,6 +38,7 @@ void ObjectFactory::produce(void* args) { PersistentTmStores stores; readFirmwareVersion(); + new XiphosWdtHandler(objects::XIPHOS_WDT); ObjectFactory::produceGenericObjects(&healthTable, &pusFunnel, &cfdpFunnel, *SdCardManager::instance(), &ipcStore, &tmStore, stores, 200, enableHkSets, true); diff --git a/bsp_q7s/fmObjectFactory.cpp b/bsp_q7s/fmObjectFactory.cpp index 9ade8df3..e4a61124 100644 --- a/bsp_q7s/fmObjectFactory.cpp +++ b/bsp_q7s/fmObjectFactory.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -34,6 +35,7 @@ void ObjectFactory::produce(void* args) { PersistentTmStores stores; readFirmwareVersion(); + new XiphosWdtHandler(objects::XIPHOS_WDT); ObjectFactory::produceGenericObjects(&healthTable, &pusFunnel, &cfdpFunnel, *SdCardManager::instance(), &ipcStore, &tmStore, stores, 200, true, true); diff --git a/bsp_q7s/scheduling.cpp b/bsp_q7s/scheduling.cpp index fa7fbe18..3c30e91a 100644 --- a/bsp_q7s/scheduling.cpp +++ b/bsp_q7s/scheduling.cpp @@ -82,6 +82,16 @@ void scheduling::initTasks() { } #endif + // Medium priority, higher than something like payload, but not the highest priority to also + // detect tasks which choke other tasks. + PeriodicTaskIF* xiphosWdtTask = + factory->createPeriodicTask("XIPHOS_WDT", 40, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.4, + missedDeadlineFunc, &RR_SCHEDULING); + result = xiphosWdtTask->addComponent(objects::XIPHOS_WDT); + if (result != returnvalue::OK) { + scheduling::printAddObjectError("XIPHOS_WDT", objects::XIPHOS_WDT); + } + PeriodicTaskIF* coreCtrlTask = factory->createPeriodicTask( "CORE_CTRL", 55, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.4, missedDeadlineFunc, &RR_SCHEDULING); result = coreCtrlTask->addComponent(objects::CORE_CONTROLLER); @@ -414,6 +424,7 @@ void scheduling::initTasks() { }; sif::info << "Starting tasks.." << std::endl; + xiphosWdtTask->startTask(); tmTcDistributor->startTask(); #if OBSW_ADD_TCPIP_SERVERS == 1 diff --git a/common/config/eive/objects.h b/common/config/eive/objects.h index 5d93dffc..d6b15b8d 100644 --- a/common/config/eive/objects.h +++ b/common/config/eive/objects.h @@ -28,6 +28,7 @@ enum commonObjects : uint32_t { CORE_CONTROLLER = 0x43000003, POWER_CONTROLLER = 0x43000004, GLOBAL_JSON_CFG = 0x43000006, + XIPHOS_WDT = 0x43000007, /* 0x44 ('D') for device handlers */ MGM_0_LIS3_HANDLER = 0x44120006, diff --git a/common/config/eive/resultClassIds.h b/common/config/eive/resultClassIds.h index 9e2a3f2f..35b2a419 100644 --- a/common/config/eive/resultClassIds.h +++ b/common/config/eive/resultClassIds.h @@ -41,6 +41,7 @@ enum commonClassIds : uint8_t { LOCAL_PARAM_HANDLER, // LPH PERSISTENT_TM_STORE, // PTM TM_SINK, // TMS + VIRTUAL_CHANNEL, // VCS COMMON_CLASS_ID_END // [EXPORT] : [END] }; } diff --git a/generators/bsp_hosted_objects.csv b/generators/bsp_hosted_objects.csv index 4cb21f46..689a58f2 100644 --- a/generators/bsp_hosted_objects.csv +++ b/generators/bsp_hosted_objects.csv @@ -3,6 +3,7 @@ 0x43000003;CORE_CONTROLLER 0x43000004;POWER_CONTROLLER 0x43000006;GLOBAL_JSON_CFG +0x43000007;XIPHOS_WDT 0x43400001;THERMAL_CONTROLLER 0x44000001;DUMMY_HANDLER 0x44120006;MGM_0_LIS3_HANDLER diff --git a/generators/bsp_hosted_returnvalues.csv b/generators/bsp_hosted_returnvalues.csv index fd428bfb..e8d79402 100644 --- a/generators/bsp_hosted_returnvalues.csv +++ b/generators/bsp_hosted_returnvalues.csv @@ -520,3 +520,6 @@ Full ID (hex); Name; Description; Unique ID; Subsytem Name; File Path 0x6e00;PTM_DumpDone;No description;0;PERSISTENT_TM_STORE;mission/tmtc/PersistentTmStore.h 0x6e01;PTM_BusyDumping;No description;1;PERSISTENT_TM_STORE;mission/tmtc/PersistentTmStore.h 0x6f00;TMS_IsBusy;No description;0;TM_SINK;mission/tmtc/DirectTmSinkIF.h +0x6f01;TMS_PartiallyWritten;No description;1;TM_SINK;mission/tmtc/DirectTmSinkIF.h +0x6f02;TMS_IncompletePartialWrite;No description;2;TM_SINK;mission/tmtc/DirectTmSinkIF.h +0x7000;VCS_ChannelDoesNotExist;No description;0;VIRTUAL_CHANNEL;mission/com/VirtualChannel.h diff --git a/generators/bsp_q7s_objects.csv b/generators/bsp_q7s_objects.csv index 914b7df6..10cfed26 100644 --- a/generators/bsp_q7s_objects.csv +++ b/generators/bsp_q7s_objects.csv @@ -3,6 +3,7 @@ 0x43000003;CORE_CONTROLLER 0x43000004;POWER_CONTROLLER 0x43000006;GLOBAL_JSON_CFG +0x43000007;XIPHOS_WDT 0x43400001;THERMAL_CONTROLLER 0x44120006;MGM_0_LIS3_HANDLER 0x44120010;GYRO_0_ADIS_HANDLER diff --git a/generators/bsp_q7s_returnvalues.csv b/generators/bsp_q7s_returnvalues.csv index 48e1e326..0d08c2b7 100644 --- a/generators/bsp_q7s_returnvalues.csv +++ b/generators/bsp_q7s_returnvalues.csv @@ -615,4 +615,7 @@ Full ID (hex); Name; Description; Unique ID; Subsytem Name; File Path 0x6e00;PTM_DumpDone;No description;0;PERSISTENT_TM_STORE;mission/tmtc/PersistentTmStore.h 0x6e01;PTM_BusyDumping;No description;1;PERSISTENT_TM_STORE;mission/tmtc/PersistentTmStore.h 0x6f00;TMS_IsBusy;No description;0;TM_SINK;mission/tmtc/DirectTmSinkIF.h -0x7100;SCBU_KeyNotFound;No description;0;SCRATCH_BUFFER;bsp_q7s/memory/scratchApi.h +0x6f01;TMS_PartiallyWritten;No description;1;TM_SINK;mission/tmtc/DirectTmSinkIF.h +0x6f02;TMS_IncompletePartialWrite;No description;2;TM_SINK;mission/tmtc/DirectTmSinkIF.h +0x7000;VCS_ChannelDoesNotExist;No description;0;VIRTUAL_CHANNEL;mission/com/VirtualChannel.h +0x7200;SCBU_KeyNotFound;No description;0;SCRATCH_BUFFER;bsp_q7s/memory/scratchApi.h diff --git a/generators/events/translateEvents.cpp b/generators/events/translateEvents.cpp index e42457ce..56112520 100644 --- a/generators/events/translateEvents.cpp +++ b/generators/events/translateEvents.cpp @@ -1,7 +1,7 @@ /** * @brief Auto-generated event translation file. Contains 314 translations. * @details - * Generated on: 2023-10-11 12:58:28 + * Generated on: 2023-10-13 09:44:05 */ #include "translateEvents.h" diff --git a/generators/objects/translateObjects.cpp b/generators/objects/translateObjects.cpp index a45fa281..96e07335 100644 --- a/generators/objects/translateObjects.cpp +++ b/generators/objects/translateObjects.cpp @@ -1,8 +1,8 @@ /** * @brief Auto-generated object translation file. * @details - * Contains 177 translations. - * Generated on: 2023-10-11 12:58:28 + * Contains 178 translations. + * Generated on: 2023-10-13 09:44:05 */ #include "translateObjects.h" @@ -11,6 +11,7 @@ const char *ACS_CONTROLLER_STRING = "ACS_CONTROLLER"; const char *CORE_CONTROLLER_STRING = "CORE_CONTROLLER"; const char *POWER_CONTROLLER_STRING = "POWER_CONTROLLER"; const char *GLOBAL_JSON_CFG_STRING = "GLOBAL_JSON_CFG"; +const char *XIPHOS_WDT_STRING = "XIPHOS_WDT"; const char *THERMAL_CONTROLLER_STRING = "THERMAL_CONTROLLER"; const char *MGM_0_LIS3_HANDLER_STRING = "MGM_0_LIS3_HANDLER"; const char *GYRO_0_ADIS_HANDLER_STRING = "GYRO_0_ADIS_HANDLER"; @@ -196,6 +197,8 @@ const char *translateObject(object_id_t object) { return POWER_CONTROLLER_STRING; case 0x43000006: return GLOBAL_JSON_CFG_STRING; + case 0x43000007: + return XIPHOS_WDT_STRING; case 0x43400001: return THERMAL_CONTROLLER_STRING; case 0x44120006: diff --git a/linux/fsfwconfig/events/translateEvents.cpp b/linux/fsfwconfig/events/translateEvents.cpp index e42457ce..56112520 100644 --- a/linux/fsfwconfig/events/translateEvents.cpp +++ b/linux/fsfwconfig/events/translateEvents.cpp @@ -1,7 +1,7 @@ /** * @brief Auto-generated event translation file. Contains 314 translations. * @details - * Generated on: 2023-10-11 12:58:28 + * Generated on: 2023-10-13 09:44:05 */ #include "translateEvents.h" diff --git a/linux/fsfwconfig/objects/translateObjects.cpp b/linux/fsfwconfig/objects/translateObjects.cpp index a45fa281..96e07335 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 177 translations. - * Generated on: 2023-10-11 12:58:28 + * Contains 178 translations. + * Generated on: 2023-10-13 09:44:05 */ #include "translateObjects.h" @@ -11,6 +11,7 @@ const char *ACS_CONTROLLER_STRING = "ACS_CONTROLLER"; const char *CORE_CONTROLLER_STRING = "CORE_CONTROLLER"; const char *POWER_CONTROLLER_STRING = "POWER_CONTROLLER"; const char *GLOBAL_JSON_CFG_STRING = "GLOBAL_JSON_CFG"; +const char *XIPHOS_WDT_STRING = "XIPHOS_WDT"; const char *THERMAL_CONTROLLER_STRING = "THERMAL_CONTROLLER"; const char *MGM_0_LIS3_HANDLER_STRING = "MGM_0_LIS3_HANDLER"; const char *GYRO_0_ADIS_HANDLER_STRING = "GYRO_0_ADIS_HANDLER"; @@ -196,6 +197,8 @@ const char *translateObject(object_id_t object) { return POWER_CONTROLLER_STRING; case 0x43000006: return GLOBAL_JSON_CFG_STRING; + case 0x43000007: + return XIPHOS_WDT_STRING; case 0x43400001: return THERMAL_CONTROLLER_STRING; case 0x44120006: diff --git a/linux/ipcore/PapbVcInterface.cpp b/linux/ipcore/PapbVcInterface.cpp index 5dcb4519..88684772 100644 --- a/linux/ipcore/PapbVcInterface.cpp +++ b/linux/ipcore/PapbVcInterface.cpp @@ -23,11 +23,15 @@ ReturnValue_t PapbVcInterface::initialize() { return returnvalue::OK; } -ReturnValue_t PapbVcInterface::write(const uint8_t* data, size_t size) { +ReturnValue_t PapbVcInterface::write(const uint8_t* data, size_t size, size_t& writtenSize) { // There are no packets smaller than 4, this is considered a configuration error. if (size < 4) { return returnvalue::FAILED; } + // The user must call finishWrite before starting a new packet transfer. + if (partialWriteActive) { + return INCOMPLETE_PARTIAL_WRITE; + } if (pollReadyForPacket()) { startPacketTransfer(ByteWidthCfg::ONE); } else { @@ -37,19 +41,7 @@ ReturnValue_t PapbVcInterface::write(const uint8_t* data, size_t size) { abortPacketTransfer(); return returnvalue::FAILED; } - for (size_t idx = 0; idx < size; idx++) { - if (not pollReadyForOctet(MAX_BUSY_POLLS)) { - abortPacketTransfer(); - return returnvalue::FAILED; - } - *(vcBaseReg + DATA_REG_OFFSET) = static_cast(data[idx]); - } - if (not pollReadyForOctet(MAX_BUSY_POLLS)) { - abortPacketTransfer(); - return returnvalue::FAILED; - } - completePacketTransfer(); - return returnvalue::OK; + return finishWriteInternal(data, 0, size, writtenSize, false); } void PapbVcInterface::startPacketTransfer(ByteWidthCfg initWidth) { @@ -65,6 +57,43 @@ bool PapbVcInterface::pollReadyForPacket() const { return (reg >> 6) & 0b1; } +ReturnValue_t PapbVcInterface::finishWrite(const uint8_t* data, size_t start, + size_t remainingSize) { + if (not pollReadyForPacket()) { + return returnvalue::FAILED; + } + size_t dummy = 0; + return finishWriteInternal(data, start, remainingSize, dummy, true); +} + +ReturnValue_t PapbVcInterface::finishWriteInternal(const uint8_t* data, size_t start, + size_t remainingSize, size_t& writtenSize, + bool abortOnPartialWrite) { + for (size_t idx = 0; idx < remainingSize; idx++) { + if (not pollReadyForOctet(MAX_BUSY_POLLS)) { + if (not pollReadyForPacket()) { + writtenSize = start + idx; + partialWriteActive = true; + if (abortOnPartialWrite) { + abortPacketTransfer(); + partialWriteActive = false; + return returnvalue::FAILED; + } + return PARTIALLY_WRITTEN; + } + abortPacketTransfer(); + return returnvalue::FAILED; + } + *(vcBaseReg + DATA_REG_OFFSET) = static_cast(data[start + idx]); + } + if (not pollReadyForOctet(MAX_BUSY_POLLS)) { + abortPacketTransfer(); + return returnvalue::FAILED; + } + completePacketTransfer(); + return returnvalue::OK; +} + bool PapbVcInterface::isVcInterfaceBufferEmpty() { ReturnValue_t result = returnvalue::OK; gpio::Levels papbEmptyState = gpio::Levels::HIGH; @@ -101,21 +130,4 @@ inline bool PapbVcInterface::pollReadyForOctet(uint32_t maxCycles) const { return false; } -ReturnValue_t PapbVcInterface::sendTestFrame() { - /** Size of one complete transfer frame data field amounts to 1105 bytes */ - uint8_t testPacket[1105]; - - /** Fill one test packet */ - for (int idx = 0; idx < 1105; idx++) { - testPacket[idx] = static_cast(idx & 0xFF); - } - - ReturnValue_t result = write(testPacket, 1105); - if (result != returnvalue::OK) { - return result; - } - - return returnvalue::OK; -} - void PapbVcInterface::abortPacketTransfer() { *vcBaseReg = CONFIG_ABORT; } diff --git a/linux/ipcore/PapbVcInterface.h b/linux/ipcore/PapbVcInterface.h index b5160748..35bd0439 100644 --- a/linux/ipcore/PapbVcInterface.h +++ b/linux/ipcore/PapbVcInterface.h @@ -40,7 +40,11 @@ class PapbVcInterface : public VirtualChannelIF { * @param size * @return returnvalue::OK on successfull write, PAPB_BUSY if PAPB is busy. */ - ReturnValue_t write(const uint8_t* data, size_t size) override; + ReturnValue_t write(const uint8_t* data, size_t size, size_t& writtenSize) override; + + ReturnValue_t finishWrite(const uint8_t* data, size_t start, size_t remainingSize) override; + ReturnValue_t finishWriteInternal(const uint8_t* data, size_t start, size_t remainingSize, + size_t& writtenSize, bool abortOnPartialWrite); void cancelTransfer() override; @@ -88,6 +92,7 @@ class PapbVcInterface : public VirtualChannelIF { std::string uioFile; int mapNum = 0; + bool partialWriteActive = false; mutable struct timespec nextDelay = {.tv_sec = 0, .tv_nsec = 0}; const struct timespec BETWEEN_POLL_DELAY = {.tv_sec = 0, .tv_nsec = 10}; mutable struct timespec remDelay; @@ -126,12 +131,6 @@ class PapbVcInterface : public VirtualChannelIF { * the packet buffer of the virtual channel or not. */ bool isVcInterfaceBufferEmpty(); - - /** - * @brief This function sends a complete telemetry transfer frame data field (1105 bytes) - * to the papb interface of the PTME IP Core. Can be used to test the implementation. - */ - ReturnValue_t sendTestFrame(); }; #endif /* LINUX_OBC_PAPBVCINTERFACE_H_ */ diff --git a/linux/ipcore/Ptme.cpp b/linux/ipcore/Ptme.cpp index f33c8725..9b688d16 100644 --- a/linux/ipcore/Ptme.cpp +++ b/linux/ipcore/Ptme.cpp @@ -19,15 +19,12 @@ ReturnValue_t Ptme::initialize() { return returnvalue::OK; } -ReturnValue_t Ptme::writeToVc(uint8_t vcId, const uint8_t* data, size_t size) { - VcInterfaceMapIter vcInterfaceMapIter = vcInterfaceMap.find(vcId); - if (vcInterfaceMapIter == vcInterfaceMap.end()) { - sif::warning << "Ptme::writeToVc: No virtual channel interface found for the virtual " - "channel with id " - << static_cast(vcId) << std::endl; - return UNKNOWN_VC_ID; +bool Ptme::containsVc(uint8_t vcId) const { + auto channelIter = vcInterfaceMap.find(vcId); + if (channelIter == vcInterfaceMap.end()) { + return false; } - return vcInterfaceMapIter->second->write(data, size); + return true; } void Ptme::addVcInterface(VcId_t vcId, VirtualChannelIF* vc) { @@ -50,21 +47,10 @@ void Ptme::addVcInterface(VcId_t vcId, VirtualChannelIF* vc) { } } -bool Ptme::isBusy(uint8_t vcId) const { - const auto& vcInterfaceMapIter = vcInterfaceMap.find(vcId); - if (vcInterfaceMapIter == vcInterfaceMap.end()) { - sif::warning << "Ptme::writeToVc: No virtual channel interface found for the virtual " - "channel with id " - << static_cast(vcId) << std::endl; - return UNKNOWN_VC_ID; +VirtualChannelIF* Ptme::getVirtChannel(uint8_t vcId) { + auto channelIter = vcInterfaceMap.find(vcId); + if (channelIter == vcInterfaceMap.end()) { + return nullptr; } - return vcInterfaceMapIter->second->isBusy(); -} - -void Ptme::cancelTransfer(uint8_t vcId) { - VcInterfaceMapIter vcInterfaceMapIter = vcInterfaceMap.find(vcId); - if (vcInterfaceMapIter == vcInterfaceMap.end()) { - return; - } - return vcInterfaceMapIter->second->cancelTransfer(); + return channelIter->second; } diff --git a/linux/ipcore/Ptme.h b/linux/ipcore/Ptme.h index 2607fc90..d5e77024 100644 --- a/linux/ipcore/Ptme.h +++ b/linux/ipcore/Ptme.h @@ -34,9 +34,8 @@ class Ptme : public PtmeIF, public SystemObject { virtual ~Ptme(); ReturnValue_t initialize() override; - ReturnValue_t writeToVc(uint8_t vcId, const uint8_t* data, size_t size) override; - bool isBusy(uint8_t vcId) const override; - void cancelTransfer(uint8_t vcId) override; + bool containsVc(uint8_t vcId) const override; + VirtualChannelIF* getVirtChannel(uint8_t vcId) override; /** * @brief This function adds the reference to a virtual channel interface to the vcInterface diff --git a/linux/ipcore/PtmeIF.h b/linux/ipcore/PtmeIF.h index 38804484..a5ea0c95 100644 --- a/linux/ipcore/PtmeIF.h +++ b/linux/ipcore/PtmeIF.h @@ -1,6 +1,8 @@ #ifndef LINUX_OBC_PTMEIF_H_ #define LINUX_OBC_PTMEIF_H_ +#include + #include "fsfw/returnvalues/returnvalue.h" /** @@ -14,16 +16,8 @@ class PtmeIF { public: virtual ~PtmeIF(){}; - /** - * @brief Implements to function to write to a specific virtual channel. - * - * @param vcId Virtual channel to write to - * @param data Pointer to buffer holding the data to write - * @param size Number of bytes to write - */ - virtual ReturnValue_t writeToVc(uint8_t vcId, const uint8_t* data, size_t size) = 0; - virtual bool isBusy(uint8_t vcId) const = 0; - virtual void cancelTransfer(uint8_t vcId) = 0; + virtual bool containsVc(uint8_t vcId) const = 0; + virtual VirtualChannelIF* getVirtChannel(uint8_t vcId) = 0; }; #endif /* LINUX_OBC_PTMEIF_H_ */ diff --git a/mission/com/TmStoreTaskBase.cpp b/mission/com/TmStoreTaskBase.cpp index 80900975..c51d6f9b 100644 --- a/mission/com/TmStoreTaskBase.cpp +++ b/mission/com/TmStoreTaskBase.cpp @@ -138,8 +138,17 @@ ReturnValue_t TmStoreTaskBase::performDump(PersistentTmStoreWithTmQueue& store, return result; } dumpedLen = tmReader.getFullPacketLen(); - result = channel.write(tmReader.getFullData(), dumpedLen); - if (result == DirectTmSinkIF::IS_BUSY) { + size_t partiallyWrittenSize = 0; + result = channel.write(tmReader.getFullData(), dumpedLen, partiallyWrittenSize); + if (result == VirtualChannelIF::PARTIALLY_WRITTEN) { + result = channel.handleLastWriteSynchronously(tmReader.getFullData(), partiallyWrittenSize, + dumpedLen - partiallyWrittenSize, 200); + if (result != returnvalue::OK) { + // TODO: Event? Might lead to dangerous spam though.. + sif::warning << "PersistentTmStore: Synchronous write of last segment failed with code 0x" + << std::setw(4) << std::hex << result << std::dec << std::endl; + } + } else if (result == DirectTmSinkIF::IS_BUSY) { sif::warning << "PersistentTmStore: Unexpected VC channel busy" << std::endl; } else if (result != returnvalue::OK) { sif::warning << "PersistentTmStore: Unexpected VC channel write failure" << std::endl; diff --git a/mission/com/VirtualChannel.cpp b/mission/com/VirtualChannel.cpp index ff3749a9..140b23f6 100644 --- a/mission/com/VirtualChannel.cpp +++ b/mission/com/VirtualChannel.cpp @@ -1,25 +1,64 @@ #include "VirtualChannel.h" +#include + VirtualChannel::VirtualChannel(object_id_t objectId, uint8_t vcId, const char* vcName, PtmeIF& ptme, const std::atomic_bool& txOn) : SystemObject(objectId), ptme(ptme), vcId(vcId), vcName(vcName), txOn(txOn) {} ReturnValue_t VirtualChannel::initialize() { return returnvalue::OK; } -ReturnValue_t VirtualChannel::sendNextTm(const uint8_t* data, size_t size) { - return write(data, size); +ReturnValue_t VirtualChannel::sendNextTm(const uint8_t* data, size_t size, size_t& writtenSize) { + return write(data, size, writtenSize); } -ReturnValue_t VirtualChannel::write(const uint8_t* data, size_t size) { - return ptme.writeToVc(vcId, data, size); +ReturnValue_t VirtualChannel::write(const uint8_t* data, size_t size, size_t& writtenSize) { + if (!ptme.containsVc(vcId)) { + return CHANNEL_DOES_NOT_EXIST; + } + return ptme.getVirtChannel(vcId)->write(data, size, writtenSize); } uint8_t VirtualChannel::getVcid() const { return vcId; } +ReturnValue_t VirtualChannel::finishWrite(const uint8_t* data, size_t start, size_t remainingSize) { + if (!ptme.containsVc(vcId)) { + return CHANNEL_DOES_NOT_EXIST; + } + return ptme.getVirtChannel(vcId)->finishWrite(data, start, remainingSize); +} + const char* VirtualChannel::getName() const { return vcName.c_str(); } -bool VirtualChannel::isBusy() const { return ptme.isBusy(vcId); } +bool VirtualChannel::isBusy() const { + if (!ptme.containsVc(vcId)) { + return CHANNEL_DOES_NOT_EXIST; + } + return ptme.getVirtChannel(vcId)->isBusy(); +} -void VirtualChannel::cancelTransfer() { ptme.cancelTransfer(vcId); } +void VirtualChannel::cancelTransfer() { + if (!ptme.containsVc(vcId)) { + return; + } + ptme.getVirtChannel(vcId)->cancelTransfer(); +} bool VirtualChannel::isTxOn() const { return txOn; } + +ReturnValue_t VirtualChannel::handleLastWriteSynchronously(const uint8_t* data, size_t start, + size_t remLen, unsigned maxDelayMs) { + unsigned delayMs = 0; + while (true) { + if (isBusy()) { + if (delayMs >= maxDelayMs) { + break; + } + TaskFactory::delayTask(10); + delayMs += 10; + continue; + } + return finishWrite(data, start, remLen); + } + return returnvalue::FAILED; +} diff --git a/mission/com/VirtualChannel.h b/mission/com/VirtualChannel.h index 98aba903..3d61bd13 100644 --- a/mission/com/VirtualChannel.h +++ b/mission/com/VirtualChannel.h @@ -15,6 +15,10 @@ */ class VirtualChannel : public SystemObject, public VirtualChannelIF { public: + static constexpr uint8_t CLASS_ID = CLASS_ID::VIRTUAL_CHANNEL; + + static constexpr ReturnValue_t CHANNEL_DOES_NOT_EXIST = returnvalue::makeCode(CLASS_ID, 0); + /** * @brief Constructor * @@ -25,9 +29,12 @@ class VirtualChannel : public SystemObject, public VirtualChannelIF { const std::atomic_bool& linkStateProvider); ReturnValue_t initialize() override; - ReturnValue_t sendNextTm(const uint8_t* data, size_t size); + ReturnValue_t sendNextTm(const uint8_t* data, size_t size, size_t& writtenSize); bool isBusy() const override; - ReturnValue_t write(const uint8_t* data, size_t size) override; + ReturnValue_t write(const uint8_t* data, size_t size, size_t& writtenSize) override; + ReturnValue_t finishWrite(const uint8_t* data, size_t start, size_t remainingSize) override; + ReturnValue_t handleLastWriteSynchronously(const uint8_t* data, size_t start, size_t remLen, + unsigned maxDelayMs); void cancelTransfer() override; uint8_t getVcid() const; bool isTxOn() const; diff --git a/mission/com/VirtualChannelWithQueue.cpp b/mission/com/VirtualChannelWithQueue.cpp index a90829ab..53f2ee52 100644 --- a/mission/com/VirtualChannelWithQueue.cpp +++ b/mission/com/VirtualChannelWithQueue.cpp @@ -36,8 +36,19 @@ ReturnValue_t VirtualChannelWithQueue::handleNextTm(bool performWriteOp) { return result; } + // TODO: Hnadle partial write handling + size_t partiallyWrittenSize = 0; if (performWriteOp) { - result = write(data, size); + result = write(data, size, partiallyWrittenSize); + if (result == PARTIALLY_WRITTEN) { + result = handleLastWriteSynchronously(data, size, partiallyWrittenSize, 200); + if (result != returnvalue::OK) { + // TODO: Event? Might lead to dangerous spam though.. + sif::warning + << "VirtualChannelWithQueue: Synchronous write of last segment failed with code 0x" + << std::setw(4) << std::hex << result << std::dec << std::endl; + } + } } // Try delete in any case, ignore failures (which should not happen), it is more important to // propagate write errors. diff --git a/mission/controller/CMakeLists.txt b/mission/controller/CMakeLists.txt index 901decc0..690ede64 100644 --- a/mission/controller/CMakeLists.txt +++ b/mission/controller/CMakeLists.txt @@ -1,7 +1,7 @@ if(TGT_BSP MATCHES "arm/q7s" OR TGT_BSP MATCHES "") - target_sources(${LIB_EIVE_MISSION} PRIVATE ThermalController.cpp - AcsController.cpp - PowerController.cpp) + target_sources( + ${LIB_EIVE_MISSION} PRIVATE ThermalController.cpp AcsController.cpp + PowerController.cpp) endif() add_subdirectory(acs) diff --git a/mission/payload/ScexDeviceHandler.cpp b/mission/payload/ScexDeviceHandler.cpp index 12c62f7a..ac668cdf 100644 --- a/mission/payload/ScexDeviceHandler.cpp +++ b/mission/payload/ScexDeviceHandler.cpp @@ -38,9 +38,13 @@ void ScexDeviceHandler::doShutDown() { setMode(_MODE_POWER_DOWN); } -ReturnValue_t ScexDeviceHandler::buildNormalDeviceCommand(DeviceCommandId_t* id) { return OK; } +ReturnValue_t ScexDeviceHandler::buildNormalDeviceCommand(DeviceCommandId_t* id) { + return NOTHING_TO_SEND; +} -ReturnValue_t ScexDeviceHandler::buildTransitionDeviceCommand(DeviceCommandId_t* id) { return OK; } +ReturnValue_t ScexDeviceHandler::buildTransitionDeviceCommand(DeviceCommandId_t* id) { + return NOTHING_TO_SEND; +} ReturnValue_t ScexDeviceHandler::buildCommandFromCommand(DeviceCommandId_t deviceCommand, const uint8_t* commandData, @@ -269,7 +273,7 @@ ReturnValue_t ScexDeviceHandler::interpretDeviceReply(DeviceCommandId_t id, cons // Unknown DeviceCommand return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED; } - if (helper.getPacketCounter() == helper.getTotalPacketCounter()) { + if (helper.getPacketCounter() >= helper.getTotalPacketCounter()) { reader.finish(); commandActive = false; if (id != PING) { diff --git a/mission/system/power/CMakeLists.txt b/mission/system/power/CMakeLists.txt index a7e91e05..2c720978 100644 --- a/mission/system/power/CMakeLists.txt +++ b/mission/system/power/CMakeLists.txt @@ -1 +1,2 @@ -target_sources(${LIB_EIVE_MISSION} PRIVATE epsModeTree.cpp EpsSubsystem.cpp GomspacePowerFdir.cpp) +target_sources(${LIB_EIVE_MISSION} PRIVATE epsModeTree.cpp EpsSubsystem.cpp + GomspacePowerFdir.cpp) diff --git a/mission/tmtc/DirectTmSinkIF.h b/mission/tmtc/DirectTmSinkIF.h index 11a17c79..e1fcf54f 100644 --- a/mission/tmtc/DirectTmSinkIF.h +++ b/mission/tmtc/DirectTmSinkIF.h @@ -13,6 +13,8 @@ class DirectTmSinkIF { static constexpr uint8_t CLASS_ID = CLASS_ID::TM_SINK; static constexpr ReturnValue_t IS_BUSY = returnvalue::makeCode(CLASS_ID, 0); + static constexpr ReturnValue_t PARTIALLY_WRITTEN = returnvalue::makeCode(CLASS_ID, 1); + static constexpr ReturnValue_t INCOMPLETE_PARTIAL_WRITE = returnvalue::makeCode(CLASS_ID, 2); /** * @brief Implements the functionality to write to a TM sink directly @@ -20,9 +22,12 @@ class DirectTmSinkIF { * @param data Pointer to buffer holding the data to write * @param size Number of bytes to write * @return returnvalue::OK on success, returnvalue::FAILED on failure, IS_BUSY - * if the TM sink is busy. + * if the TM sink is busy, PARTIALLY_WRITTEN if only a portion of the bytes could be + * written. */ - virtual ReturnValue_t write(const uint8_t* data, size_t size) = 0; + virtual ReturnValue_t write(const uint8_t* data, size_t size, size_t& writtenSize) = 0; + + virtual ReturnValue_t finishWrite(const uint8_t* data, size_t start, size_t remainingSize) = 0; virtual bool isBusy() const = 0; }; diff --git a/tmtc b/tmtc index 012e9b14..10e163be 160000 --- a/tmtc +++ b/tmtc @@ -1 +1 @@ -Subproject commit 012e9b148e882cea78fdfce192ac3d9e3034f342 +Subproject commit 10e163be752a6a259b6d3dabea825acc9c9725f8