Compare commits

..
59 changed files with 147 additions and 997 deletions

No files matched your search

+1 -5
View File
@@ -26,8 +26,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## Added ## Added
- `hk::Dataset::deSerializeWithValidityBlob`, the counterpart of serializing with validity blob
- functions to configure pus routing
- FreeRTOS monotonic clock which is not subjected to time jumps of the system clock - FreeRTOS monotonic clock which is not subjected to time jumps of the system clock
- add CFDP subsystem ID - add CFDP subsystem ID
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/742 https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/742
@@ -37,7 +35,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## Changed ## Changed
- send HK one-parameter-report back to sender instead of default hk queue
- Complete overhaul of HK subsystem. Replaced local data pool manager by periodic HK - Complete overhaul of HK subsystem. Replaced local data pool manager by periodic HK
helper. The shared pool and the periodic HK generation are now distinct concepts. helper. The shared pool and the periodic HK generation are now distinct concepts.
- The local HK manager was replaced by a periodic HK helper which has reduced responsibilities. - The local HK manager was replaced by a periodic HK helper which has reduced responsibilities.
@@ -71,8 +68,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
configurable. configurable.
- Switched to vendored versions for both the Embedded Template Library (ETL) and the - Switched to vendored versions for both the Embedded Template Library (ETL) and the
Catch2 unittesting library. Catch2 unittesting library.
- Increased maximum number of mode tables from 70 to 100
- Exposed health table mutex via getter function
## Added ## Added
- `EventManager`: Add function to print all listeners. - `EventManager`: Add function to print all listeners.
+2 -2
View File
@@ -18,13 +18,13 @@ class MgmRM3100Handler : public DeviceHandlerBase {
static const uint8_t INTERFACE_ID = CLASS_ID::MGM_RM3100; static const uint8_t INTERFACE_ID = CLASS_ID::MGM_RM3100;
//! [EXPORT] : [COMMENT] P1: TMRC value which was set, P2: 0 //! [EXPORT] : [COMMENT] P1: TMRC value which was set, P2: 0
static constexpr Event tmrcSet = event::makeEvent<SUBSYSTEM_ID::MGM_RM3100, 0x00, severity::INFO>(); static constexpr Event tmrcSet = event::makeEvent(SUBSYSTEM_ID::MGM_RM3100, 0x00, severity::INFO);
//! [EXPORT] : [COMMENT] Cycle counter set. P1: First two bytes new Cycle Count X //! [EXPORT] : [COMMENT] Cycle counter set. P1: First two bytes new Cycle Count X
//! P1: Second two bytes new Cycle Count Y //! P1: Second two bytes new Cycle Count Y
//! P2: New cycle count Z //! P2: New cycle count Z
static constexpr Event cycleCountersSet = static constexpr Event cycleCountersSet =
event::makeEvent<SUBSYSTEM_ID::MGM_RM3100, 0x01, severity::INFO>(); event::makeEvent(SUBSYSTEM_ID::MGM_RM3100, 0x01, severity::INFO);
MgmRM3100Handler(object_id_t objectId, object_id_t deviceCommunication, CookieIF *comCookie, MgmRM3100Handler(object_id_t objectId, object_id_t deviceCommunication, CookieIF *comCookie,
uint32_t transitionDelay); uint32_t transitionDelay);
-1
View File
@@ -71,7 +71,6 @@ static constexpr size_t FSFW_EVENTMGMR_RANGEMATCHERS = 120;
static constexpr uint8_t FSFW_CSB_FIFO_DEPTH = 6; static constexpr uint8_t FSFW_CSB_FIFO_DEPTH = 6;
static constexpr size_t FSFW_PRINT_BUFFER_SIZE = 124; static constexpr size_t FSFW_PRINT_BUFFER_SIZE = 124;
static constexpr size_t FSFW_PRINT_BUFFER_AMOUNT = 32;
static constexpr size_t FSFW_MAX_TM_PACKET_SIZE = 2048; static constexpr size_t FSFW_MAX_TM_PACKET_SIZE = 2048;
+2 -2
View File
@@ -18,12 +18,12 @@ else
echo "No ${cmake_fmt} tool found, not formatting CMake files" echo "No ${cmake_fmt} tool found, not formatting CMake files"
fi fi
cpp_format="clang-format-19" cpp_format="clang-format"
file_selectors="-iname *.h -o -iname *.cpp -o -iname *.c -o -iname *.tpp" file_selectors="-iname *.h -o -iname *.cpp -o -iname *.c -o -iname *.tpp"
if command -v ${cpp_format} &> /dev/null; then if command -v ${cpp_format} &> /dev/null; then
for dir in ${folder_list[@]}; do for dir in ${folder_list[@]}; do
echo "Auto-formatting ${dir} recursively" echo "Auto-formatting ${dir} recursively"
find ${dir} ${file_selectors} | xargs ${cpp_format} --style=file -i find ${dir} ${file_selectors} | xargs clang-format --style=file -i
done done
else else
echo "No ${cpp_format} tool found, not formatting C++/C files" echo "No ${cpp_format} tool found, not formatting C++/C files"
+1 -18
View File
@@ -9,9 +9,7 @@
#include "fsfw/cfdp/pdu/FileDataReader.h" #include "fsfw/cfdp/pdu/FileDataReader.h"
#include "fsfw/cfdp/pdu/FinishedPduCreator.h" #include "fsfw/cfdp/pdu/FinishedPduCreator.h"
#include "fsfw/cfdp/pdu/HeaderReader.h" #include "fsfw/cfdp/pdu/HeaderReader.h"
#include "fsfw/cfdp/pdu/KeepAlivePduCreator.h"
#include "fsfw/objectmanager.h" #include "fsfw/objectmanager.h"
#include "fsfw/returnvalues/returnvalue.h"
#include "fsfw/tmtcservices/TmTcMessage.h" #include "fsfw/tmtcservices/TmTcMessage.h"
using namespace returnvalue; using namespace returnvalue;
@@ -452,19 +450,6 @@ ReturnValue_t cfdp::DestHandler::noticeOfCompletion() {
return OK; return OK;
} }
ReturnValue_t cfdp::DestHandler::sendKeepAlivePdu() {
Fss progress(transactionParams.progress);
KeepAlivePduCreator keepAlivePdu(transactionParams.pduConf, progress);
size_t serLen = 0;
ReturnValue_t result =
keepAlivePdu.serialize(pduBuf.data(), serLen, keepAlivePdu.getSerializedSize());
if (result != OK) {
return result;
}
return pduSender.sendPdu(PduType::FILE_DIRECTIVE, FileDirective::KEEP_ALIVE, pduBuf.data(),
serLen);
}
ReturnValue_t cfdp::DestHandler::sendFinishedPdu() { ReturnValue_t cfdp::DestHandler::sendFinishedPdu() {
FinishedInfo info(transactionParams.conditionCode, transactionParams.deliveryCode, FinishedInfo info(transactionParams.conditionCode, transactionParams.deliveryCode,
transactionParams.deliveryStatus); transactionParams.deliveryStatus);
@@ -511,8 +496,6 @@ const cfdp::TransactionId& cfdp::DestHandler::getTransactionId() const {
return transactionParams.transactionId; return transactionParams.transactionId;
} }
uint64_t cfdp::DestHandler::getProgress() const { return transactionParams.progress; }
void cfdp::DestHandler::checkAndHandleError(ReturnValue_t result, uint8_t& errorIdx) { void cfdp::DestHandler::checkAndHandleError(ReturnValue_t result, uint8_t& errorIdx) {
if (result != OK and errorIdx < 3) { if (result != OK and errorIdx < 3) {
fsmRes.errorCodes[errorIdx] = result; fsmRes.errorCodes[errorIdx] = result;
@@ -526,4 +509,4 @@ void cfdp::DestHandler::setEventReporter(EventReportingProxyIF& reporter) {
const cfdp::DestHandlerParams& cfdp::DestHandler::getDestHandlerParams() const { const cfdp::DestHandlerParams& cfdp::DestHandler::getDestHandlerParams() const {
return destParams; return destParams;
} }
-2
View File
@@ -101,8 +101,6 @@ class DestHandler {
[[nodiscard]] CfdpState getCfdpState() const; [[nodiscard]] CfdpState getCfdpState() const;
[[nodiscard]] TransactionStep getTransactionStep() const; [[nodiscard]] TransactionStep getTransactionStep() const;
[[nodiscard]] uint64_t getProgress() const;
ReturnValue_t sendKeepAlivePdu();
[[nodiscard]] const TransactionId& getTransactionId() const; [[nodiscard]] const TransactionId& getTransactionId() const;
[[nodiscard]] const DestHandlerParams& getDestHandlerParams() const; [[nodiscard]] const DestHandlerParams& getDestHandlerParams() const;
+5 -5
View File
@@ -19,13 +19,13 @@ struct FsfwParams {
}; };
namespace events { namespace events {
static constexpr Event PDU_SEND_ERROR = event::makeEvent<SSID, 1, severity::LOW>(); static constexpr Event PDU_SEND_ERROR = event::makeEvent(SSID, 1, severity::LOW);
static constexpr Event SERIALIZATION_ERROR = event::makeEvent<SSID, 2, severity::LOW>(); static constexpr Event SERIALIZATION_ERROR = event::makeEvent(SSID, 2, severity::LOW);
static constexpr Event FILESTORE_ERROR = event::makeEvent<SSID, 3, severity::LOW>(); static constexpr Event FILESTORE_ERROR = event::makeEvent(SSID, 3, severity::LOW);
//! [EXPORT] : [COMMENT] P1: Transaction step ID, P2: 0 for source file name, 1 for dest file name //! [EXPORT] : [COMMENT] P1: Transaction step ID, P2: 0 for source file name, 1 for dest file name
static constexpr Event FILENAME_TOO_LARGE_ERROR = event::makeEvent<SSID, 4, severity::LOW>(); static constexpr Event FILENAME_TOO_LARGE_ERROR = event::makeEvent(SSID, 4, severity::LOW);
//! [EXPORT] : [COMMENT] CFDP request handling failed. P2: Returncode. //! [EXPORT] : [COMMENT] CFDP request handling failed. P2: Returncode.
static constexpr Event HANDLING_CFDP_REQUEST_FAILED = event::makeEvent<SSID, 5, severity::LOW>(); static constexpr Event HANDLING_CFDP_REQUEST_FAILED = event::makeEvent(SSID, 5, severity::LOW);
} // namespace events } // namespace events
static constexpr ReturnValue_t SOURCE_TRANSACTION_PENDING = returnvalue::makeCode(CID, 0); static constexpr ReturnValue_t SOURCE_TRANSACTION_PENDING = returnvalue::makeCode(CID, 0);
-1
View File
@@ -14,7 +14,6 @@ class KeepAlivePduCreator : public FileDirectiveCreator {
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize, ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
Endianness streamEndianness) const override; Endianness streamEndianness) const override;
using FileDirectiveCreator::serialize;
private: private:
cfdp::Fss& progress; cfdp::Fss& progress;
+9 -25
View File
@@ -71,8 +71,7 @@ bool AssemblyBase::handleChildrenChangedHealth() {
if (iter == childrenMap.end()) { if (iter == childrenMap.end()) {
return false; return false;
} }
HealthState healthState = HealthState healthState = healthHelper.healthTable->getHealth(iter->first);
healthHelper.healthTable->getHealth(convertToDeviceObjectId(iter->first));
if (healthState == HasHealthIF::NEEDS_RECOVERY) { if (healthState == HasHealthIF::NEEDS_RECOVERY) {
triggerEvent(TRYING_RECOVERY, iter->first, 0); triggerEvent(TRYING_RECOVERY, iter->first, 0);
recoveryState = RECOVERY_STARTED; recoveryState = RECOVERY_STARTED;
@@ -92,14 +91,10 @@ bool AssemblyBase::handleChildrenChangedHealth() {
void AssemblyBase::handleChildrenTransition() { void AssemblyBase::handleChildrenTransition() {
if (commandsOutstanding <= 0) { if (commandsOutstanding <= 0) {
switch (internalState) { switch (internalState) {
case STATE_NEED_SECOND_STEP: { case STATE_NEED_SECOND_STEP:
internalState = STATE_SECOND_STEP; internalState = STATE_SECOND_STEP;
ReturnValue_t result = commandChildren(targetMode, targetSubmode); commandChildren(targetMode, targetSubmode);
if (result == NEED_SECOND_STEP) {
internalState = STATE_NEED_SECOND_STEP;
}
return; return;
}
case STATE_OVERWRITE_HEALTH: { case STATE_OVERWRITE_HEALTH: {
internalState = STATE_SINGLE_STEP; internalState = STATE_SINGLE_STEP;
ReturnValue_t result = commandChildren(mode, submode); ReturnValue_t result = commandChildren(mode, submode);
@@ -175,7 +170,7 @@ ReturnValue_t AssemblyBase::checkChildrenStateOff() {
ReturnValue_t AssemblyBase::checkChildOff(uint32_t objectId) { ReturnValue_t AssemblyBase::checkChildOff(uint32_t objectId) {
ChildInfo childInfo = childrenMap.find(objectId)->second; ChildInfo childInfo = childrenMap.find(objectId)->second;
if (healthHelper.healthTable->isCommandable(convertToDeviceObjectId(objectId))) { if (healthHelper.healthTable->isCommandable(objectId)) {
if (childInfo.submode != SUBMODE_NONE) { if (childInfo.submode != SUBMODE_NONE) {
return returnvalue::FAILED; return returnvalue::FAILED;
} else { } else {
@@ -232,7 +227,7 @@ bool AssemblyBase::checkAndHandleRecovery() {
case RECOVERY_STARTED: case RECOVERY_STARTED:
// The recovery was already start in #handleChildrenChangedHealth and we just need // The recovery was already start in #handleChildrenChangedHealth and we just need
// to wait for an off time period. // to wait for an off time period.
// The timeout can be defined by #setRecoveryWaitTimer // TODO: make time period configurable
recoveryState = RECOVERY_WAIT; recoveryState = RECOVERY_WAIT;
recoveryOffTimer.resetTimer(); recoveryOffTimer.resetTimer();
return true; return true;
@@ -240,14 +235,14 @@ bool AssemblyBase::checkAndHandleRecovery() {
if (recoveryOffTimer.isBusy()) { if (recoveryOffTimer.isBusy()) {
return true; return true;
} }
triggerEvent(RECOVERY_WAITING, recoveringDevice->first); triggerEvent(RECOVERY_STEP, 0);
sendHealthCommand(recoveringDevice->second.commandQueue, HEALTHY); sendHealthCommand(recoveringDevice->second.commandQueue, HEALTHY);
internalState = STATE_NONE; internalState = STATE_NONE;
recoveryState = RECOVERY_ONGOING; recoveryState = RECOVERY_ONGOING;
// Don't check state! // Don't check state!
return true; return true;
case RECOVERY_ONGOING: case RECOVERY_ONGOING:
triggerEvent(RECOVERY_RESTARTING, recoveringDevice->first); triggerEvent(RECOVERY_STEP, 1);
recoveryState = RECOVERY_ONGOING_2; recoveryState = RECOVERY_ONGOING_2;
recoveringDevice->second.healthChanged = false; recoveringDevice->second.healthChanged = false;
// Device should be healthy again, so restart a transition. // Device should be healthy again, so restart a transition.
@@ -255,7 +250,7 @@ bool AssemblyBase::checkAndHandleRecovery() {
doStartTransition(targetMode, targetSubmode); doStartTransition(targetMode, targetSubmode);
return true; return true;
case RECOVERY_ONGOING_2: case RECOVERY_ONGOING_2:
triggerEvent(RECOVERY_DONE, recoveringDevice->first); triggerEvent(RECOVERY_DONE);
// Now we're through, but not sure if it was successful. // Now we're through, but not sure if it was successful.
recoveryState = RECOVERY_IDLE; recoveryState = RECOVERY_IDLE;
return false; return false;
@@ -269,14 +264,7 @@ void AssemblyBase::overwriteDeviceHealth(object_id_t objectId, HasHealthIF::Heal
triggerEvent(OVERWRITING_HEALTH, objectId, oldHealth); triggerEvent(OVERWRITING_HEALTH, objectId, oldHealth);
internalState = STATE_OVERWRITE_HEALTH; internalState = STATE_OVERWRITE_HEALTH;
modeHelper.setForced(true); modeHelper.setForced(true);
if (childrenMap.find(objectId) != childrenMap.end()) { sendHealthCommand(childrenMap[objectId].commandQueue, EXTERNAL_CONTROL);
sendHealthCommand(childrenMap.at(objectId).commandQueue, EXTERNAL_CONTROL);
} else {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::debug << std::hex << SystemObject::getObjectId() << ": invalid mode table entry"
<< std::endl;
#endif
}
} }
void AssemblyBase::triggerModeHelperEvents(Mode_t mode, Submode_t submode) { void AssemblyBase::triggerModeHelperEvents(Mode_t mode, Submode_t submode) {
@@ -286,7 +274,3 @@ void AssemblyBase::triggerModeHelperEvents(Mode_t mode, Submode_t submode) {
triggerEvent(CHANGING_MODE, mode, submode); triggerEvent(CHANGING_MODE, mode, submode);
} }
} }
void AssemblyBase::setRecoveryWaitTimer(uint32_t timeoutMS) {
recoveryOffTimer.setTimeout(timeoutMS);
}
-2
View File
@@ -206,8 +206,6 @@ class AssemblyBase : public SubsystemBase {
void overwriteDeviceHealth(object_id_t objectId, HasHealthIF::HealthState oldHealth); void overwriteDeviceHealth(object_id_t objectId, HasHealthIF::HealthState oldHealth);
void triggerModeHelperEvents(Mode_t mode, Submode_t submode); void triggerModeHelperEvents(Mode_t mode, Submode_t submode);
void setRecoveryWaitTimer(uint32_t timeoutMS);
}; };
#endif /* FSFW_DEVICEHANDLERS_ASSEMBLYBASE_H_ */ #endif /* FSFW_DEVICEHANDLERS_ASSEMBLYBASE_H_ */
@@ -1268,7 +1268,7 @@ ReturnValue_t DeviceHandlerBase::letChildHandleMessage(CommandMessage* message)
void DeviceHandlerBase::handleDeviceTm(const uint8_t* rawData, size_t rawDataLen, void DeviceHandlerBase::handleDeviceTm(const uint8_t* rawData, size_t rawDataLen,
DeviceCommandId_t replyId, bool forceDirectTm) { DeviceCommandId_t replyId, bool forceDirectTm) {
SerialBufferAdapter<uint32_t> bufferWrapper(rawData, rawDataLen); SerialBufferAdapter bufferWrapper(rawData, rawDataLen);
handleDeviceTm(bufferWrapper, replyId, forceDirectTm); handleDeviceTm(bufferWrapper, replyId, forceDirectTm);
} }
+4 -5
View File
@@ -14,6 +14,8 @@ enum Severity : EventSeverity_t { INFO = 1, LOW = 2, MEDIUM = 3, HIGH = 4 };
} // namespace severity } // namespace severity
#define MAKE_EVENT(id, severity) (((severity) << 16) + (SUBSYSTEM_ID * 100) + (id))
typedef uint32_t Event; typedef uint32_t Event;
namespace event { namespace event {
@@ -22,14 +24,11 @@ constexpr EventId_t getEventId(Event event) { return (event & 0xFFFF); }
constexpr EventSeverity_t getSeverity(Event event) { return ((event >> 16) & 0xFF); } constexpr EventSeverity_t getSeverity(Event event) { return ((event >> 16) & 0xFF); }
template <uint8_t subsystemId, UniqueEventId_t uniqueEventId, EventSeverity_t eventSeverity> constexpr Event makeEvent(uint8_t subsystemId, UniqueEventId_t uniqueEventId,
constexpr Event makeEvent() { EventSeverity_t eventSeverity) {
static_assert(uniqueEventId < 100, "The unique event ID must be smaller than 100!");
return (eventSeverity << 16) + (subsystemId * 100) + uniqueEventId; return (eventSeverity << 16) + (subsystemId * 100) + uniqueEventId;
} }
} // namespace event } // namespace event
#define MAKE_EVENT(id, severity) event::makeEvent<SUBSYSTEM_ID, id, severity>();
#endif /* EVENTOBJECT_EVENT_H_ */ #endif /* EVENTOBJECT_EVENT_H_ */
-1
View File
@@ -2,7 +2,6 @@ target_sources(
${LIB_FSFW_NAME} ${LIB_FSFW_NAME}
PRIVATE arrayprinter.cpp PRIVATE arrayprinter.cpp
AsciiConverter.cpp AsciiConverter.cpp
CobsEncoder.cpp
CRC.cpp CRC.cpp
DleEncoder.cpp DleEncoder.cpp
DleParser.cpp DleParser.cpp
-78
View File
@@ -1,78 +0,0 @@
#include "fsfw/globalfunctions/CobsEncoder.h"
#include <cstddef>
#include <cstring>
ReturnValue_t CobsEncoder::encode(const uint8_t* sourceStream, size_t sourceLen,
uint8_t* destStream, size_t maxDestLen, size_t* encodedLen) {
*encodedLen = 0;
if (maxDestLen < worstCaseEncodedLen(sourceLen)) return INSUFFICIENT_SPACE;
size_t codeIdx = 0;
size_t outIdx = 1;
uint8_t code = 1;
for (size_t i = 0; i < sourceLen; ++i) {
const auto isZero = sourceStream[i] == 0x00;
if (not isZero) {
destStream[outIdx++] = sourceStream[i];
code += 1;
}
if (isZero or code == CobsEncoder::MAX_BLOCK_LEN + 1) { // + delimiter
destStream[codeIdx] = code;
codeIdx = outIdx++;
code = 1;
}
}
destStream[codeIdx] = code;
destStream[outIdx++] = 0x00;
*encodedLen = outIdx;
return returnvalue::OK;
}
ReturnValue_t CobsEncoder::decode(const uint8_t* sourceStream, size_t sourceLen, size_t* readLen,
uint8_t* destStream, size_t maxDestLen, size_t* decodedLen) {
*readLen = 0;
*decodedLen = 0;
if (sourceLen == 0) return NO_DATA_AVAILABLE;
// COBS, so 0 byte never occures. Used as delimiters.
const auto* delimiter = static_cast<const uint8_t*>(std::memchr(sourceStream, 0x00, sourceLen));
if (delimiter == nullptr) return STREAM_TOO_SHORT;
const size_t frameLen = delimiter - sourceStream;
const size_t consumedLen = frameLen + 1; // + delimiter
size_t inIdx = 0;
size_t outIdx = 0;
while (inIdx < frameLen) {
const size_t blockLen = sourceStream[inIdx++] - 1; // COBS, so for all bytes > 0
const auto isFullBlock = blockLen == MAX_BLOCK_LEN;
// COBS data block is bogus => bad data (set readLen != 0)
if (inIdx + blockLen > frameLen) return *readLen = consumedLen, DECODING_ERROR;
// Data block won't fit into provided buffer
if (maxDestLen < outIdx + blockLen) return INSUFFICIENT_SPACE;
std::memcpy(destStream + outIdx, sourceStream + inIdx, blockLen);
inIdx += blockLen;
outIdx += blockLen;
// COBS: != 0xFF, then meant to be zero
if (not isFullBlock and inIdx < frameLen) {
if (maxDestLen < outIdx + 1) return INSUFFICIENT_SPACE;
destStream[outIdx++] = 0x00;
}
}
*readLen = consumedLen;
*decodedLen = outIdx;
return returnvalue::OK;
}
-113
View File
@@ -1,113 +0,0 @@
#ifndef FSFW_GLOBALFUNCTIONS_COBSENCODER_H_
#define FSFW_GLOBALFUNCTIONS_COBSENCODER_H_
#include <cstddef>
#include <cstdint>
#include "fsfw/returnvalues/returnvalue.h"
/**
* @brief This COBS Encoder (Consistent Overhead Byte Stuffing) can be used to encode and
* decode arbitrary data.
*
* @details
* Protocol information: https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
*
* A COBS frame contains no zero bytes but is terminated by one, so frames can be picked out of a
* byte stream by looking for that delimiter.
*/
class CobsEncoder {
public:
CobsEncoder() = delete;
virtual ~CobsEncoder() = delete;
static constexpr uint8_t INTERFACE_ID = CLASS_ID::COBS_ENCODER;
/** The source stream holds no frame delimiter yet, so the frame may still be arriving.
* Nothing was consumed and the caller should retry once more data has been received. */
static constexpr ReturnValue_t STREAM_TOO_SHORT = MAKE_RETURN_CODE(1);
/** The frame is delimited but its block structure is malformed, so it cannot be recovered.
* `readLen` skips past the whole frame so decoding can resynchronise on the next one. */
static constexpr ReturnValue_t DECODING_ERROR = MAKE_RETURN_CODE(2);
/** The input stream is empty. */
static constexpr ReturnValue_t NO_DATA_AVAILABLE = MAKE_RETURN_CODE(3);
/** The output buffer is not large enough to fit the input data. */
static constexpr ReturnValue_t INSUFFICIENT_SPACE = MAKE_RETURN_CODE(4);
/** Longest run of data bytes a single code byte can describe. */
static constexpr size_t MAX_BLOCK_LEN = 254;
/**
* Upper bound on the encoded size of `sourceLen` bytes, including the frame delimiter.
* Exact when the input holds no zero bytes and at most one byte per full block too large
* otherwise.
* Use this during compiletime to create a correctly sized output buffer.
* @param sourceLen Max length of buffer to encode
* @return Use as follows: `uint8_t destBuffer[worstCaseEncodedLen(srcBufferSize)];`.
*/
static constexpr size_t worstCaseEncodedLen(size_t sourceLen) {
// Derivation:
// We know: Frame length = (data bytes) + (code bytes) + (delimiter)
// Let n be the source length, z the number of zero bytes in it and s the number of times a data
// block is over 254 and splits.
// We have (data bytes) = n - z, since only non-zero bytes get copied over.
// (code bytes) = 1 + z + s, since starting code, blocks end at zero and 254 splits.
// So, Frame length = (data bytes) + (code bytes) + (delimiter)
// = (n - z) + (1 + z + s) + 1 = n + s + 2
// s depends on the data, so we derive the upper bound, since we know these splits happen every
// 254 bytes (or less since maybe there are enough zeros spread out to not need a 254 split):
// s <= (n - z) / 254 <= n / 254. (z >= 0)
// Thus, n + s + 2 <= n + (n / 254) + 2
// Note: We do integer division (floor), since a 254 code only appears every FULL 254.
return sourceLen + sourceLen / MAX_BLOCK_LEN + 2;
}
/**
* Encodes the given data stream into COBS format
* @param sourceStream Start of the source buffer
* @param sourceLen Length of the source buffer
* @param destStream Destination buffer
* @param maxDestLen Maximum length of the destination buffer
* @param encodedLen Out pointer which is written with the actual amount of data written to the
* destination buffer.
* @return
* - returnvalue::OK for successful encoding operation
* - INSUFFICIENT_SPACE if `maxDestLen` is below `worstCaseEncodedLen(sourceLen)`.
* Note: Technically smaller sizes would fit but `worstCaseEncodedLen` is easy to compute.
*/
static ReturnValue_t encode(const uint8_t* sourceStream, size_t sourceLen, uint8_t* destStream,
size_t maxDestLen, size_t* encodedLen);
/**
* Converts an encoded stream back from COBS format.
*
* This function only ever decodes a single COBS frame.
* To drain a stream, advance it by `readLen` and call again until `STREAM_TOO_SHORT` reports that
* no complete frame is left.
* Because `readLen` is also set for a corrupt frame, a damaged frame never blocks the intact ones
* queued behind it.
*
* An empty frame, meaning a delimiter with no data in front of it, decodes successfully with a
* `decodedLen` of zero.
* Callers that treat runs of delimiters as idle filler should ignore those.
* @param sourceStream Start of the source buffer
* @param sourceStreamLen Length of the source buffer
* @param readLen Out pointer which is written with the amount of data that was actually read from
* the source buffer. Set on success and on DECODING_ERROR, both times covering the whole
* frame including its delimiter. Left at zero otherwise.
* @param destStream Destination buffer
* @param maxDestStreamlen Maximum length of the destination buffer
* @param decodedLen Out pointer which is written with the actual amount of data written to the
* destination buffer.
* @return
* - returnvalue::OK for successful decode operation
* - STREAM_TOO_SHORT if the source stream holds no complete frame yet
* - DECODING_ERROR if the frame is delimited but malformed. Skip `readLen` bytes to resync
* - INSUFFICIENT_SPACE if the destination buffer cannot hold the decoded frame
* - NO_DATA_AVAILABLE if the source stream is empty
*/
static ReturnValue_t decode(const uint8_t* sourceStream, size_t sourceStreamLen, size_t* readLen,
uint8_t* destStream, size_t maxDestStreamlen, size_t* decodedLen);
};
#endif /* FSFW_GLOBALFUNCTIONS_COBSENCODER_H_ */
+4 -10
View File
@@ -27,19 +27,13 @@ class HasHealthIF {
static const Event CHILD_PROBLEMS = MAKE_EVENT(8, severity::LOW); static const Event CHILD_PROBLEMS = MAKE_EVENT(8, severity::LOW);
//! Assembly overwrites health information of children to keep satellite alive. //! Assembly overwrites health information of children to keep satellite alive.
static const Event OVERWRITING_HEALTH = MAKE_EVENT(9, severity::LOW); static const Event OVERWRITING_HEALTH = MAKE_EVENT(9, severity::LOW);
//! Someone starts a recovery of a component (typically power-cycle). //! Someone starts a recovery of a component (typically power-cycle). No parameters.
//! P1: Object Id of the recovering device.
static const Event TRYING_RECOVERY = MAKE_EVENT(10, severity::MEDIUM); static const Event TRYING_RECOVERY = MAKE_EVENT(10, severity::MEDIUM);
//! Recovery is ongoing. Comes twice during recovery.
//! P1: 0 for the first, 1 for the second event. P2: 0
static const Event RECOVERY_STEP = MAKE_EVENT(11, severity::MEDIUM);
//! Recovery was completed. Not necessarily successful. No parameters. //! Recovery was completed. Not necessarily successful. No parameters.
//! P1: Object Id of the recovering device.
static const Event RECOVERY_DONE = MAKE_EVENT(12, severity::MEDIUM); static const Event RECOVERY_DONE = MAKE_EVENT(12, severity::MEDIUM);
//! Recovery is ongoing. The recovering device is currently OFF, waiting for restart.
//! P1: Object Id of the recovering device.
static const Event RECOVERY_WAITING = MAKE_EVENT(13, severity::MEDIUM);
//! Recovery is ongoing. Restarting the recovering device.
//! P1: Object Id of the recovering device.
static const Event RECOVERY_RESTARTING = MAKE_EVENT(14, severity::MEDIUM);
virtual ~HasHealthIF() {} virtual ~HasHealthIF() {}
virtual MessageQueueId_t getCommandQueue() const = 0; virtual MessageQueueId_t getCommandQueue() const = 0;
+2 -4
View File
@@ -17,11 +17,11 @@ void HealthTable::setMutexTimeout(MutexIF::TimeoutType timeoutType, uint32_t tim
HealthTable::~HealthTable() { MutexFactory::instance()->deleteMutex(mutex); } HealthTable::~HealthTable() { MutexFactory::instance()->deleteMutex(mutex); }
ReturnValue_t HealthTable::registerObject(object_id_t object, ReturnValue_t HealthTable::registerObject(object_id_t object,
HasHealthIF::HealthState initialState) { HasHealthIF::HealthState initilialState) {
if (healthMap.count(object) != 0) { if (healthMap.count(object) != 0) {
return returnvalue::FAILED; return returnvalue::FAILED;
} }
healthMap.emplace(object, initialState); healthMap.emplace(object, initilialState);
return returnvalue::OK; return returnvalue::OK;
} }
@@ -112,5 +112,3 @@ ReturnValue_t HealthTable::iterate(HealthEntry* value, bool reset) {
mapIterator++; mapIterator++;
return result; return result;
} }
MutexIF* HealthTable::getMutex() { return mutex; }
+1 -3
View File
@@ -18,7 +18,7 @@ class HealthTable : public HealthTableIF, public SystemObject {
/** HealthTableIF overrides */ /** HealthTableIF overrides */
virtual ReturnValue_t registerObject( virtual ReturnValue_t registerObject(
object_id_t object, HasHealthIF::HealthState initialState = HasHealthIF::HEALTHY) override; object_id_t object, HasHealthIF::HealthState initilialState = HasHealthIF::HEALTHY) override;
ReturnValue_t removeObject(object_id_t object) override; ReturnValue_t removeObject(object_id_t object) override;
virtual size_t getPrintSize() override; virtual size_t getPrintSize() override;
virtual void printAll(uint8_t* pointer, size_t maxSize) override; virtual void printAll(uint8_t* pointer, size_t maxSize) override;
@@ -28,8 +28,6 @@ class HealthTable : public HealthTableIF, public SystemObject {
virtual void setHealth(object_id_t object, HasHealthIF::HealthState newState) override; virtual void setHealth(object_id_t object, HasHealthIF::HealthState newState) override;
virtual HasHealthIF::HealthState getHealth(object_id_t) override; virtual HasHealthIF::HealthState getHealth(object_id_t) override;
MutexIF* getMutex();
protected: protected:
using HealthMap = std::map<object_id_t, HasHealthIF::HealthState>; using HealthMap = std::map<object_id_t, HasHealthIF::HealthState>;
using HealthEntry = std::pair<object_id_t, HasHealthIF::HealthState>; using HealthEntry = std::pair<object_id_t, HasHealthIF::HealthState>;
+1 -1
View File
@@ -12,7 +12,7 @@ class HealthTableIF : public ManagesHealthIF {
virtual ~HealthTableIF() {} virtual ~HealthTableIF() {}
virtual ReturnValue_t registerObject( virtual ReturnValue_t registerObject(
object_id_t object, HasHealthIF::HealthState initialState = HasHealthIF::HEALTHY) = 0; object_id_t object, HasHealthIF::HealthState initilialState = HasHealthIF::HEALTHY) = 0;
virtual ReturnValue_t removeObject(object_id_t objectId) = 0; virtual ReturnValue_t removeObject(object_id_t objectId) = 0;
+1 -30
View File
@@ -91,7 +91,7 @@ class Dataset : public SerializeIF {
void setChildrenValidity(bool valid) { void setChildrenValidity(bool valid) {
for (auto &serializable : serializables) { for (auto &serializable : serializables) {
serializable.get().setValid(valid); serializable.get().setValid(true);
} }
} }
@@ -118,35 +118,6 @@ class Dataset : public SerializeIF {
return result; return result;
} }
/**
* Counterpart of doSerializeWithValidityBlob: reads the values, then sets the validity of each
* serializable from the validity blob. deSerialize ignores the blob.
*/
ReturnValue_t deSerializeWithValidityBlob(const uint8_t **buffer, size_t *size,
const Endianness streamEndianness) {
const size_t validityMaskSize = (serializables.size() + 7) / 8;
size_t valuesSize = 0;
for (auto &serializable : serializables) {
valuesSize += serializable.get().getSerializedSize();
}
// Checked up front so a short stream leaves the dataset untouched
if (*size < valuesSize + validityMaskSize) {
return SerializeIF::STREAM_TOO_SHORT;
}
ReturnValue_t result = Dataset::deSerialize(buffer, size, streamEndianness);
if (result != returnvalue::OK) {
return result;
}
for (size_t idx = 0; idx < serializables.size(); idx++) {
bool valid = false;
bitutil::get(*buffer + idx / 8, idx % 8, valid);
serializables[idx].get().setValid(valid);
}
*buffer += validityMaskSize;
*size -= validityMaskSize;
return returnvalue::OK;
}
bool serializeWithValidityBlob = false; bool serializeWithValidityBlob = false;
private: private:
+2 -2
View File
@@ -9,6 +9,7 @@
#include "fsfw/housekeeping/HousekeepingSnapshot.h" #include "fsfw/housekeeping/HousekeepingSnapshot.h"
#include "fsfw/ipc/QueueFactory.h" #include "fsfw/ipc/QueueFactory.h"
#include "fsfw/objectmanager/ObjectManager.h" #include "fsfw/objectmanager/ObjectManager.h"
#include "fsfw/timemanager/CCSDSTime.h"
using namespace hk; using namespace hk;
@@ -83,7 +84,6 @@ ReturnValue_t PeriodicHelper::performHkOperation() {
ReturnValue_t PeriodicHelper::handleHousekeepingMessage(CommandMessage* message) { ReturnValue_t PeriodicHelper::handleHousekeepingMessage(CommandMessage* message) {
Command_t command = message->getCommand(); Command_t command = message->getCommand();
MessageQueueId_t sender = message->getSender();
dp::sid_t sid = HousekeepingMessage::getStructureId(message); dp::sid_t sid = HousekeepingMessage::getStructureId(message);
ReturnValue_t result = returnvalue::OK; ReturnValue_t result = returnvalue::OK;
switch (command) { switch (command) {
@@ -113,7 +113,7 @@ ReturnValue_t PeriodicHelper::handleHousekeepingMessage(CommandMessage* message)
} }
case (HousekeepingMessage::GENERATE_ONE_PARAMETER_REPORT): { case (HousekeepingMessage::GENERATE_ONE_PARAMETER_REPORT): {
return generateHousekeepingPacket(HousekeepingMessage::getStructureId(message), sender); return generateHousekeepingPacket(HousekeepingMessage::getStructureId(message));
} }
default: default:
+1 -1
View File
@@ -45,7 +45,7 @@ bool MessageQueueBase::isDefaultDestinationSet() const { return (defaultDest !=
ReturnValue_t MessageQueueBase::sendMessage(MessageQueueId_t sendTo, MessageQueueMessageIF* message, ReturnValue_t MessageQueueBase::sendMessage(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
bool ignoreFault) { bool ignoreFault) {
return sendMessageFrom(sendTo, message, this->getId(), ignoreFault); return sendMessageFrom(sendTo, message, this->getId(), false);
} }
ReturnValue_t MessageQueueBase::sendToDefaultFrom(MessageQueueMessageIF* message, ReturnValue_t MessageQueueBase::sendToDefaultFrom(MessageQueueMessageIF* message,
+1 -4
View File
@@ -47,10 +47,7 @@ class MutexGuard {
ReturnValue_t getLockResult() const { return result; } ReturnValue_t getLockResult() const { return result; }
~MutexGuard() { ~MutexGuard() {
// Only unlock what was actually locked. Unlocking after a failed take gives away a mutex held if (internalMutex != nullptr) {
// by another task, which trips configASSERT(pxTCB == pxCurrentTCB) in FreeRTOS'
// xTaskPriorityDisinherit and halts the system.
if (internalMutex != nullptr and result == returnvalue::OK) {
internalMutex->unlockMutex(); internalMutex->unlockMutex();
} }
} }
@@ -36,8 +36,6 @@ enum framework_objects : object_id_t {
TIME_STAMPER = 0x53500010, TIME_STAMPER = 0x53500010,
VERIFICATION_REPORTER = 0x53500020, VERIFICATION_REPORTER = 0x53500020,
SIF_PRINT_TASK = 0x53600000,
FSFW_OBJECTS_END = 0x53ffffff, FSFW_OBJECTS_END = 0x53ffffff,
NO_OBJECT = 0xFFFFFFFF NO_OBJECT = 0xFFFFFFFF
}; };
+1 -1
View File
@@ -41,7 +41,7 @@ UdpTcPollingTask::UdpTcPollingTask(object_id_t objectId, object_id_t tmtcUdpBrid
[[noreturn]] ReturnValue_t UdpTcPollingTask::performOperation(uint8_t opCode) { [[noreturn]] ReturnValue_t UdpTcPollingTask::performOperation(uint8_t opCode) {
/* Sender Address is cached here. */ /* Sender Address is cached here. */
struct sockaddr senderAddress{}; struct sockaddr senderAddress {};
socklen_t senderAddressSize = sizeof(senderAddress); socklen_t senderAddressSize = sizeof(senderAddress);
/* Poll for new UDP datagrams in permanent loop. */ /* Poll for new UDP datagrams in permanent loop. */
+2 -3
View File
@@ -5,8 +5,8 @@
#include "FreeRTOS.h" #include "FreeRTOS.h"
#include "fsfw/globalfunctions/timevalOperations.h" #include "fsfw/globalfunctions/timevalOperations.h"
#include "fsfw/osal/freertos/Timekeeper.h"
#include "fsfw/serviceinterface/ServiceInterfacePrinter.h" #include "fsfw/serviceinterface/ServiceInterfacePrinter.h"
#include "fsfw/osal/freertos/Timekeeper.h"
#include "task.h" #include "task.h"
// TODO sanitize input? // TODO sanitize input?
@@ -48,8 +48,7 @@ ReturnValue_t Clock::getClock(timeval* time) {
} }
ReturnValue_t Clock::getClockMonotonic(timeval* time) { ReturnValue_t Clock::getClockMonotonic(timeval* time) {
// Retrieves the FreeRTOS tick count, which is hopefully monotonic *time = Timekeeper::instance()->getMonotonicClockOffset() + getUptime();
*time = getUptime();
return returnvalue::OK; return returnvalue::OK;
} }
+11 -1
View File
@@ -17,7 +17,13 @@ Timekeeper* Timekeeper::instance() {
return myinstance; return myinstance;
} }
void Timekeeper::setOffset(const timeval& offset) { this->offset = offset; } void Timekeeper::setOffset(const timeval& offset) {
if (not monotonicClockInitialized) {
this->monotonicClockOffset = offset;
monotonicClockInitialized = true;
}
this->offset = offset;
}
timeval Timekeeper::ticksToTimeval(TickType_t ticks) { timeval Timekeeper::ticksToTimeval(TickType_t ticks) {
timeval uptime; timeval uptime;
@@ -33,3 +39,7 @@ timeval Timekeeper::ticksToTimeval(TickType_t ticks) {
} }
TickType_t Timekeeper::getTicks() { return xTaskGetTickCount(); } TickType_t Timekeeper::getTicks() { return xTaskGetTickCount(); }
const timeval Timekeeper::getMonotonicClockOffset() const {
return monotonicClockOffset;
}
+6
View File
@@ -18,9 +18,14 @@ class Timekeeper {
Timekeeper(); Timekeeper();
timeval offset; timeval offset;
// Set when offset is initialized the first time
timeval monotonicClockOffset;
bool monotonicClockInitialized = false;
static Timekeeper* myinstance; static Timekeeper* myinstance;
void setMonotonicClockOffset(const timeval& monotonicClockOffset);
public: public:
static Timekeeper* instance(); static Timekeeper* instance();
virtual ~Timekeeper(); virtual ~Timekeeper();
@@ -34,6 +39,7 @@ class Timekeeper {
const timeval& getOffset() const; const timeval& getOffset() const;
void setOffset(const timeval& offset); void setOffset(const timeval& offset);
const timeval getMonotonicClockOffset() const;
}; };
#endif /* FRAMEWORK_OSAL_FREERTOS_TIMEKEEPER_H_ */ #endif /* FRAMEWORK_OSAL_FREERTOS_TIMEKEEPER_H_ */
+1 -1
View File
@@ -173,7 +173,7 @@ ReturnValue_t Clock::getDateAndTime(TimeOfDay_t* time) {
} }
ReturnValue_t Clock::convertTimeOfDayToTimeval(const TimeOfDay_t* from, timeval* to) { ReturnValue_t Clock::convertTimeOfDayToTimeval(const TimeOfDay_t* from, timeval* to) {
struct tm time_tm{}; struct tm time_tm {};
time_tm.tm_year = from->year - 1900; time_tm.tm_year = from->year - 1900;
time_tm.tm_mon = from->month - 1; time_tm.tm_mon = from->month - 1;
+1 -1
View File
@@ -52,6 +52,6 @@ void TaskFactory::printMissedDeadline() {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "TaskFactory::printMissedDeadline: " << name << std::endl; sif::warning << "TaskFactory::printMissedDeadline: " << name << std::endl;
#else #else
sif::printWarning("TaskFactory::printMissedDeadline: %s\n", name.c_str()); sif::printWarning("TaskFactory::printMissedDeadline: %s\n", name);
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ #endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
} }
@@ -49,7 +49,7 @@ class Service11TelecommandScheduling final : public PusServiceBase {
//! [EXPORT] : [COMMENT] Deletion of a TC from the map failed. //! [EXPORT] : [COMMENT] Deletion of a TC from the map failed.
//! P1: First 32 bit of request ID, P2. Last 32 bit of Request ID //! P1: First 32 bit of request ID, P2. Last 32 bit of Request ID
static constexpr Event TC_DELETION_FAILED = event::makeEvent<SUBSYSTEM_ID, 0, severity::MEDIUM>(); static constexpr Event TC_DELETION_FAILED = MAKE_EVENT(0, severity::MEDIUM);
// The types of PUS-11 subservices // The types of PUS-11 subservices
enum Subservice : uint8_t { enum Subservice : uint8_t {
+7 -9
View File
@@ -1,8 +1,6 @@
#ifndef FSFW_PUS_SERVICE8FUNCTIONMANAGEMENT_H_ #ifndef FSFW_PUS_SERVICE8FUNCTIONMANAGEMENT_H_
#define FSFW_PUS_SERVICE8FUNCTIONMANAGEMENT_H_ #define FSFW_PUS_SERVICE8FUNCTIONMANAGEMENT_H_
#include <cstdint>
#include "fsfw/action/ActionMessage.h" #include "fsfw/action/ActionMessage.h"
#include "fsfw/tmtcservices/CommandingServiceBase.h" #include "fsfw/tmtcservices/CommandingServiceBase.h"
@@ -37,13 +35,6 @@ class Service8FunctionManagement : public CommandingServiceBase {
uint16_t commandTimeoutSeconds = 60); uint16_t commandTimeoutSeconds = 60);
~Service8FunctionManagement() override; ~Service8FunctionManagement() override;
enum class Subservice : uint8_t {
//!< [EXPORT] : [COMMAND] Functional commanding
COMMAND_DIRECT_COMMANDING = 128,
//!< [EXPORT] : [REPLY] Data reply
REPLY_DIRECT_COMMANDING_DATA = 130,
};
protected: protected:
/* CSB abstract functions implementation . See CSB documentation. */ /* CSB abstract functions implementation . See CSB documentation. */
ReturnValue_t isValidSubservice(uint8_t subservice) override; ReturnValue_t isValidSubservice(uint8_t subservice) override;
@@ -57,6 +48,13 @@ class Service8FunctionManagement : public CommandingServiceBase {
bool* isStep) override; bool* isStep) override;
private: private:
enum class Subservice {
//!< [EXPORT] : [COMMAND] Functional commanding
COMMAND_DIRECT_COMMANDING = 128,
//!< [EXPORT] : [REPLY] Data reply
REPLY_DIRECT_COMMANDING_DATA = 130,
};
ReturnValue_t checkInterfaceAndAcquireMessageQueue(MessageQueueId_t* messageQueueToSet, ReturnValue_t checkInterfaceAndAcquireMessageQueue(MessageQueueId_t* messageQueueToSet,
object_id_t* objectId); object_id_t* objectId);
ReturnValue_t prepareDirectCommand(CommandMessage* message, const uint8_t* tcData, ReturnValue_t prepareDirectCommand(CommandMessage* message, const uint8_t* tcData,
+1 -31
View File
@@ -1,14 +1,11 @@
#ifndef FSFW_PUS_SERVICEPACKETS_SERVICE8PACKETS_H_ #ifndef FSFW_PUS_SERVICEPACKETS_SERVICE8PACKETS_H_
#define FSFW_PUS_SERVICEPACKETS_SERVICE8PACKETS_H_ #define FSFW_PUS_SERVICEPACKETS_SERVICE8PACKETS_H_
#include <cstdint>
#include "../../action/ActionMessage.h" #include "../../action/ActionMessage.h"
#include "../../objectmanager/SystemObjectIF.h" #include "../../objectmanager/SystemObjectIF.h"
#include "../../returnvalues/returnvalue.h"
#include "../../serialize/SerialBufferAdapter.h" #include "../../serialize/SerialBufferAdapter.h"
#include "../../serialize/SerialFixedArrayListAdapter.h"
#include "../../serialize/SerialLinkedListAdapter.h" #include "../../serialize/SerialLinkedListAdapter.h"
#include "../../serialize/SerializeAdapter.h"
#include "../../serialize/SerializeElement.h" #include "../../serialize/SerializeElement.h"
/** /**
@@ -25,41 +22,14 @@ class DirectCommand
parametersSize = size; parametersSize = size;
} }
DirectCommand() : parametersSize(0), parameterBuffer(nullptr) {}
ActionId_t getActionId() const { return actionId; } ActionId_t getActionId() const { return actionId; }
void setActionId(ActionId_t actionId) { this->actionId = actionId; }
object_id_t getObjectId() const { return objectId; } object_id_t getObjectId() const { return objectId; }
void setObjectId(object_id_t objectId) { this->objectId = objectId; }
const uint8_t* getParameters() { return parameterBuffer; } const uint8_t* getParameters() { return parameterBuffer; }
// The given pointer is not deallocated and must outlive the DirectCommand!
void setParameters(const uint8_t* parameters, uint32_t parametersSize) {
this->parameterBuffer = parameters;
this->parametersSize = parametersSize;
}
uint32_t getParametersSize() const { return parametersSize; } uint32_t getParametersSize() const { return parametersSize; }
// ^SerializeIF
virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
Endianness streamEndianness) const override {
auto const oldSize = *size;
auto result = SerializeAdapter::serialize(&objectId, buffer, size, maxSize, streamEndianness);
if (result != returnvalue::OK) return result;
result = SerializeAdapter::serialize(&actionId, buffer, size, maxSize - ((*size) - oldSize),
streamEndianness);
if (result != returnvalue::OK) return result;
auto remainingSize = maxSize - ((*size) - oldSize);
if (remainingSize < parametersSize) return returnvalue::FAILED;
memcpy(*buffer, parameterBuffer, parametersSize);
*size += parametersSize;
return returnvalue::OK;
}
private: private:
DirectCommand(const DirectCommand& command); DirectCommand(const DirectCommand& command);
object_id_t objectId = 0; object_id_t objectId = 0;
-1
View File
@@ -85,7 +85,6 @@ enum : uint8_t {
MGM_LIS3MDL, // MGMLIS3 MGM_LIS3MDL, // MGMLIS3
MGM_RM3100, // MGMRM3100 MGM_RM3100, // MGMRM3100
SPACE_PACKET_PARSER, // SPPA SPACE_PACKET_PARSER, // SPPA
COBS_ENCODER, // COBS
FW_CLASS_ID_COUNT // [EXPORT] : [END] FW_CLASS_ID_COUNT // [EXPORT] : [END]
}; };
+1 -1
View File
@@ -1,4 +1,4 @@
target_sources( target_sources(
${LIB_FSFW_NAME} ${LIB_FSFW_NAME}
PRIVATE ServiceInterfaceStream.cpp ServiceInterfaceBuffer.cpp PRIVATE ServiceInterfaceStream.cpp ServiceInterfaceBuffer.cpp
ServiceInterfacePrinter.cpp ServiceInterfacePrinterTask.cpp) ServiceInterfacePrinter.cpp)
@@ -1,12 +1,9 @@
#include "fsfw/serviceinterface/ServiceInterfacePrinter.h" #include "fsfw/serviceinterface/ServiceInterfacePrinter.h"
#include <algorithm>
#include <cstdarg> #include <cstdarg>
#include <cstring> #include <cstdint>
#include "fsfw/FSFW.h" #include "fsfw/FSFW.h"
#include "fsfw/ipc/MutexFactory.h"
#include "fsfw/ipc/MutexGuard.h"
#include "fsfw/serviceinterface/serviceInterfaceDefintions.h" #include "fsfw/serviceinterface/serviceInterfaceDefintions.h"
#include "fsfw/timemanager/Clock.h" #include "fsfw/timemanager/Clock.h"
@@ -17,32 +14,11 @@ static bool consoleInitialized = false;
#if FSFW_DISABLE_PRINTOUT == 0 #if FSFW_DISABLE_PRINTOUT == 0
namespace { static bool addCrAtEnd = false;
// Formatted messages are staged in a ring buffer and drained by the print task uint8_t printBuffer[fsfwconfig::FSFW_PRINT_BUFFER_SIZE];
// in bounded chunks, so producers never block on the slow debug UART.
constexpr size_t RING_SIZE =
fsfwconfig::FSFW_PRINT_BUFFER_SIZE * fsfwconfig::FSFW_PRINT_BUFFER_AMOUNT;
constexpr size_t MAX_BYTES_PER_CYCLE = 512; void fsfwPrint(sif::PrintLevel printType, const char *fmt, va_list arg) {
// The print ring buffer
char ring[RING_SIZE];
MutexIF* ringMutex = nullptr;
size_t readIdx = 0; // Start of data to print out
size_t bytesUsed = 0; // Pending data amount to print out
uint32_t droppedMessages = 0;
uint32_t droppedMessagesTotal = 0;
bool addCrAtEnd = false;
bool replaceLastCharWithNewline = false;
// False until the print task runs.
// Prints go directly to stdout before that.
bool taskRunning = false;
void fsfwPrint(sif::PrintLevel printType, const char* fmt, va_list arg) {
#if defined(WIN32) && FSFW_COLORED_OUTPUT == 1 #if defined(WIN32) && FSFW_COLORED_OUTPUT == 1
if (not consoleInitialized) { if (not consoleInitialized) {
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
@@ -54,156 +30,100 @@ void fsfwPrint(sif::PrintLevel printType, const char* fmt, va_list arg) {
consoleInitialized = true; consoleInitialized = true;
#endif #endif
size_t len = 0;
char *bufferPosition = reinterpret_cast<char *>(printBuffer);
/* Check logger level */
if (printType == sif::PrintLevel::NONE or printType > printLevel) { if (printType == sif::PrintLevel::NONE or printType > printLevel) {
return; return;
} }
static const char* const labels[] = {"", "ERROR ", "WARNING", "INFO ", "DEBUG "}; /* Log message to terminal */
#if FSFW_COLORED_OUTPUT == 1 #if FSFW_COLORED_OUTPUT == 1
static const char* const colors[] = {"", sif::ANSI_COLOR_RED, sif::ANSI_COLOR_YELLOW, if (printType == sif::PrintLevel::INFO_LEVEL) {
sif::ANSI_COLOR_GREEN, sif::ANSI_COLOR_CYAN}; len += sprintf(bufferPosition, sif::ANSI_COLOR_GREEN);
const char* color = colors[printType]; } else if (printType == sif::PrintLevel::DEBUG_LEVEL) {
const char* reset = sif::ANSI_COLOR_RESET; len += sprintf(bufferPosition, sif::ANSI_COLOR_CYAN);
#else } else if (printType == sif::PrintLevel::WARNING_LEVEL) {
const char* color = ""; len += sprintf(bufferPosition, sif::ANSI_COLOR_YELLOW);
const char* reset = ""; } else if (printType == sif::PrintLevel::ERROR_LEVEL) {
len += sprintf(bufferPosition, sif::ANSI_COLOR_RED);
}
#endif
if (printType == sif::PrintLevel::INFO_LEVEL) {
len += sprintf(bufferPosition + len, "INFO ");
}
if (printType == sif::PrintLevel::DEBUG_LEVEL) {
len += sprintf(bufferPosition + len, "DEBUG ");
}
if (printType == sif::PrintLevel::WARNING_LEVEL) {
len += sprintf(bufferPosition + len, "WARNING");
}
if (printType == sif::PrintLevel::ERROR_LEVEL) {
len += sprintf(bufferPosition + len, "ERROR ");
}
#if FSFW_COLORED_OUTPUT == 1
len += sprintf(bufferPosition + len, sif::ANSI_COLOR_RESET);
#endif #endif
Clock::TimeOfDay_t now; Clock::TimeOfDay_t now;
Clock::getDateAndTime(&now); Clock::getDateAndTime(&now);
/*
* Log current time to terminal if desired.
*/
len += sprintf(bufferPosition + len, " | %02lu:%02lu:%02lu.%03lu | ", (unsigned long)now.hour,
(unsigned long)now.minute, (unsigned long)now.second,
(unsigned long)now.usecond / 1000);
char buf[fsfwconfig::FSFW_PRINT_BUFFER_SIZE + 2]; // slack for the CR/newline len += vsnprintf(bufferPosition + len, sizeof(printBuffer) - len, fmt, arg);
// Need to clamp, since snprintf returns WOULD be written length (not actual; could be higher than if (addCrAtEnd) {
// really) len += sprintf(bufferPosition + len, "\r");
const auto prefixLen =
std::min(static_cast<size_t>(snprintf(
buf, fsfwconfig::FSFW_PRINT_BUFFER_SIZE, "%s%s%s | %02u:%02u:%02u.%03u | ",
color, labels[printType], reset, static_cast<unsigned>(now.hour),
static_cast<unsigned>(now.minute), static_cast<unsigned>(now.second),
static_cast<unsigned>(now.usecond / 1000))),
fsfwconfig::FSFW_PRINT_BUFFER_SIZE - 1);
// Same here
const auto msgLen =
std::min(static_cast<size_t>(vsnprintf(
buf + prefixLen, fsfwconfig::FSFW_PRINT_BUFFER_SIZE - prefixLen, fmt, arg)),
fsfwconfig::FSFW_PRINT_BUFFER_SIZE - 1 - prefixLen);
size_t textLen = prefixLen + msgLen;
if (addCrAtEnd and buf[textLen - 1] == '\n') {
buf[textLen++] = '\r';
}
if (replaceLastCharWithNewline and buf[textLen - 1] != '\n' and buf[textLen - 1] != '\r') {
buf[textLen++] = '\n';
} }
if (not taskRunning) { printf("%s", printBuffer);
fwrite(buf, 1, textLen, stdout);
return;
}
MutexGuard guard(ringMutex, MutexIF::TimeoutType::BLOCKING);
if (textLen > RING_SIZE - bytesUsed) {
++droppedMessages;
return;
}
const size_t writeIdx = (readIdx + bytesUsed) % RING_SIZE;
const size_t firstPart = std::min(textLen, RING_SIZE - writeIdx);
std::memcpy(ring + writeIdx, buf, firstPart);
std::memcpy(ring, buf + firstPart, textLen - firstPart);
bytesUsed += textLen;
} }
} // namespace void sif::printInfo(const char *fmt, ...) {
void sif::setToAddCrAtEnd(const bool addCrAtEnd_) { addCrAtEnd = addCrAtEnd_; }
void sif::setReplaceLastCharWithNewline(const bool replace) {
replaceLastCharWithNewline = replace;
}
void sif::printInfo(const char* fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
fsfwPrint(sif::PrintLevel::INFO_LEVEL, fmt, args); fsfwPrint(sif::PrintLevel::INFO_LEVEL, fmt, args);
va_end(args); va_end(args);
} }
void sif::printWarning(const char* fmt, ...) { void sif::printWarning(const char *fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
fsfwPrint(sif::PrintLevel::WARNING_LEVEL, fmt, args); fsfwPrint(sif::PrintLevel::WARNING_LEVEL, fmt, args);
va_end(args); va_end(args);
} }
void sif::printDebug(const char* fmt, ...) { void sif::printDebug(const char *fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
fsfwPrint(sif::PrintLevel::DEBUG_LEVEL, fmt, args); fsfwPrint(sif::PrintLevel::DEBUG_LEVEL, fmt, args);
va_end(args); va_end(args);
} }
void sif::printError(const char* fmt, ...) { void sif::setToAddCrAtEnd(bool addCrAtEnd_) { addCrAtEnd = addCrAtEnd_; }
void sif::printError(const char *fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
fsfwPrint(sif::PrintLevel::ERROR_LEVEL, fmt, args); fsfwPrint(sif::PrintLevel::ERROR_LEVEL, fmt, args);
va_end(args); va_end(args);
} }
void sif::printCallback() {
taskRunning = true;
size_t chunk;
{
MutexGuard guard(ringMutex, MutexIF::TimeoutType::BLOCKING);
chunk = std::min(bytesUsed, MAX_BYTES_PER_CYCLE);
}
if (chunk > 0) {
// Safe without the mutex: producers only touch the ring beyond
// readIdx + bytesUsed and only the print task advances readIdx.
const size_t firstPart = std::min(chunk, RING_SIZE - readIdx);
fwrite(ring + readIdx, 1, firstPart, stdout);
fwrite(ring, 1, chunk - firstPart, stdout);
fflush(stdout);
}
uint32_t dropped;
{
MutexGuard guard(ringMutex, MutexIF::TimeoutType::BLOCKING);
readIdx = (readIdx + chunk) % RING_SIZE;
bytesUsed -= chunk;
dropped = droppedMessages;
droppedMessages = 0;
}
droppedMessagesTotal += dropped;
if (dropped != 0) {
sif::printError("ServiceInterfacePrinter: Dropped %lu messages\n",
static_cast<unsigned long>(dropped));
}
}
void sif::init() { ringMutex = MutexFactory::instance()->createMutex(); }
uint32_t sif::getDroppedMessagesCount() { return droppedMessagesTotal; }
#else #else
void sif::printInfo(const char* fmt, ...) {} void sif::printInfo(const char *fmt, ...) {}
void sif::printWarning(const char* fmt, ...) {} void sif::printWarning(const char *fmt, ...) {}
void sif::printDebug(const char* fmt, ...) {} void sif::printDebug(const char *fmt, ...) {}
void sif::printError(const char* fmt, ...) {} void sif::printError(const char *fmt, ...) {}
void sif::printCallback() {}
void sif::init() {}
uint32_t sif::getDroppedMessagesCount() { return 0; }
#endif /* FSFW_DISABLE_PRINTOUT == 0 */ #endif /* FSFW_DISABLE_PRINTOUT == 0 */
@@ -39,11 +39,6 @@ PrintLevel getPrintLevel();
void setToAddCrAtEnd(bool addCrAtEnd_); void setToAddCrAtEnd(bool addCrAtEnd_);
/**
* Replaces the last char of a print buffer with a newline
*/
void setReplaceLastCharWithNewline(bool replace);
/** /**
* These functions can be used like the C stdio printf and forward the * These functions can be used like the C stdio printf and forward the
* supplied formatted string arguments to a printf function. * supplied formatted string arguments to a printf function.
@@ -56,21 +51,6 @@ void printWarning(const char* fmt, ...);
void printDebug(const char* fmt, ...); void printDebug(const char* fmt, ...);
void printError(const char* fmt, ...); void printError(const char* fmt, ...);
/**
* This function is to be called periodically by a dedicated print task.
*/
void printCallback();
/**
* Initializes the global state for the print task.
*/
void init();
/**
* Gets the total estimated number of dropped messages
*/
uint32_t getDroppedMessagesCount();
} // namespace sif } // namespace sif
#endif /* FSFW_SERVICEINTERFACE_SERVICEINTERFACEPRINTER */ #endif /* FSFW_SERVICEINTERFACE_SERVICEINTERFACEPRINTER */
@@ -1,14 +0,0 @@
#include "ServiceInterfacePrinterTask.h"
#include "ServiceInterfacePrinter.h"
#include "fsfw/objectmanager/SystemObject.h"
ServiceInterfacePrinterTask::ServiceInterfacePrinterTask(object_id_t objectId)
: SystemObject(objectId) {
sif::init();
}
ReturnValue_t ServiceInterfacePrinterTask::performOperation(uint8_t operationCode) {
sif::printCallback();
return returnvalue::OK;
}
@@ -1,10 +0,0 @@
#pragma once
#include "fsfw/objectmanager/SystemObject.h"
#include "fsfw/tasks/ExecutableObjectIF.h"
class ServiceInterfacePrinterTask : public ExecutableObjectIF, public SystemObject {
public:
explicit ServiceInterfacePrinterTask(object_id_t objectId);
ReturnValue_t performOperation(uint8_t operationCode) override;
};
+1 -1
View File
@@ -99,7 +99,7 @@ class Subsystem : public SubsystemBase, public HasModeSequenceIF {
EntryPointer entries; EntryPointer entries;
}; };
static const uint8_t MAX_NUMBER_OF_TABLES_OR_SEQUENCES = 100; static const uint8_t MAX_NUMBER_OF_TABLES_OR_SEQUENCES = 70;
static const uint8_t MAX_LENGTH_OF_TABLE_OR_SEQUENCE = 20; static const uint8_t MAX_LENGTH_OF_TABLE_OR_SEQUENCE = 20;
+3 -4
View File
@@ -78,8 +78,9 @@ void SubsystemBase::executeTable(HybridIterator<ModeListEntry> tableIter, Submod
submodeToCommand = targetSubmode; submodeToCommand = targetSubmode;
} }
if (healthHelper.healthTable->hasHealth(convertToDeviceObjectId(object))) { if (healthHelper.healthTable->hasHealth(object)) {
switch (healthHelper.healthTable->getHealth(convertToDeviceObjectId(object))) {
switch (healthHelper.healthTable->getHealth(object)) {
case NEEDS_RECOVERY: case NEEDS_RECOVERY:
case FAULTY: case FAULTY:
case PERMANENT_FAULTY: case PERMANENT_FAULTY:
@@ -352,5 +353,3 @@ ReturnValue_t SubsystemBase::registerChild(object_id_t childObjectId, MessageQue
} }
return returnvalue::OK; return returnvalue::OK;
} }
object_id_t SubsystemBase::convertToDeviceObjectId(object_id_t id) { return id; }
+2 -8
View File
@@ -113,8 +113,8 @@ class SubsystemBase : public SystemObject,
* We need to know the target Submode, as children are able to inherit the submode * We need to know the target Submode, as children are able to inherit the submode
* Still, we have a default for all child implementations which do not use submode inheritance * Still, we have a default for all child implementations which do not use submode inheritance
*/ */
virtual void executeTable(HybridIterator<ModeListEntry> tableIter, void executeTable(HybridIterator<ModeListEntry> tableIter,
Submode_t targetSubmode = SUBMODE_NONE); Submode_t targetSubmode = SUBMODE_NONE);
ReturnValue_t updateChildMode(MessageQueueId_t queue, Mode_t mode, Submode_t submode); ReturnValue_t updateChildMode(MessageQueueId_t queue, Mode_t mode, Submode_t submode);
ReturnValue_t updateChildModeByObjId(object_id_t objectId, Mode_t mode, Submode_t submode); ReturnValue_t updateChildModeByObjId(object_id_t objectId, Mode_t mode, Submode_t submode);
@@ -153,12 +153,6 @@ class SubsystemBase : public SystemObject,
virtual void announceMode(bool recursive) override; virtual void announceMode(bool recursive) override;
virtual void modeChanged(); virtual void modeChanged();
/**
* @brief Provides an adaptation point for the user to change an objectId into
* a different objectId.
*/
virtual object_id_t convertToDeviceObjectId(object_id_t id);
}; };
#endif /* FSFW_SUBSYSTEM_SUBSYSTEMBASE_H_ */ #endif /* FSFW_SUBSYSTEM_SUBSYSTEMBASE_H_ */
+2 -5
View File
@@ -18,10 +18,6 @@ PusDistributor::PusDistributor(uint16_t setApid, object_id_t setObjectId, Storag
PusDistributor::~PusDistributor() = default; PusDistributor::~PusDistributor() = default;
void PusDistributor::setVerificationReporter(object_id_t verificationReporter_) {
verificationReporter = verificationReporter_;
}
ReturnValue_t PusDistributor::selectDestination(MessageQueueId_t& destId) { ReturnValue_t PusDistributor::selectDestination(MessageQueueId_t& destId) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 && PUS_DISTRIBUTOR_DEBUGGING == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1 && PUS_DISTRIBUTOR_DEBUGGING == 1
store_address_t storeId = currentMessage.getStorageId(); store_address_t storeId = currentMessage.getStorageId();
@@ -135,7 +131,8 @@ ReturnValue_t PusDistributor::initialize() {
return ObjectManagerIF::CHILD_INIT_FAILED; return ObjectManagerIF::CHILD_INIT_FAILED;
} }
if (verifyChannel == nullptr) { if (verifyChannel == nullptr) {
verifyChannel = ObjectManager::instance()->get<VerificationReporterIF>(verificationReporter); verifyChannel =
ObjectManager::instance()->get<VerificationReporterIF>(objects::VERIFICATION_REPORTER);
if (verifyChannel == nullptr) { if (verifyChannel == nullptr) {
return ObjectManagerIF::CHILD_INIT_FAILED; return ObjectManagerIF::CHILD_INIT_FAILED;
} }
-6
View File
@@ -43,10 +43,6 @@ class PusDistributor : public TcDistributorBase,
[[nodiscard]] MessageQueueId_t getRequestQueue() const override; [[nodiscard]] MessageQueueId_t getRequestQueue() const override;
ReturnValue_t initialize() override; ReturnValue_t initialize() override;
[[nodiscard]] uint32_t getIdentifier() const override; [[nodiscard]] uint32_t getIdentifier() const override;
/**
* @brief Can be used to set the verification reporter if another than the default should be used
*/
void setVerificationReporter(object_id_t verificationReporter_);
protected: protected:
struct ServiceInfo { struct ServiceInfo {
@@ -79,8 +75,6 @@ class PusDistributor : public TcDistributorBase,
*/ */
ReturnValue_t tcStatus; ReturnValue_t tcStatus;
object_id_t verificationReporter = objects::VERIFICATION_REPORTER;
/** /**
* This method reads the packet service, checks if such a service is * This method reads the packet service, checks if such a service is
* registered and forwards the packet to the destination. * registered and forwards the packet to the destination.
+1 -1
View File
@@ -25,7 +25,7 @@ static constexpr ReturnValue_t INCORRECT_SECONDARY_HEADER = MAKE_RETURN_CODE(11)
static constexpr uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::TMTC_DISTRIBUTION; static constexpr uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::TMTC_DISTRIBUTION;
//! P1: Returnvalue, P2: 0 for TM issues, 1 for TC issues //! P1: Returnvalue, P2: 0 for TM issues, 1 for TC issues
static constexpr Event HANDLE_PACKET_FAILED = event::makeEvent<SUBSYSTEM_ID, 0, severity::LOW>(); static constexpr Event HANDLE_PACKET_FAILED = MAKE_EVENT(0, severity::LOW);
}; // namespace tmtcdistrib }; // namespace tmtcdistrib
#endif // FSFW_TMTCPACKET_DEFINITIONS_H #endif // FSFW_TMTCPACKET_DEFINITIONS_H
+2
View File
@@ -192,6 +192,8 @@ class Clock {
static MutexIF *timeMutex; static MutexIF *timeMutex;
static uint16_t leapSeconds; static uint16_t leapSeconds;
static bool leapSecondsSet; static bool leapSecondsSet;
static bool monotonicClockInitialized;
static timeval monotonicClockOffset;
}; };
#endif /* FSFW_TIMEMANAGER_CLOCK_H_ */ #endif /* FSFW_TIMEMANAGER_CLOCK_H_ */
-5
View File
@@ -114,11 +114,6 @@ void TmStoreMessage::setDownlinkContentTimeMessage(CommandMessage* cmd, store_ad
cmd->setParameter2(storeId.raw); cmd->setParameter2(storeId.raw);
} }
void TmStoreMessage::setStopDownlinkContentMessage(CommandMessage* cmd, store_address_t storeId) {
cmd->setCommand(STOP_DOWNLINK_STORE_CONTENT);
cmd->setParameter2(storeId.raw);
}
uint32_t TmStoreMessage::getAddressLow(CommandMessage* cmd) { return cmd->getParameter(); } uint32_t TmStoreMessage::getAddressLow(CommandMessage* cmd) { return cmd->getParameter(); }
uint32_t TmStoreMessage::getAddressHigh(CommandMessage* cmd) { return cmd->getParameter2(); } uint32_t TmStoreMessage::getAddressHigh(CommandMessage* cmd) { return cmd->getParameter2(); }
-2
View File
@@ -21,7 +21,6 @@ class TmStoreMessage {
static void setStoreCatalogueReportMessage(CommandMessage* cmd, object_id_t objectId, static void setStoreCatalogueReportMessage(CommandMessage* cmd, object_id_t objectId,
store_address_t storeId); store_address_t storeId);
static void setDownlinkContentTimeMessage(CommandMessage* cmd, store_address_t storeId); static void setDownlinkContentTimeMessage(CommandMessage* cmd, store_address_t storeId);
static void setStopDownlinkContentMessage(CommandMessage* cmd, store_address_t storeId);
static void setIndexReportMessage(CommandMessage* cmd, store_address_t storeId); static void setIndexReportMessage(CommandMessage* cmd, store_address_t storeId);
static ReturnValue_t setDeleteBlocksMessage(CommandMessage* cmd, uint32_t addressLow, static ReturnValue_t setDeleteBlocksMessage(CommandMessage* cmd, uint32_t addressLow,
uint32_t addressHigh); uint32_t addressHigh);
@@ -55,7 +54,6 @@ class TmStoreMessage {
static const Command_t DOWNLINK_STORE_CONTENT_BLOCKS = MAKE_COMMAND_ID(12); static const Command_t DOWNLINK_STORE_CONTENT_BLOCKS = MAKE_COMMAND_ID(12);
static const Command_t REPORT_INDEX_REQUEST = MAKE_COMMAND_ID(13); static const Command_t REPORT_INDEX_REQUEST = MAKE_COMMAND_ID(13);
static const Command_t INDEX_REPORT = MAKE_COMMAND_ID(14); static const Command_t INDEX_REPORT = MAKE_COMMAND_ID(14);
static const Command_t STOP_DOWNLINK_STORE_CONTENT = MAKE_COMMAND_ID(15);
private: private:
TmStoreMessage(); TmStoreMessage();
@@ -128,7 +128,7 @@ ReturnValue_t CommandingServiceBase::initialize() {
if (verificationReporter == nullptr) { if (verificationReporter == nullptr) {
verificationReporter = verificationReporter =
ObjectManager::instance()->get<VerificationReporterIF>(verificationReporterId); ObjectManager::instance()->get<VerificationReporterIF>(objects::VERIFICATION_REPORTER);
if (verificationReporter == nullptr) { if (verificationReporter == nullptr) {
return ObjectManagerIF::CHILD_INIT_FAILED; return ObjectManagerIF::CHILD_INIT_FAILED;
} }
@@ -136,10 +136,6 @@ ReturnValue_t CommandingServiceBase::initialize() {
return returnvalue::OK; return returnvalue::OK;
} }
void CommandingServiceBase::setVerificationReporter(object_id_t verificationReporterId_) {
verificationReporterId = verificationReporterId_;
}
void CommandingServiceBase::handleCommandQueue() { void CommandingServiceBase::handleCommandQueue() {
CommandMessage reply; CommandMessage reply;
ReturnValue_t result; ReturnValue_t result;
@@ -126,8 +126,6 @@ class CommandingServiceBase : public SystemObject,
ReturnValue_t initialize() override; ReturnValue_t initialize() override;
void setVerificationReporter(object_id_t verificationReporterId_);
/** /**
* Implementation of ExecutableObjectIF function * Implementation of ExecutableObjectIF function
* *
@@ -264,8 +262,6 @@ class CommandingServiceBase : public SystemObject,
const uint16_t timeoutSeconds; const uint16_t timeoutSeconds;
object_id_t verificationReporterId = objects::VERIFICATION_REPORTER;
PusTcReader tcReader; PusTcReader tcReader;
TmStoreHelper tmStoreHelper; TmStoreHelper tmStoreHelper;
TmSendHelper tmSendHelper; TmSendHelper tmSendHelper;
+2 -10
View File
@@ -111,7 +111,7 @@ ReturnValue_t PusServiceBase::initialize() {
} }
if (psbParams.pusDistributor == nullptr) { if (psbParams.pusDistributor == nullptr) {
psbParams.pusDistributor = ObjectManager::instance()->get<PusDistributorIF>(pusDistributor); psbParams.pusDistributor = ObjectManager::instance()->get<PusDistributorIF>(PUS_DISTRIBUTOR);
if (psbParams.pusDistributor != nullptr) { if (psbParams.pusDistributor != nullptr) {
registerService(*psbParams.pusDistributor); registerService(*psbParams.pusDistributor);
} }
@@ -126,7 +126,7 @@ ReturnValue_t PusServiceBase::initialize() {
if (psbParams.verifReporter == nullptr) { if (psbParams.verifReporter == nullptr) {
psbParams.verifReporter = psbParams.verifReporter =
ObjectManager::instance()->get<VerificationReporterIF>(verificationReporter); ObjectManager::instance()->get<VerificationReporterIF>(objects::VERIFICATION_REPORTER);
if (psbParams.verifReporter == nullptr) { if (psbParams.verifReporter == nullptr) {
return ObjectManagerIF::CHILD_INIT_FAILED; return ObjectManagerIF::CHILD_INIT_FAILED;
} }
@@ -134,14 +134,6 @@ ReturnValue_t PusServiceBase::initialize() {
return returnvalue::OK; return returnvalue::OK;
} }
void PusServiceBase::setPusDistributor(object_id_t pusDistributor_) {
pusDistributor = pusDistributor_;
}
void PusServiceBase::setVerificationReporter(object_id_t verificationReporter_) {
verificationReporter = verificationReporter_;
}
void PusServiceBase::setTcPool(StorageManagerIF& tcPool) { psbParams.tcPool = &tcPool; } void PusServiceBase::setTcPool(StorageManagerIF& tcPool) { psbParams.tcPool = &tcPool; }
void PusServiceBase::setErrorReporter(InternalErrorReporterIF& errReporter_) { void PusServiceBase::setErrorReporter(InternalErrorReporterIF& errReporter_) {
-6
View File
@@ -201,9 +201,6 @@ class PusServiceBase : public ExecutableObjectIF,
void setTaskIF(PeriodicTaskIF* taskHandle) override; void setTaskIF(PeriodicTaskIF* taskHandle) override;
[[nodiscard]] const char* getName() const override; [[nodiscard]] const char* getName() const override;
void setPusDistributor(object_id_t pusDistributor_);
void setVerificationReporter(object_id_t verificationReporter_);
protected: protected:
/** /**
* @brief Handle to the underlying task * @brief Handle to the underlying task
@@ -231,9 +228,6 @@ class PusServiceBase : public ExecutableObjectIF,
static object_id_t PACKET_DESTINATION; static object_id_t PACKET_DESTINATION;
static object_id_t PUS_DISTRIBUTOR; static object_id_t PUS_DISTRIBUTOR;
object_id_t pusDistributor = PUS_DISTRIBUTOR;
object_id_t verificationReporter = objects::VERIFICATION_REPORTER;
private: private:
void handleRequestQueue(); void handleRequestQueue();
}; };
+1 -1
View File
@@ -1,7 +1,7 @@
add_subdirectory(common) add_subdirectory(common)
add_subdirectory(host)
if(UNIX) if(UNIX)
add_subdirectory(host)
add_subdirectory(linux) add_subdirectory(linux)
endif() endif()
+1 -1
View File
@@ -1 +1 @@
target_sources(${LIB_FSFW_NAME} PRIVATE HostFilesystem.cpp) target_sources(${LIB_FSFW_NAME} PUBLIC HostFilesystem.cpp)
+1 -1
View File
@@ -116,7 +116,7 @@ class CommandExecutor {
int currentFd = 0; int currentFd = 0;
bool printOutput = true; bool printOutput = true;
std::vector<char> readVec; std::vector<char> readVec;
struct pollfd waiter{}; struct pollfd waiter {};
SimpleRingBuffer* ringBuffer = nullptr; SimpleRingBuffer* ringBuffer = nullptr;
DynamicFIFO<uint16_t>* sizesFifo = nullptr; DynamicFIFO<uint16_t>* sizesFifo = nullptr;
-48
View File
@@ -82,52 +82,4 @@ TEST_CASE("Pool Dataset Test", "[datapool]") {
CHECK(buf[31] == 0b10000000); CHECK(buf[31] == 0b10000000);
CHECK(buf[32] == 0); CHECK(buf[32] == 0);
} }
SECTION("Pool Dataset Deserialization With Validity") {
uint8_t buf[64]{};
TestDatasetLarger source;
source.setChildrenValidity(true);
source.test1 = 502392;
source.test1.setValid(false);
source.test7 = -5;
source.test8.entry[1] = 1234;
source.test8.setValid(false);
source.serializeWithValidityBlob = true;
size_t serLen = 0;
uint8_t* dataPtr = buf;
REQUIRE(source.serialize(&dataPtr, &serLen, sizeof(buf), SerializeIF::Endianness::NETWORK) ==
returnvalue::OK);
TestDatasetLarger target;
target.setChildrenValidity(true);
const uint8_t* readPtr = buf;
size_t remaining = serLen;
CHECK(target.deSerializeWithValidityBlob(&readPtr, &remaining,
SerializeIF::Endianness::NETWORK) == returnvalue::OK);
CHECK(remaining == 0);
CHECK(readPtr == buf + serLen);
CHECK(target.test0.isValid());
CHECK(!target.test1.isValid());
CHECK(target.test1.get() == 502392);
CHECK(target.test7.isValid());
CHECK(target.test7.get() == -5);
CHECK(!target.test8.isValid());
CHECK(target.test8.entry[1] == 1234);
}
SECTION("Pool Dataset Deserialization With Validity Short Stream") {
uint8_t buf[64]{};
TestDatasetSmall target;
target.test0 = 7;
target.test0.setValid(true);
const uint8_t* readPtr = buf;
// One byte short of the 5 value bytes and the 1 byte validity blob
size_t remaining = 5;
CHECK(target.deSerializeWithValidityBlob(&readPtr, &remaining,
SerializeIF::Endianness::NETWORK) ==
SerializeIF::STREAM_TOO_SHORT);
CHECK(remaining == 5);
CHECK(target.test0.get() == 7);
CHECK(target.test0.isValid());
}
} }
+2 -3
View File
@@ -1,4 +1,3 @@
target_sources( target_sources(
${FSFW_TEST_TGT} ${FSFW_TEST_TGT} PRIVATE testDleEncoder.cpp testOpDivider.cpp testBitutil.cpp
PRIVATE testCobsEncoder.cpp testDleEncoder.cpp testOpDivider.cpp testCRC.cpp testTimevalOperations.cpp)
testBitutil.cpp testCRC.cpp testTimevalOperations.cpp)
@@ -1,336 +0,0 @@
#include <algorithm>
#include <array>
#include <cstdint>
#include <vector>
#include "catch2/catch_test_macros.hpp"
#include "fsfw/globalfunctions/CobsEncoder.h"
#include "fsfw/returnvalues/returnvalue.h"
// no zero bytes
constexpr std::array<uint8_t, 3> ti0 = {1, 2, 43};
constexpr std::array<uint8_t, 5> to0 = {0x04, 0x01, 0x02, 0x2b, 0x00};
// single zero byte
constexpr std::array<uint8_t, 4> ti1 = {1, 2, 0, 3};
constexpr std::array<uint8_t, 6> to1 = {3, 1, 2, 2, 3, 0};
// multiple zero bytes in sequence
constexpr std::array<uint8_t, 6> ti2 = {1, 2, 0, 0, 0, 3};
constexpr std::array<uint8_t, 8> to2 = {3, 1, 2, 1, 1, 2, 3, 0};
// stuffing bytes required because of the length without a single zero byte
// clang-format off
constexpr std::array<uint8_t, 257> ti3 = {
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
// #257
0xaa,
};
constexpr std::array<uint8_t, 260> to3 = {
0xff,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4,5 , 6, 7, 8, 9, 10, 11, 12, 13, 14,
// #255 -> stuffing
0x04,
15, 16,
0xaa, 0
};
// clang-format on
// real packet data
// PLOC startup packets (usually in one or two messages, but 3 separate COBS encoded packets)
// clang-format off
constexpr std::array<uint8_t, 36> ploc_startup_1_i = {
0x08,0x42,0xc0,0x00,0x00,0x1d,0x20,0x05,0x02,0x00,0x00,0x00,0x00,0x40,0x61,0x0d,0x02,0x8e,
0xb4,0xef,0x2e,0xe1,0x44,0x00,0x1f,0x03,0x00,0x00,0x52,0x18,0x00,0x00,0x00,0x00,0x1b,0xe2,
};
constexpr std::array<uint8_t, 36> ploc_startup_2_i = {
0x08,0x42,0xc0,0x01,0x00,0x1d,0x20,0x05,0x02,0x00,0x01,0x00,0x00,0x40,0x61,0x0d,0x02,0x8e,
0xb4,0xef,0x30,0x74,0x44,0x00,0x1f,0x07,0x00,0x00,0x50,0x03,0x00,0x00,0x00,0x00,0x98,0xe3,
};
constexpr std::array<uint8_t, 36> ploc_startup_3_i = {
0x08,0x42,0xc0,0x02,0x00,0x1d,0x20,0x05,0x01,0x00,0x02,0x00,0x00,0x40,0x61,0x0d,0x02,0x8e,
0xb5,0xb8,0x30,0x73,0x44,0x00,0x1f,0x07,0x00,0x00,0x3b,0x04,0x00,0x00,0x00,0x00,0x22,0x68,
};
constexpr std::array<uint8_t, 38> ploc_startup_1_o = {
0x04,0x08,0x42,0xc0,0x01,0x05,0x1d,0x20,0x05,0x02,
0x01,0x01,0x01,0x0b,0x40,0x61,0x0d,0x02,0x8e,0xb4,
0xef,0x2e,0xe1,0x44,0x03,0x1f,0x03,0x01,0x03,0x52,
0x18,0x01,0x01,0x01,0x03,0x1b,0xe2, 0x00,
};
constexpr std::array<uint8_t, 38> ploc_startup_2_o = {
0x05, 0x08,
0x42,0xc0,0x01,0x05,0x1d,0x20,0x05,0x02,0x02,0x01,
0x01,0x0b,0x40,0x61,0x0d,0x02,0x8e,0xb4,0xef,0x30,
0x74,0x44,0x03,0x1f,0x07,0x01,0x03,0x50,0x03,0x01,
0x01,0x01,0x03,0x98,0xe3,0x00
};
constexpr std::array<uint8_t, 38> ploc_startup_3_o = {
0x05,0x08,0x42,0xc0,0x02,0x05,0x1d,0x20,0x05,0x01,
0x02,0x02,0x01,0x0b,0x40,0x61,0x0d,0x02,0x8e,0xb5,
0xb8,0x30,0x73,0x44,0x03,0x1f,0x07,0x01,0x03,0x3b,
0x04,0x01,0x01,0x01,0x03,0x22,0x68,0x00,
};
// clang-format on
// PLOC Ping (sent from OBC)
// clang-format off
constexpr std::array<uint8_t, 13> ploc_ping_i = {
// as received by PLOC
0x18,0x42,0xc0,0x00,0x00,0x06,0x2f,0x11,0x01,0x00,
0x00,0xd5,0xc8,
};
constexpr std::array<uint8_t, 15> ploc_ping_o = {
// as sent from OBC
0x04,0x18,0x42,0xc0,0x01,0x05,0x06,0x2f,0x11,0x01,0x01,0x03,0xd5,0xc8,0x00,
};
// clang-format on
// Corresponding PLOC Pong (sent from PLOC)
// clang-format off
constexpr std::array<uint8_t, 26> ploc_pong_i = {
// as received by OBC
0x08,0x42,0xc0,0x05,0x00,0x13,0x20,0x01,0x07,0x00,
0x01,0x00,0x00,0x40,0x61,0x0d,0x02,0x98,0xbd,0xab,
0x18,0x42,0xc0,0x00,0x13,0xd5,
};
constexpr std::array<uint8_t, 28> ploc_pong_o = {
// as sent by PLOC
0x05,0x08,0x42,0xc0,0x05,0x05,0x13,0x20,0x01,0x07,
0x02,0x01,0x01,0x0b,0x40,0x61,0x0d,0x02,0x98,0xbd,
0xab,0x18,0x42,0xc0,0x03,0x13,0xd5,0x00,
};
// clang-format on
#define FOR_EACH_TEST_ARRAY(macro) \
macro(ti0, to0); \
macro(ti1, to1); \
macro(ti2, to2); \
macro(ti3, to3); \
macro(ploc_startup_1_i, ploc_startup_1_o); \
macro(ploc_startup_2_i, ploc_startup_2_o); \
macro(ploc_startup_3_i, ploc_startup_3_o); \
macro(ploc_ping_i, ploc_ping_o); \
macro(ploc_pong_i, ploc_pong_o);
template <typename FI, typename FE, typename SI>
void check_iter_equal(FI first_begin, FE first_end, SI second_begin) {
auto first_iter = first_begin;
auto second_iter = second_begin;
auto i = 0;
while (first_iter < first_end) {
CHECK(*first_iter == *second_iter);
++first_iter;
++second_iter;
++i;
}
// safety check
CHECK(std::equal(first_begin, first_end, second_begin));
}
#define ENCODE_TEST(input, output) \
TEST_CASE("COBS encode " #input, "[cobs]") { \
std::vector<uint8_t> outstream; \
outstream.resize(output.size()); \
size_t encodedLen = 0; \
auto result = CobsEncoder::encode(input.data(), input.size(), outstream.data(), \
outstream.size(), &encodedLen); \
if (result == returnvalue::OK) { \
CHECK(encodedLen == output.size()); \
check_iter_equal(outstream.begin(), outstream.end(), output.begin()); \
} else { \
CHECK(result == returnvalue::OK); \
} \
}
FOR_EACH_TEST_ARRAY(ENCODE_TEST)
#undef ENCODE_TEST
#define DECODE_TEST(input, output) \
TEST_CASE("COBS decode " #output, "[cobs]") { \
std::vector<uint8_t> instream; \
instream.resize(input.size()); \
size_t decodedLen = 0; \
size_t readLen = 0; \
auto result = CobsEncoder::decode(output.data(), output.size(), &readLen, instream.data(), \
instream.size(), &decodedLen); \
if (result == returnvalue::OK) { \
CHECK(readLen == output.size()); \
CHECK(decodedLen == input.size()); \
check_iter_equal(instream.begin(), instream.end(), input.begin()); \
} else { \
CHECK(result == returnvalue::OK); \
} \
}
FOR_EACH_TEST_ARRAY(DECODE_TEST)
#undef DECODE_TEST
// payloads far past any block count round-trip and the worst case bound is tight enough to hold
// them
TEST_CASE("Large payload", "[cobs]") {
// no zero bytes at all, so this needs the maximum number of blocks the input length allows
std::vector<uint8_t> input(64 * 1024, 1);
std::vector<uint8_t> encoded(CobsEncoder::worstCaseEncodedLen(input.size()));
size_t encodedLen = 0;
auto result =
CobsEncoder::encode(input.data(), input.size(), encoded.data(), encoded.size(), &encodedLen);
REQUIRE(result == returnvalue::OK);
CHECK(encodedLen == encoded.size());
std::vector<uint8_t> decoded(input.size());
size_t decodedLen = 0;
size_t readLen = 0;
result = CobsEncoder::decode(encoded.data(), encodedLen, &readLen, decoded.data(), decoded.size(),
&decodedLen);
REQUIRE(result == returnvalue::OK);
CHECK(readLen == encodedLen);
REQUIRE(decodedLen == input.size());
CHECK(std::equal(decoded.begin(), decoded.end(), input.begin()));
}
// check that too small target buffers are not blindly used
TEST_CASE("Insufficient space", "[cobs]") {
std::vector<uint8_t> output{0, 2};
size_t encodedLen = 0;
auto result =
CobsEncoder::encode(ti1.data(), ti1.size(), output.data(), output.size(), &encodedLen);
CHECK(result == CobsEncoder::INSUFFICIENT_SPACE);
// even equal size should not work
output.resize(ti1.size());
result = CobsEncoder::encode(ti1.data(), ti1.size(), output.data(), output.size(), &encodedLen);
CHECK(result == CobsEncoder::INSUFFICIENT_SPACE);
// check reverse direction for decoding
output.resize(2);
size_t readLen;
size_t decodedLen;
result = CobsEncoder::decode(to1.data(), to1.size(), &readLen, output.data(), output.size(),
&decodedLen);
CHECK(result == CobsEncoder::INSUFFICIENT_SPACE);
}
TEST_CASE("Malformed COBS data", "[cobs]") {
// decode a cut-off packet
std::vector<uint8_t> input{std::begin(to2), std::end(to2)};
input.resize(to2.size() / 2);
std::vector<uint8_t> output;
output.resize(ti2.size() * 2);
size_t encodedLen;
size_t readLen;
auto result = CobsEncoder::decode(input.data(), input.size(), &readLen, output.data(),
output.size(), &encodedLen);
CHECK(result == CobsEncoder::STREAM_TOO_SHORT);
// inject a zero byte somewhere in the middle of a list of nonzero bytes
input = {10, 1, 2, 3, 4, 0, 6, 7, 8, 9, 0};
result = CobsEncoder::decode(input.data(), input.size(), &readLen, output.data(), output.size(),
&encodedLen);
CHECK(result == CobsEncoder::DECODING_ERROR);
// the whole broken frame is skipped, up to and including its delimiter
CHECK(readLen == 6);
}
// a corrupt frame must not hold back the intact frames queued behind it
TEST_CASE("Resynchronisation after a corrupt frame", "[cobs]") {
// a block claiming far more bytes than the frame holds, terminated like a real frame
std::vector<uint8_t> stream{0x40, 0x11, 0x22, 0x00};
const size_t corruptLen = stream.size();
stream.insert(stream.end(), to1.begin(), to1.end());
std::vector<uint8_t> output(ti1.size());
size_t readLen = 0;
size_t decodedLen = 0;
auto result = CobsEncoder::decode(stream.data(), stream.size(), &readLen, output.data(),
output.size(), &decodedLen);
REQUIRE(result == CobsEncoder::DECODING_ERROR);
REQUIRE(readLen == corruptLen);
// the next frame decodes from where the corrupt one ended
result = CobsEncoder::decode(stream.data() + readLen, stream.size() - readLen, &readLen,
output.data(), output.size(), &decodedLen);
REQUIRE(result == returnvalue::OK);
CHECK(readLen == to1.size());
REQUIRE(decodedLen == ti1.size());
CHECK(std::equal(output.begin(), output.end(), ti1.begin()));
}
// without a delimiter the frame may simply still be arriving, so nothing may be consumed
TEST_CASE("Incomplete frame is not consumed", "[cobs]") {
std::vector<uint8_t> output(16);
size_t readLen = 0xdead;
size_t decodedLen = 0xdead;
// a self-consistent frame that is merely missing its delimiter
std::vector<uint8_t> input{3, 1, 2, 2, 3};
auto result = CobsEncoder::decode(input.data(), input.size(), &readLen, output.data(),
output.size(), &decodedLen);
CHECK(result == CobsEncoder::STREAM_TOO_SHORT);
CHECK(readLen == 0);
CHECK(decodedLen == 0);
// once the delimiter arrives, the very same bytes decode
input.push_back(0);
result = CobsEncoder::decode(input.data(), input.size(), &readLen, output.data(), output.size(),
&decodedLen);
REQUIRE(result == returnvalue::OK);
CHECK(readLen == input.size());
CHECK(decodedLen == ti1.size());
}
// an empty payload survives a round trip and is reported as a zero length frame
TEST_CASE("Empty frame", "[cobs]") {
std::vector<uint8_t> encoded(CobsEncoder::worstCaseEncodedLen(0));
size_t encodedLen = 0;
auto result = CobsEncoder::encode(nullptr, 0, encoded.data(), encoded.size(), &encodedLen);
REQUIRE(result == returnvalue::OK);
REQUIRE(encodedLen == 2);
CHECK(encoded[0] == 0x01);
CHECK(encoded[1] == 0x00);
std::vector<uint8_t> output(4);
size_t readLen = 0;
size_t decodedLen = 0;
result = CobsEncoder::decode(encoded.data(), encodedLen, &readLen, output.data(), output.size(),
&decodedLen);
CHECK(result == returnvalue::OK);
CHECK(readLen == 2);
CHECK(decodedLen == 0);
}
+1 -1
View File
@@ -38,7 +38,7 @@ TEST_CASE("PUS TM Reader", "[pus-tm-reader]") {
readerPtr->setTimeReader(&timeStamperAndReader); readerPtr->setTimeReader(&timeStamperAndReader);
deleteReader = true; deleteReader = true;
} }
REQUIRE(not*readerPtr); REQUIRE(not *readerPtr);
REQUIRE(readerPtr->isNull()); REQUIRE(readerPtr->isNull());
REQUIRE(readerPtr->parseDataWithCrcCheck() == returnvalue::OK); REQUIRE(readerPtr->parseDataWithCrcCheck() == returnvalue::OK);
REQUIRE(not readerPtr->isNull()); REQUIRE(not readerPtr->isNull());