Merge pull request 'Pull CFPD bugfix in dev branch' (#147) from pull-cfdp-fix-into-dev-branch into obsw-v3.0.0-dev

Reviewed-on: eive/fsfw#147
This commit is contained in:
Robin Müller 2023-06-09 13:41:29 +02:00
commit 5322de0599
12 changed files with 49 additions and 47 deletions

View File

@ -8,8 +8,34 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased] # [unreleased]
## Changed ## Fixes
- Important bugfix in CFDP PDU header format: The entity length field and the transaction sequence
number fields stored the actual length of the field instead of the length minus 1 like specified
in the CFDP standard.
- PUS Health Service: Size check for set health command.
Perform operation completion for announce health command.
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/746
- Linux OSAL `getUptime` fix: Check validity of `/proc/uptime` file before reading uptime.
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/745
- Small tweak for version getter
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/744
## Added
- add CFDP subsystem ID
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/742
## Changed
- Bump ETL version to 20.35.14
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/748
- Renamed `PCDU_2` subsystem ID to `POWER_SWITCH_IF`.
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/743
- Add new `PowerSwitchIF::SWITCH_UNKNOWN` returnvalue.
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/743
- Assert that `FixedArrayList` is larger than 0 at compile time.
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/740
- Health functions are virtual now. - Health functions are virtual now.
# [v6.0.0] 2023-02-10 # [v6.0.0] 2023-02-10

View File

@ -72,7 +72,7 @@ set(FSFW_ETL_LIB_MAJOR_VERSION
20 20
CACHE STRING "ETL library major version requirement") CACHE STRING "ETL library major version requirement")
set(FSFW_ETL_LIB_VERSION set(FSFW_ETL_LIB_VERSION
${FSFW_ETL_LIB_MAJOR_VERSION}.28.0 ${FSFW_ETL_LIB_MAJOR_VERSION}.35.14
CACHE STRING "ETL library exact version requirement") CACHE STRING "ETL library exact version requirement")
set(FSFW_ETL_LINK_TARGET etl::etl) set(FSFW_ETL_LINK_TARGET etl::etl)

View File

