Merge branch 'refactor-fix-ptme' into cfdp-source-handler
Some checks failed
EIVE/eive-obsw/pipeline/pr-main There was a failure building this commit

This commit is contained in:
Robin Müller 2023-10-13 09:45:21 +02:00
commit 5bc468f13f
Signed by: muellerr
GPG Key ID: A649FB78196E3849
34 changed files with 392 additions and 113 deletions

View File

@ -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

View File

@ -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)

View File

@ -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"

View File

@ -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:

View File

@ -1 +1,2 @@
target_sources(${OBSW_NAME} PRIVATE CoreController.cpp WatchdogHandler.cpp)
target_sources(${OBSW_NAME} PRIVATE CoreController.cpp WatchdogHandler.cpp
XiphosWdtHandler.cpp)

View File

@ -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(); }

View File

@ -0,0 +1,36 @@
#ifndef BSP_Q7S_CORE_XIPHOSWDTHANDLER_H_
#define BSP_Q7S_CORE_XIPHOSWDTHANDLER_H_
#include <fsfw/action/HasActionsIF.h>
#include <fsfw/objectmanager/SystemObject.h>
#include <fsfw/tasks/ExecutableObjectIF.h>
#include <libxiphos.h>
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_ */

View File

@ -1,4 +1,5 @@
#include <bsp_q7s/callbacks/q7sGpioCallbacks.h>
#include <bsp_q7s/core/XiphosWdtHandler.h>
#include <bsp_q7s/objectFactory.h>
#include <dummies/ComCookieDummy.h>
#include <dummies/PcduHandlerDummy.h>
@ -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);

View File

@ -1,4 +1,5 @@
#include <bsp_q7s/callbacks/q7sGpioCallbacks.h>
#include <bsp_q7s/core/XiphosWdtHandler.h>
#include <bsp_q7s/objectFactory.h>
#include <devices/gpioIds.h>
#include <fsfw/storagemanager/LocalPool.h>
@ -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);

View File

@ -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

View File

@ -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,

View File

@ -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]
};
}

View File

@ -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

1 0x42694269 TEST_TASK
3 0x43000003 CORE_CONTROLLER
4 0x43000004 POWER_CONTROLLER
5 0x43000006 GLOBAL_JSON_CFG
6 0x43000007 XIPHOS_WDT
7 0x43400001 THERMAL_CONTROLLER
8 0x44000001 DUMMY_HANDLER
9 0x44120006 MGM_0_LIS3_HANDLER

View File

@ -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

1 Full ID (hex) Name Description Unique ID Subsytem Name File Path
520 0x6e00 PTM_DumpDone No description 0 PERSISTENT_TM_STORE mission/tmtc/PersistentTmStore.h
521 0x6e01 PTM_BusyDumping No description 1 PERSISTENT_TM_STORE mission/tmtc/PersistentTmStore.h
522 0x6f00 TMS_IsBusy No description 0 TM_SINK mission/tmtc/DirectTmSinkIF.h
523 0x6f01 TMS_PartiallyWritten No description 1 TM_SINK mission/tmtc/DirectTmSinkIF.h
524 0x6f02 TMS_IncompletePartialWrite No description 2 TM_SINK mission/tmtc/DirectTmSinkIF.h
525 0x7000 VCS_ChannelDoesNotExist No description 0 VIRTUAL_CHANNEL mission/com/VirtualChannel.h

View File

@ -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

1 0x00005060 P60DOCK_TEST_TASK
3 0x43000003 CORE_CONTROLLER
4 0x43000004 POWER_CONTROLLER
5 0x43000006 GLOBAL_JSON_CFG
6 0x43000007 XIPHOS_WDT
7 0x43400001 THERMAL_CONTROLLER
8 0x44120006 MGM_0_LIS3_HANDLER
9 0x44120010 GYRO_0_ADIS_HANDLER

View File

@ -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

1 Full ID (hex) Name Description Unique ID Subsytem Name File Path
615 0x6e00 PTM_DumpDone No description 0 PERSISTENT_TM_STORE mission/tmtc/PersistentTmStore.h
616 0x6e01 PTM_BusyDumping No description 1 PERSISTENT_TM_STORE mission/tmtc/PersistentTmStore.h
617 0x6f00 TMS_IsBusy No description 0 TM_SINK mission/tmtc/DirectTmSinkIF.h
618 0x7100 0x6f01 SCBU_KeyNotFound TMS_PartiallyWritten No description 0 1 SCRATCH_BUFFER TM_SINK bsp_q7s/memory/scratchApi.h mission/tmtc/DirectTmSinkIF.h
619 0x6f02 TMS_IncompletePartialWrite No description 2 TM_SINK mission/tmtc/DirectTmSinkIF.h
620 0x7000 VCS_ChannelDoesNotExist No description 0 VIRTUAL_CHANNEL mission/com/VirtualChannel.h
621 0x7200 SCBU_KeyNotFound No description 0 SCRATCH_BUFFER bsp_q7s/memory/scratchApi.h

View File

@ -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"

View File

@ -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:

View File

@ -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"

View File

@ -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:

View File

@ -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<uint32_t>(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<uint32_t>(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<uint8_t>(idx & 0xFF);
}
ReturnValue_t result = write(testPacket, 1105);
if (result != returnvalue::OK) {
return result;
}
return returnvalue::OK;
}
void PapbVcInterface::abortPacketTransfer() { *vcBaseReg = CONFIG_ABORT; }

View File

@ -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_ */

View File

@ -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<unsigned int>(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<unsigned int>(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;
}

View File

@ -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

View File

@ -1,6 +1,8 @@
#ifndef LINUX_OBC_PTMEIF_H_
#define LINUX_OBC_PTMEIF_H_
#include <linux/ipcore/VirtualChannelIF.h>
#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_ */

View File

@ -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;

View File

@ -1,25 +1,64 @@
#include "VirtualChannel.h"
#include <fsfw/tasks/TaskFactory.h>
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;
}

View File

@ -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;

View File

@ -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.

View File

@ -1,6 +1,6 @@
if(TGT_BSP MATCHES "arm/q7s" OR TGT_BSP MATCHES "")
target_sources(${LIB_EIVE_MISSION} PRIVATE ThermalController.cpp
AcsController.cpp
target_sources(
${LIB_EIVE_MISSION} PRIVATE ThermalController.cpp AcsController.cpp
PowerController.cpp)
endif()

View File

@ -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) {

View File

@ -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)

View File

@ -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;
};

2
tmtc

@ -1 +1 @@
Subproject commit 012e9b148e882cea78fdfce192ac3d9e3034f342
Subproject commit 10e163be752a6a259b6d3dabea825acc9c9725f8