@ -24,8 +24,8 @@ ReturnValue_t HeaderCreator::serialize(uint8_t **buffer, size_t *size, size_t ma
*buffer += 1; *buffer += 1;
**buffer = pduDataFieldLen & 0x00ff; **buffer = pduDataFieldLen & 0x00ff;
*buffer += 1; *buffer += 1;
**buffer = segmentationCtrl << 7 | pduConf.sourceId.getWidth() << 4 | segmentMetadataFlag << 3 | **buffer = segmentationCtrl << 7 | ((pduConf.sourceId.getWidth() - 1) << 4) |
pduConf.seqNum.getWidth(); segmentMetadataFlag << 3 | (pduConf.seqNum.getWidth() - 1);
*buffer += 1; *buffer += 1;
*size += 4; *size += 4;
ReturnValue_t result = pduConf.sourceId.serialize(buffer, size, maxSize, streamEndianness); ReturnValue_t result = pduConf.sourceId.serialize(buffer, size, maxSize, streamEndianness);

View File

@ -78,11 +78,11 @@ cfdp::SegmentationControl PduHeaderReader::getSegmentationControl() const {
} }
cfdp::WidthInBytes PduHeaderReader::getLenEntityIds() const { cfdp::WidthInBytes PduHeaderReader::getLenEntityIds() const {
return static_cast<cfdp::WidthInBytes>((pointers.fixedHeader->fourthByte >> 4) & 0x07); return static_cast<cfdp::WidthInBytes>(((pointers.fixedHeader->fourthByte >> 4) & 0b111) + 1);
} }
cfdp::WidthInBytes PduHeaderReader::getLenSeqNum() const { cfdp::WidthInBytes PduHeaderReader::getLenSeqNum() const {
return static_cast<cfdp::WidthInBytes>(pointers.fixedHeader->fourthByte & 0x07); return static_cast<cfdp::WidthInBytes>((pointers.fixedHeader->fourthByte & 0b111) + 1);
} }
cfdp::SegmentMetadataFlag PduHeaderReader::getSegmentMetadataFlag() const { cfdp::SegmentMetadataFlag PduHeaderReader::getSegmentMetadataFlag() const {

View File

@ -89,8 +89,6 @@ timeval Clock::getUptime() {
} }
ReturnValue_t Clock::getUptime(timeval* uptime) { ReturnValue_t Clock::getUptime(timeval* uptime) {
// TODO This is not posix compatible and delivers only seconds precision
// Linux specific file read but more precise.
double uptimeSeconds; double uptimeSeconds;
std::ifstream ifile("/proc/uptime"); std::ifstream ifile("/proc/uptime");
if (ifile.bad()) { if (ifile.bad()) {

View File

@ -30,7 +30,7 @@
*/ */
void Factory::produceFrameworkObjects(void* args) { void Factory::produceFrameworkObjects(void* args) {
setStaticFrameworkObjectIds(); setStaticFrameworkObjectIds();
new EventManager(objects::EVENT_MANAGER); new EventManager(objects::EVENT_MANAGER, 120);
new HealthTable(objects::HEALTH_TABLE); new HealthTable(objects::HEALTH_TABLE);
new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER); new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER);

View File

@ -27,12 +27,7 @@ TEST_CASE("Action Helper", "[action]") {
CHECK(not testDhMock.executeActionCalled); CHECK(not testDhMock.executeActionCalled);
REQUIRE(actionHelper.handleActionMessage(&actionMessage) == returnvalue::OK); REQUIRE(actionHelper.handleActionMessage(&actionMessage) == returnvalue::OK);
CHECK(testDhMock.executeActionCalled); CHECK(testDhMock.executeActionCalled);
// No message is sent if everything is alright.
CHECK(not testMqMock.wasMessageSent()); CHECK(not testMqMock.wasMessageSent());
store_address_t invalidAddress;
ActionMessage::setCommand(&actionMessage, testActionId, invalidAddress);
actionHelper.handleActionMessage(&actionMessage);
CHECK(testMqMock.wasMessageSent());
const uint8_t* ptr = nullptr; const uint8_t* ptr = nullptr;
size_t size = 0; size_t size = 0;
REQUIRE(ipcStore->getData(paramAddress, &ptr, &size) == REQUIRE(ipcStore->getData(paramAddress, &ptr, &size) ==
@ -44,6 +39,10 @@ TEST_CASE("Action Helper", "[action]") {
for (uint8_t i = 0; i < 3; i++) { for (uint8_t i = 0; i < 3; i++) {
REQUIRE(ptr[i] == (i + 1)); REQUIRE(ptr[i] == (i + 1));
} }
// Action message without application data is also valid
store_address_t invalidAddress;
ActionMessage::setCommand(&actionMessage, testActionId, invalidAddress);
actionHelper.handleActionMessage(&actionMessage);
testDhMock.clearBuffer(); testDhMock.clearBuffer();
} }
@ -95,17 +94,5 @@ TEST_CASE("Action Helper", "[action]") {
REQUIRE(ActionMessage::getActionId(&testMessage) == testActionId); REQUIRE(ActionMessage::getActionId(&testMessage) == testActionId);
} }
SECTION("Missing IPC Data") {
ActionMessage::setCommand(&actionMessage, testActionId, store_address_t::invalid());
CHECK(not testDhMock.executeActionCalled);
REQUIRE(actionHelper.handleActionMessage(&actionMessage) == returnvalue::OK);
CommandMessage testMessage;
REQUIRE(testMqMock.getNextSentMessage(testMessage) == returnvalue::OK);
REQUIRE(testMessage.getCommand() == static_cast<uint32_t>(ActionMessage::STEP_FAILED));
REQUIRE(ActionMessage::getReturnCode(&testMessage) ==
static_cast<uint32_t>(StorageManagerIF::ILLEGAL_STORAGE_ID));
REQUIRE(ActionMessage::getStep(&testMessage) == 0);
}
SECTION("Data Reply") {} SECTION("Data Reply") {}
} }

View File

@ -97,7 +97,7 @@ TEST_CASE("CFDP Header", "[cfdp]") {
REQUIRE(creator.serialize(&serTarget, &serSize, serBuf.size(), REQUIRE(creator.serialize(&serTarget, &serSize, serBuf.size(),
SerializeIF::Endianness::BIG) == returnvalue::OK); SerializeIF::Endianness::BIG) == returnvalue::OK);
CHECK(serBuf[0] == 0x3f); CHECK(serBuf[0] == 0x3f);
CHECK(serBuf[3] == 0x99); CHECK(serBuf[3] == 0x88);
REQUIRE(creator.getCrcFlag() == true); REQUIRE(creator.getCrcFlag() == true);
REQUIRE(creator.getDirection() == cfdp::Direction::TOWARDS_SENDER); REQUIRE(creator.getDirection() == cfdp::Direction::TOWARDS_SENDER);
REQUIRE(creator.getLargeFileFlag() == true); REQUIRE(creator.getLargeFileFlag() == true);
@ -127,7 +127,7 @@ TEST_CASE("CFDP Header", "[cfdp]") {
REQUIRE(creator.getTransmissionMode() == cfdp::TransmissionMode::UNACKNOWLEDGED); REQUIRE(creator.getTransmissionMode() == cfdp::TransmissionMode::UNACKNOWLEDGED);
REQUIRE(creator.getSegmentationControl() == true); REQUIRE(creator.getSegmentationControl() == true);
// Last three bits are 2 now (length of seq number) and bit 1 to bit 3 is 4 (len entity IDs) // Last three bits are 2 now (length of seq number) and bit 1 to bit 3 is 4 (len entity IDs)
REQUIRE(serBuf[3] == 0b11001010); REQUIRE(serBuf[3] == 0b10111001);
uint32_t entityId = 0; uint32_t entityId = 0;
size_t deSerSize = 0; size_t deSerSize = 0;
SerializeAdapter::deSerialize(&entityId, serBuf.data() + 4, &deSerSize, SerializeAdapter::deSerialize(&entityId, serBuf.data() + 4, &deSerSize,
@ -175,7 +175,7 @@ TEST_CASE("CFDP Header", "[cfdp]") {
REQUIRE(serBuf[1] == 0); REQUIRE(serBuf[1] == 0);
REQUIRE(serBuf[2] == 0); REQUIRE(serBuf[2] == 0);
// Entity and Transaction Sequence number are 1 byte large // Entity and Transaction Sequence number are 1 byte large
REQUIRE(serBuf[3] == 0b00010001); REQUIRE(serBuf[3] == 0b00000000);
// Source ID // Source ID
REQUIRE(serBuf[4] == 0); REQUIRE(serBuf[4] == 0);
// Transaction Seq Number // Transaction Seq Number
@ -220,7 +220,7 @@ TEST_CASE("CFDP Header", "[cfdp]") {
REQUIRE(serBuf[1] == 0); REQUIRE(serBuf[1] == 0);
REQUIRE(serBuf[2] == 0); REQUIRE(serBuf[2] == 0);
// Entity and Transaction Sequence number are 1 byte large // Entity and Transaction Sequence number are 1 byte large
REQUIRE(serBuf[3] == 0b00010001); REQUIRE(serBuf[3] == 0b00000000);
REQUIRE(serSize == 7); REQUIRE(serSize == 7);
// Deser call not strictly necessary // Deser call not strictly necessary
auto reader = PduHeaderReader(serBuf.data(), serBuf.size()); auto reader = PduHeaderReader(serBuf.data(), serBuf.size());
@ -270,7 +270,7 @@ TEST_CASE("CFDP Header", "[cfdp]") {
REQUIRE(reader.parseData() == returnvalue::OK); REQUIRE(reader.parseData() == returnvalue::OK);
// Everything except version bit flipped to one now // Everything except version bit flipped to one now
REQUIRE(serBuf[0] == 0x3f); REQUIRE(serBuf[0] == 0x3f);
REQUIRE(serBuf[3] == 0b11001010); REQUIRE(serBuf[3] == 0b10111001);
REQUIRE(reader.getWholePduSize() == 14); REQUIRE(reader.getWholePduSize() == 14);
REQUIRE(reader.getCrcFlag() == true); REQUIRE(reader.getCrcFlag() == true);

View File

@ -68,7 +68,7 @@ TEST_CASE("File Data PDU", "[cfdp][pdu]") {
// Bits 1 to 3 length of enitity IDs is 2 // Bits 1 to 3 length of enitity IDs is 2
// Bit 4: Segment metadata flag is set // Bit 4: Segment metadata flag is set
// Bit 5 to seven: length of transaction seq num is 2 // Bit 5 to seven: length of transaction seq num is 2
REQUIRE(fileDataBuffer[3] == 0b10101010); REQUIRE(fileDataBuffer[3] == 0b10011001);
REQUIRE((fileDataBuffer[10] >> 6) & REQUIRE((fileDataBuffer[10] >> 6) &
0b11 == cfdp::RecordContinuationState::CONTAINS_START_AND_END); 0b11 == cfdp::RecordContinuationState::CONTAINS_START_AND_END);
// Segment metadata length // Segment metadata length

View File

@ -30,7 +30,7 @@ TEST_CASE("CFDP File Directive", "[cfdp][pdu]") {
REQUIRE(serBuf[1] == 0); REQUIRE(serBuf[1] == 0);
REQUIRE(serBuf[2] == 5); REQUIRE(serBuf[2] == 5);
// Entity and Transaction Sequence number are 1 byte large // Entity and Transaction Sequence number are 1 byte large
REQUIRE(serBuf[3] == 0b00010001); REQUIRE(serBuf[3] == 0b00000000);
// Source ID // Source ID
REQUIRE(serBuf[4] == 0); REQUIRE(serBuf[4] == 0);
// Transaction Seq Number // Transaction Seq Number
@ -82,4 +82,4 @@ TEST_CASE("CFDP File Directive", "[cfdp][pdu]") {
// Invalid file directive // Invalid file directive
REQUIRE(fdDeser.parseData() == cfdp::INVALID_DIRECTIVE_FIELD); REQUIRE(fdDeser.parseData() == cfdp::INVALID_DIRECTIVE_FIELD);
} }
} }

View File

@ -33,8 +33,8 @@ TEST_CASE("CFDP Base", "[cfdp]") {
// PDU data field length is 5 (4 + Directive code octet) // PDU data field length is 5 (4 + Directive code octet)
REQUIRE(serBuf[1] == 0); REQUIRE(serBuf[1] == 0);
REQUIRE(serBuf[2] == 5); REQUIRE(serBuf[2] == 5);
// Entity and Transaction Sequence number are 1 byte large // Entity and Transaction Sequence number are 1 byte large, value minus one is stored
REQUIRE(serBuf[3] == 0b00010001); REQUIRE(serBuf[3] == 0b00000000);
// Source ID // Source ID
REQUIRE(serBuf[4] == 0); REQUIRE(serBuf[4] == 0);
// Transaction Seq Number // Transaction Seq Number

View File

@ -9,6 +9,7 @@
#include <catch2/catch_approx.hpp> #include <catch2/catch_approx.hpp>
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <iostream> #include <iostream>
#include "CatchDefinitions.h" #include "CatchDefinitions.h"
@ -309,9 +310,7 @@ TEST_CASE("Local Pool Manager Tests", "[LocManTest]") {
HousekeepingMessage::setCollectionIntervalModificationCommand(&hkCmd, lpool::testSid, 0.4, HousekeepingMessage::setCollectionIntervalModificationCommand(&hkCmd, lpool::testSid, 0.4,
false); false);
CHECK(poolOwner.poolManager.handleHousekeepingMessage(&hkCmd) == returnvalue::OK); CHECK(poolOwner.poolManager.handleHousekeepingMessage(&hkCmd) == returnvalue::OK);
/* For non-diagnostics and a specified minimum frequency of 0.2 seconds, the CHECK_THAT(poolOwner.dataset.getCollectionInterval(), Catch::Matchers::WithinAbs(0.4, 0.01));
resulting collection interval should be 1.0 second */
CHECK(poolOwner.dataset.getCollectionInterval() == 1.0);
REQUIRE(poolOwnerMock.wasMessageSent()); REQUIRE(poolOwnerMock.wasMessageSent());
REQUIRE(poolOwnerMock.numberOfSentMessages() == 1); REQUIRE(poolOwnerMock.numberOfSentMessages() == 1);
CHECK(poolOwnerMock.clearLastSentMessage() == returnvalue::OK); CHECK(poolOwnerMock.clearLastSentMessage() == returnvalue::OK);
@ -348,14 +347,6 @@ TEST_CASE("Local Pool Manager Tests", "[LocManTest]") {
REQUIRE(poolOwnerMock.numberOfSentMessages() == 1); REQUIRE(poolOwnerMock.numberOfSentMessages() == 1);
CHECK(poolOwnerMock.clearLastSentMessage() == returnvalue::OK); CHECK(poolOwnerMock.clearLastSentMessage() == returnvalue::OK);
HousekeepingMessage::setCollectionIntervalModificationCommand(&hkCmd, lpool::testSid, 0.4,
false);
CHECK(poolOwner.poolManager.handleHousekeepingMessage(&hkCmd) ==
static_cast<int>(LocalDataPoolManager::WRONG_HK_PACKET_TYPE));
REQUIRE(poolOwnerMock.wasMessageSent());
REQUIRE(poolOwnerMock.numberOfSentMessages() == 1);
CHECK(poolOwnerMock.clearLastSentMessage() == returnvalue::OK);
HousekeepingMessage::setStructureReportingCommand(&hkCmd, lpool::testSid, false); HousekeepingMessage::setStructureReportingCommand(&hkCmd, lpool::testSid, false);
CHECK(poolOwner.poolManager.handleHousekeepingMessage(&hkCmd) == CHECK(poolOwner.poolManager.handleHousekeepingMessage(&hkCmd) ==
static_cast<int>(LocalDataPoolManager::WRONG_HK_PACKET_TYPE)); static_cast<int>(LocalDataPoolManager::WRONG_HK_PACKET_TYPE));