Compare commits

...
Author SHA1 Message Date
Tobias Baumgartl dc9c98cf8e Merge branch 'main' into baumgartl/cfdp-class-2 2026-10-02 11:27:06 +02:00
tbaumgartlandTobias Baumgartl 03d3b6cd68 fix: do not mix an epoch offset into the FreeRTOS monotonic clock (#74)
`Clock::getClockMonotonic` on FreeRTOS returned an epoch offset added on top of the uptime:

```cpp
*time = Timekeeper::instance()->getMonotonicClockOffset() + getUptime();
```

`monotonicClockOffset` is a `timeval` member with no initializer, and `Timekeeper`'s constructor
only initialises `offset`. The singleton is heap allocated, so the member holds indeterminate
bytes until `Timekeeper::setOffset` latches it, which happens on the first `Clock::setClock`.

Until that point `getClockMonotonic` returns garbage plus uptime, and at the latch it jumps by
whatever the difference happens to be. Any `Countdown` or `Stopwatch` armed before the latch
expires wrongly, in both directions:

- forward jump: `getCurrentTime() - startTime >= timeout`
- backward jump: the `getCurrentTime() < startTime` guard in `Countdown::hasTimedOut`

On our OBC this is not a race but the normal case. The CoreController sets the clock from its
own task, so everything constructed during object creation and early boot is on the wrong side
of the latch.

## Why the offset can go

The monotonicity came from the uptime alone. The offset only made the value look epoch based,
and nothing depends on that:

- `Countdown` uses `getCurrentTime() - startTime`
- `Stopwatch` uses `endTime - startTime`
- `PeriodicHelper::performPeriodicHkGeneration` uses `now - setSpec.lastGenerated`

Those are the only three callers in the framework. All take differences.

The Linux and host implementations already return `CLOCK_MONOTONIC_RAW` with no offset, so
FreeRTOS was the only OSAL where `getClockMonotonic` meant something different. The interface
doc in `Clock.h` also already describes the intended contract: "less suited when the absolute
time is required", with `CLOCK_MONOTONIC_RAW` named as the reference implementation.

## Changes

- `osal/freertos/Clock.cpp`: `getClockMonotonic` returns `getUptime()`
- `osal/freertos/Timekeeper.{h,cpp}`: drop `monotonicClockOffset`, `monotonicClockInitialized`,
  `getMonotonicClockOffset` and the never defined `setMonotonicClockOffset`. `setOffset` is now
  a one liner
- `timemanager/Clock.h`: drop the `monotonicClockInitialized` and `monotonicClockOffset` statics,
  which were declared but never defined or used

`Clock::getClock` is unchanged and still returns offset plus uptime, so the wall clock is
unaffected.

## Compatibility

`getClockMonotonic` on FreeRTOS now counts from scheduler start instead of the epoch. Code that
compares a monotonic timestamp against a wall clock value would break, but that would already be
broken on Linux, and no such code exists.

---------

Co-authored-by: Tobias Baumgartl <tobias.baumgartl@ksat-stuttgart.de>
Reviewed-on: #74
2026-09-25 14:24:14 +02:00
Tobias BaumgartlandClaude Opus 5 6faea2b0f0 cfdp: report a metadata only transaction as a complete delivery
A metadata only transaction - a proxy put request, for instance - carries no
file data and completes the moment its metadata arrives. handleTransferCompletion
took the branch for a null checksum, which sets the condition code and touches
neither delivery field, so both were reported at their reset defaults: a
transaction that succeeded announced itself as

  Finish Condition: No Error (0)
  File delivery code: Data Incomplete (1)
  File delivery status: Discard deliberately (0)

which contradicts itself, and goes out in the Finished PDU to the sender, not
only into the OBSW log. It was noticed on the flatsat, where every CFDP
downlink begins with exactly this kind of transaction carrying the proxy put
request, and each one reported a failed delivery on the console.

Nothing was expected of it and nothing is missing, so the delivery code is
DATA_COMPLETE; there is no file whose status could be reported, so the status
is FILE_STATUS_UNREPORTED.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4eBFnanCACYWdKCzhHMcC
2026-09-16 16:40:12 +02:00
Tobias BaumgartlandClaude Opus 5 844faf850f Stop arrayprinter from sizing its stack buffer from the input
printHex and printDec built a variable length array of (size + 1) * 7 + 1 and
size * 4 + 1 + lines bytes respectively, on the stack, from the caller's
buffer size. Nothing bounded that against the stack it ran on.

This reset an iOBC on the flatsat. A CFDP uplink driven without inter packet
spacing filled the USLP receive buffer with about 12 KB in one 300 ms cycle,
a frame parse error asked for the serial stream to be dumped, and printHex
tried to place an 84 KB array on an 8 KB task stack. FreeRTOS caught it as
STACK OVERFLOW DETECTED in USLP_RX and restarted the OBC.

The trigger needs a parse error, so it hid for as long as the link stayed
clean: USLP_RX sat at 800 bytes of its 8 KB, and the first corrupted frame
with a full receive buffer behind it was fatal.

Emit the output in fixed 128 byte chunks instead, flushing as it is built.
The rendered text is unchanged - verified byte for byte against the previous
implementation for sizes 0 to 4096 and several line widths, including the
line break boundaries.

printBin was already safe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4eBFnanCACYWdKCzhHMcC
2026-09-16 12:02:57 +02:00
Tobias BaumgartlandClaude Opus 5 e256fa4b92 cfdp: parse and act on a Cancel EOF at the destination
handleEofPdu built its EofInfo with a null fault location TLV pointer.
EofPduReader refuses to parse any EOF whose condition code is not NO_ERROR
unless it has somewhere to put that TLV, so every Cancel EOF a sender emits
was rejected with "Ca not deserialize fault location" and dropped before the
handler saw it.

The consequences were invisible from the ground until now: the sender's
cancellation was never acknowledged, so it retransmitted the EOF to its
positive ACK limit and declared a fault, while this handler kept the
transaction open until its own check limit expired. A flatsat uplink hit
exactly that, twice ten seconds apart, which is the sender's retransmission
interval.

Give both EOF parse sites a real EntityIdTlv, held by the handler so no
allocation happens per PDU, and adopt a non-NO_ERROR condition code into the
transaction. Without the second part the cancellation would parse but then
fall through to transfer completion, which would run a checksum pass over a
file the sender has already abandoned and report a checksum failure instead of
the cancellation.

The new test fails without the fix in the same way the flatsat did: no ACK is
emitted at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4eBFnanCACYWdKCzhHMcC
2026-09-16 11:54:34 +02:00
Tobias BaumgartlandClaude Opus 5 9d609b6d2d cfdp: exclude the PDU CRC from variable length PDU tails
FileDataReader takes the two CRC bytes out of the parsed range before it
reads its payload. The three readers with a variable length tail did not,
and each of them walks that tail until the range is exhausted:
MetadataPduReader over its option TLVs, NakPduReader over its segment
requests, FinishPduReader over its filestore and fault location TLVs.

With crcOnTransmission set at the sender, the CRC is therefore parsed as
one more TLV or segment request and the PDU is rejected, metadata and
Finished with INVALID_TLV_TYPE. Reception of CRC bearing PDUs only ever
worked for the PDUs which have no tail at all, which is why it went
unnoticed: a plain file uplink's metadata carries no options. A proxy put
request always carries one, and in acknowledged mode so do the NAK and
Finished PDUs a ground source sends, so this broke the OBSW as a
destination for any request carrying a message to user, and as a source
for every acknowledged downlink.

MetadataPduReader had a partial guard for this - an early return when the
CRC was the only thing left - which covered the no-options case and hid
the defect for the case that has them. It is replaced by the same up front
subtraction the other two now do.

The tests build CRC bearing PDUs by hand through the new PduCrcHelper,
because the creators cannot produce one: they append the CRC without
counting it in the directive data field length, which is a separate defect
left alone here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4eBFnanCACYWdKCzhHMcC
2026-09-16 10:38:08 +02:00
Tobias BaumgartlandClaude Opus 5 892fdff164 cfdp: apply clang-format to the class 2 handler changes
origin/main is clang-format clean, this branch was not: the class 2 work left
six violations across four files. Purely mechanical reflowing, no behaviour
change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RZpKfWUzvTkBMXEv9NEoTM
2026-09-15 01:08:30 +02:00
Tobias BaumgartlandClaude Opus 5 30965ed058 cfdp: fix acknowledged mode error handling and PDU transaction filtering
Review of the class 2 implementation turned up seven defects, all in the paths
that only run when something has already gone wrong on the link.

Send failures were still being treated as successful sends in two places the
earlier fix missed. SourceHandler::servicePendingRetransmissions() advanced its
cursor past a segment whose sendFileDataPdu() failed and cleared
metadataPending before the metadata PDU had gone out, so a NAK answered while
the TM store was full dropped exactly the data the peer asked for. Both now
only advance on success, matching the forward-only path.

A NAK carrying more segment requests than the reader's array can hold was a
complete no-op rather than a partial one: NakPduReader::parseData() returns
NAK_CANT_PARSE_OPTIONS without ever calling setSegmentRequestLen(), so the
length stayed at the 0 set before the loop and handleNakPdu() acted on nothing.
Every early return in that loop now reports the number of complete requests
parsed. handleNakPdu() also resets its retransmit state before parsing, because
the reader writes straight into the segment array and a hard parse error used
to leave the previous NAK's indices pointing into a half overwritten one.

handleFinishedPdu() marked the transaction finished and copied the delivery
result before checking whether the PDU had parsed at all, so a Finished PDU
truncated before its condition code byte completed the transfer and reported
the default constructed DATA_COMPLETE - a fabricated success. Only a failure
inside the optional TLVs is tolerated now.

Neither handler checked which transaction an incoming ACK or Finished PDU
belonged to, and CfdpHandler routes on direction and directive alone. A late
ACK from a previous transaction therefore drove whichever one was running now,
up to and including finishing it. Both handlers now compare the PDU's source
entity ID and sequence number against the running transaction, by value rather
than with operator==, which also compares the encoded width.

The Finished PDU send is now retried on failure instead of being assumed sent,
in both transmission modes, bounded by maxFinishedPduSendAttempts. Class 1 used
to finish() regardless, so the peer never heard that a transfer which actually
succeeded had completed and had no way to ask again. Bounding it is the point:
a busy destination handler discards incoming metadata PDUs, so retrying
forever would mean no later uplink could start. Exhausting the budget releases
the transaction without declaring a fault - the file is complete on disk and
the local user already got its indication, only the notification is lost.

Finally, DestHandler's NAK segment scratch buffer is sized to hold at least one
request, so a maxSegmentRequestsPerNakPdu of 0 is a clamped configuration
rather than an out of bounds write.

Eleven new test sections cover all of it. PduSenderMock gains failSendAtIdx and
failSendsFromIdx alongside failNextSend, for the cases where the call under
test emits several PDUs or where the downstream stays broken.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RZpKfWUzvTkBMXEv9NEoTM
2026-09-15 00:46:37 +02:00
Tobias BaumgartlandClaude Sonnet 5 fa7ecca728 cfdp: stop swallowing PduSenderIF::sendPdu() failures
SourceHandler::sendGenericPdu() and DestHandler::sendFinishedPdu() discarded
sendPdu()'s return value, so a downstream send failure (e.g. a full TM store)
was invisible to the state machine: transactionParams.progress advanced past
file data that was never actually enqueued for downlink, and a retransmit hit
the same swallowed-error path. Both now propagate the result so a failed send
retries the same segment instead of being silently treated as sent.

Adds a failure-injection knob to PduSenderMock and a SourceHandler test
covering the retry.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RZpKfWUzvTkBMXEv9NEoTM
2026-09-14 23:27:09 +02:00
Tobias Baumgartl 0224523cc5 cfdp: implement acknowledged mode (class 2) in both handlers
Class 1 has no retransmission at all: on a lossy uplink a single lost PDU
corrupts a transfer and a lost metadata PDU strands it completely. The PDU
layer for class 2 was already complete and unit tested, but neither handler
implemented the procedures on top of it - the destination handler had a
warning stub for BUSY_CLASS_2_ACKED and a SENDING_ACK_PDU step no branch
serviced, and the source handler discarded every incoming PDU and parked
permanently in BUSY_CLASS_2_ACKED.

RemoteEntityCfg gains the positive ACK, NAK and check timer parameters. The
names mirror cfdppy.mib.RemoteEntityConfig field for field so both ends of a
link can be configured from the same numbers. The defaults are inert for
class 1.

Destination handler:
- tracks received segments in the lostSegmentsContainer that was already
  plumbed through but never read, so completion is "no gaps and EOF seen"
  rather than the class 1 "progress reached the file size"
- emits the ACK for the EOF PDU before the transfer completion step, because
  the checksum pass reads the whole file back from the SD card and would
  otherwise run inside the sender's positive ACK timer
- runs the deferred lost segment procedure: one NAK sequence when the EOF
  arrives, re-issued on NAK timer expiry, NAK_LIMIT_REACHED on the limit.
  Segment requests are batched per PDU and the remainder carried over
- retains the transaction until its Finished PDU is acknowledged, retransmits
  it on positive ACK timeout, POSITIVE_ACK_LIMIT_REACHED on the limit
- can start a transaction from a file data PDU when the metadata was lost and
  request the metadata with a NAK of scope 0 to 0
- acknowledges an EOF PDU for an inactive transaction, otherwise the sender
  declares a fault at the end of an otherwise successful transfer
- runs the check timer after EOF so an incomplete file is cancelled instead
  of pinning the handler forever

Source handler:
- consumes incoming PDUs instead of dropping them on the floor
- waits for the ACK of its EOF PDU and retransmits on timeout
- answers NAK PDUs by retransmitting the requested segments, one PDU per
  state machine call, and the metadata PDU for a scope 0 to 0 request. The
  read and send path is split from the forward-only progress cursor for this
- implements WAIT_FOR_FINISH properly: parses the Finished PDU, acknowledges
  it and reports the received condition and delivery codes instead of a
  hardcoded NO_ERROR / DATA_COMPLETE. This also fixes class 1 with closure,
  which reported success for a transfer the receiver had rejected. The wait
  is bounded so a lost Finished PDU cannot pin the handler

AckPduCreator and NakPduCreator get the `using FileDirectiveCreator::serialize`
that FinishedPduCreator already had, so the convenience overloads are usable.

crcOnTransmission stays unusable and unused: the CRC sizing bug in the PDU
creators is a separate, self-contained fix.
2026-09-11 16:06:53 +02:00
tbaumgartl 42ecc7caf1 Merge pull request 'fix MessageQueueBase ignore fault' (#72) from baumgartl/fix-mqb-ignore-fault into main
Reviewed-on: #72
Reviewed-by: Robin Müller <muellerr@irs.uni-stuttgart.de>
2026-09-06 17:53:14 +02:00
muellerr 17926777a1 Merge pull request 'fix: ensure proper mutex unlocking in destructor to prevent system halts' (#71) from baumgartl/fix-mutexguard into main
Reviewed-on: #71
Reviewed-by: Robin Müller <muellerr@irs.uni-stuttgart.de>
2026-09-05 10:33:50 +02:00
Tobias Baumgartl 5761c1e187 fix: allow configurable fault handling in sendMessage function 2026-09-05 07:22:32 +02:00
Tobias Baumgartl 427f5a99b9 fix: ensure proper mutex unlocking in destructor to prevent system halts 2026-09-05 06:49:20 +02:00
tbaumgartl b1d2a4726f Merge pull request 'Add COBS encoding support (encoding and decoding)' (#70) from blochm/fsfw:bloch/cobs into main
Reviewed-on: #70
2026-08-27 07:24:55 +02:00
tbaumgartl 4a47eced59 Merge pull request 'Exclude host sources from non-host targets' (#69) from blochm/fsfw:bloch/smol-fix into main
Reviewed-on: #69
2026-08-27 07:18:55 +02:00
blochm b123b3f260 feat: cobs 2026-08-15 12:02:57 +02:00
blochm 428ff3f373 fix: kick out host stuff from device build 2026-08-15 10:11:34 +02:00
muellerr 9890a2c52e Merge pull request 'Better printer task & Bug fix' (#68) from blochm/fsfw:bloch/improve-printout into main
Reviewed-on: #68
Reviewed-by: Robin Müller <muellerr@irs.uni-stuttgart.de>
2026-08-06 10:40:08 +02:00
blochm b65854eaa0 fix(DeviceHandlerBase): SerialBufferAdapter was missing <uint32_t>
Previously it simply defaulted to size_t because of the length parameter
type. This is bad, since network serialization is then platform
dependant
2026-07-14 19:20:14 +02:00
blochm 23500c8364 feat(ServiceInterfacePrinter): ringbuffer printer
Previously the printer used a 2d array mechanism to queue up messages,
by selecting a free slot with help of a etl::bitset for tracking. A
message is then sent to the callback what slot is filled up and the
callback then drains the entire queue and prints out the messages.

This was prone to deadline issues, since the entire queue was always
flushed and often I noticed a deadline missed messages when developing
other stuff. Also memory is inefficiently used with the 2d array.

This new version fixes the above using a ring buffer datastructure. We
use a flat array and 2 integer pointers for storing and tracking the
bytes to print. So now there isn't any wasted space between messages.
Also with the design of the ring buffer messages can be written in and
read out at the same time, so we have minimal mutex use (just for
updating the integer pointers). To address the deadline issue, the
callback also only prints out a limited number of bytes per cycle.

(Also the printer code in general has been optimized a bit, since it was
quite needlessly big)
2026-07-14 19:20:14 +02:00
muellerr 91c5b05723 Merge pull request 'add keep alive PDU serializer' (#67) from add-keep-alive-pdu-serializer into main
Reviewed-on: #67
2026-04-14 10:10:45 +02:00
Robin Mueller a8bcb9c8cd add keep alive PDU serializer 2026-04-14 10:09:16 +02:00
muellerr 1d278d6f5c Merge pull request 'Fix stray import' (#66) from ritzmannc/fsfw:ritzmann/fix-stray-import into main
Reviewed-on: #66
2026-03-04 10:10:27 +01:00
ritzmannc b1bc699009 Fix stray import 2026-03-03 20:31:55 +01:00
muellerr 3668e61d5c Merge pull request 'Asynchronous ServiceInterfacePrinter' (#65) from ritzmannc/fsfw:ritzmann/sif-async-print into main
Reviewed-on: #65
2026-03-03 19:28:49 +01:00
ritzmannc 45150c8ce3 Fix of by one errors and set the position after the last char to a null byte. 2026-02-19 00:34:34 +01:00
ritzmannc 7692e598d6 Add FSFW_PRINT_BUFFER_AMOUNT to FSFW template config 2026-02-17 16:40:24 +01:00
ritzmannc 52129e0c84 Remove legacy code 2026-02-13 16:35:11 +01:00
ritzmannc acf60e55e8 Fix Host TaskFactory::printMissedDeadline warning 2026-01-23 12:49:10 +01:00
ritzmannc a625a06b7d Add async printing functionality 2026-01-23 12:48:09 +01:00
muellerr c0a665ffe6 Merge pull request 'PUS: Implement serialization for TC[8, 128] (Direct Command)' (#62) from bertschs/fsfw:bertsch/packet-apis into main
Reviewed-on: #62
Reviewed-by: Robin Müller <muellerr@irs.uni-stuttgart.de>
2026-01-12 09:40:12 +01:00
muellerr cceef62cb6 Merge pull request 'Expose health table mutex publically' (#64) from baumgartl/expose-healthtable-mutex into main
Reviewed-on: #64
2026-01-09 13:34:09 +01:00
Tobias Baumgartl 4c3c93c106 Expose health table mutex publically 2026-01-08 19:42:53 +01:00
tbaumgartl d28e2b5f07 Merge pull request 'Increasing the maximum number of allowed mode tables for subsystems' (#63) from spahr/maxNumberOfModeTables into main
Reviewed-on: #63
2026-01-04 20:28:20 +01:00
spahr@ksat-stuttgart.de 6ebe3123ff Increasing the maximum number of allowed mode tables
changelog
2026-01-04 20:27:11 +01:00
bertschs 70b9ba68bf PUS: Implement serialization for TC[8, 128] (Direct Command)
This allows creating and serializing direct
command PUS packets. This functionality is needed
in SOURCE, where OBC prepares TC[8, 128] packets
for Payload Computer (PLOC).

Additionally, expose some setters and
datastructures to facilitate this use case.
2025-11-26 22:13:02 +01:00
muellerr 59706365f6 Merge pull request 'typo' (#59) from mdemke/typo-fix into main
Reviewed-on: #59
Reviewed-by: Robin Müller <muellerr@irs.uni-stuttgart.de>
2025-11-06 16:25:07 +01:00
muellerr 0c70ff1822 Merge branch 'main' into mdemke/typo-fix 2025-11-06 16:24:52 +01:00
muellerr 76dd1d1562 Merge pull request 'PUS Routing Configuration' (#60) from meier/pus-routing into main
Reviewed-on: #60
Reviewed-by: Robin Müller <muellerr@irs.uni-stuttgart.de>
2025-11-06 16:24:32 +01:00
muellerr f2b72db481 Merge branch 'main' into meier/pus-routing 2025-11-06 16:24:25 +01:00
muellerr fa4af546fa Merge pull request 'Changing the function definition to a virtual function to allow overrides for some custom applications' (#61) from spahr/costumCommandTableExecution into main
Reviewed-on: #61
Reviewed-by: Robin Müller <muellerr@irs.uni-stuttgart.de>
2025-11-06 16:24:13 +01:00
spahr@ksat-stuttgart.de 5745d7f01c Changing the function definition to a virtual to allow overrides for custom applications 2025-10-22 23:23:26 +02:00
Jakob Meier d7c1d05599 changelog update 2025-08-03 16:36:19 +02:00
Jakob Meier 86b83810c3 run auto formatter 2025-08-03 16:29:53 +02:00
Jakob Meier d0904fdaa2 added function to set verification reporter of CommandingServiceBase 2025-08-01 08:57:08 +02:00
Jakob Meier f824c066d1 PusServiceBase public functions to change the verifcation reporter and the pus distributor 2025-07-31 16:41:14 +02:00
Jakob Meier d000365b99 PusDistributor public function to change the verifcation reporter 2025-07-31 16:40:34 +02:00
Michael Demke d99f6fd356 typo 2025-06-25 15:22:18 +02:00
muellerr 49eaeae42b Merge pull request 'Adaptions to make shared power lines possible' (#57) from spahr/shared into main
Reviewed-on: #57
Reviewed-by: Robin Müller <muellerr@irs.uni-stuttgart.de>
2025-04-28 13:50:31 +02:00
muellerr 7bfc536cf6 Merge branch 'main' into spahr/shared 2025-04-28 13:50:23 +02:00
phoffmann 1da7f7f122 Merge pull request 'Added STOP_DOWNLINK_STORE_CONTENT for Service [15,17]' (#58) from hoffmann/TmStoreMessage into main
Reviewed-on: #58
2025-04-21 19:21:40 +02:00
Philipp Hoffmann aa443e6aa6 Added STOP_DOWNLINK_STORE_CONTENT for Service [15,17] 2025-04-21 17:05:58 +02:00
spahr@ksat-stuttgart.de b13b5b456d Give AssemblyBase more functionality: Support one-by-one commanding for childrend instead of sending all mode messages on one shot 2025-04-14 00:06:34 +02:00
spahr@ksat-stuttgart.de 297ec261ce make the recovery timeout accessable to the user 2025-04-04 10:11:11 +02:00
spahr@ksat-stuttgart.de 95520d7d0c Check if objectId exists in childrednmap first; this will prevent a hardfault 2025-04-02 22:18:31 +02:00
spahr@ksat-stuttgart.de b665b2effe add an adaption point which a user can use to convert a objectId of a shared power switch into a objectId of a device handler 2025-04-02 22:13:50 +02:00
muellerr 7ae58f8125 Merge pull request 'Send HK One Parameter Report back to Sender' (#56) from meier/hk-report-reply-queue into main
Reviewed-on: #56
Reviewed-by: Robin Müller <muellerr@irs.uni-stuttgart.de>
2025-04-02 14:04:46 +02:00
muellerr 7784a26a10 Merge branch 'main' into meier/hk-report-reply-queue 2025-04-02 14:04:37 +02:00
Jakob Meier 3afd0c8d3c updated changelog 2025-04-01 17:25:02 +02:00
Jakob Meier 71623d5314 Merge commit 'f01e58a7' into meier/hk-report-reply-queue 2025-04-01 14:18:07 +02:00
muellerr daac5ea727 Merge pull request 'spahr/handleRecoveryEvents' (#54) from spahr/handleRecoveryEvents into main
Reviewed-on: #54
Reviewed-by: Robin Müller <muellerr@irs.uni-stuttgart.de>
2025-04-01 14:07:10 +02:00
muellerr 2c01b83b75 Merge branch 'main' into spahr/handleRecoveryEvents 2025-04-01 14:06:36 +02:00
muellerr f01e58a757 Merge pull request 'seems like this should set the serializables to .get().setValid(valid) instead of true' (#55) from mdemke/hotfix_hk__setChildrenValidity into main
Reviewed-on: #55
Reviewed-by: Robin Müller <muellerr@irs.uni-stuttgart.de>
2025-03-31 12:25:06 +02:00
Michael Demke 40be8ebef5 seems like this should set the serializables to .get().setValid(valid) instead of true 2025-03-28 00:41:40 +01:00
Jakob Meier 2af6e85f87 send hk report back to sender instead of default destination 2025-03-23 12:35:40 +01:00
spahr@ksat-stuttgart.de d8ac312e85 remove event because it's no longer needed. 2025-03-22 10:01:01 +01:00
spahr@ksat-stuttgart.de 1e12753533 add device object id to event 2025-03-22 09:49:44 +01:00
spahr@ksat-stuttgart.de b7699b327b add two new events for the recovery process, to make debug and output more clear. This also makes a recovery process more clear for OPS. 2025-03-22 09:48:30 +01:00
spahr@ksat-stuttgart.de 9945f72eaf improve documentation for event 2025-03-22 09:40:31 +01:00
muellerr 55b8d01b93 Merge pull request 'Compile time const event definitions and compile error for unique IDs above limit' (#53) from baumgartl/events into main
Reviewed-on: #53
Reviewed-by: Robin Müller <muellerr@irs.uni-stuttgart.de>
2025-03-18 14:39:34 +01:00
tbaumgartl 8cb1d84c58 fixed event definition for archive/mgm and pus 11 2025-03-12 22:18:55 +01:00
tbaumgartl 8801dfa31d implemented event limit. TODO: adjust generator parsing and usage in src-obsw 2025-03-12 21:46:48 +01:00
muellerr b8979d8f90 Merge pull request 'Improve readability of SubsystemBase::executeTable' (#49) from spahr/SubsystemBase into main
Reviewed-on: #49
2025-02-19 11:26:36 +01:00
muellerr 9557db7036 Merge branch 'main' into spahr/SubsystemBase 2025-02-19 11:26:26 +01:00
muellerr c9fcabccd6 Merge pull request 'Missing valid flag update in readWithoutLock and commitWithoutLock' (#48) from bugfix-poolvariable into main
Reviewed-on: #48
2025-02-19 11:26:04 +01:00
muellerr ecd36f5e52 Merge branch 'main' into bugfix-poolvariable 2025-02-19 11:25:52 +01:00
muellerr fc19c0838e Merge pull request 'Formatting of debug printout' (#50) from spahr/formattingForDebugPrintout into main
Reviewed-on: #50
2025-02-19 11:24:39 +01:00
spahr@ksat-stuttgart.de 8c3f366d1a formatting for time prinout 2025-02-16 17:36:22 +01:00
spahr@ksat-stuttgart.de 6efb2641a7 Re-write the logic in executeTable() to improve the readabilty of the code. This gets rid of the usage of isFaulty() (which is misleading), and removes nexted if-statements 2025-02-16 17:07:40 +01:00
spahr@ksat-stuttgart.de 260bbad9a0 Modify the hard-coded strings which are printed to into the debug session to enforce alignment of all printouts, regardless of their printlevel (info, debug, warning, error). 2025-02-14 19:02:57 +01:00
muellerr 9edd6221f8 Missing valid flag update in readWithoutLock and commitWithoutLock 2025-02-11 15:16:27 +01:00
muellerr 735e341aab Merge pull request 'temperaturesensor-bugfix' (#47) from temperaturesensor-bugfix into main
Reviewed-on: #47
2025-01-21 14:33:29 +01:00
muellerr 921bfb1e99 Merge branch 'main' into temperaturesensor-bugfix 2025-01-21 14:31:59 +01:00
muellerr 5b1651e1a6 Merge pull request 'Update HK and datapool handling' (#45) from update-hk-handling-datapools into main
Reviewed-on: #45
2025-01-08 10:38:34 +01:00
muellerr e916b9b096 fix for CMakeLists.txt 2025-01-07 10:50:37 +01:00
muellerr b14e761bad small typo 2025-01-07 10:50:37 +01:00
muellerr 33f3ae2434 Update and clean up HK and Local Pool Modules 2025-01-07 10:50:37 +01:00
muellerr f0087d5b0d Merge pull request 'FreeRTOS Monotonic Clock' (#46) from meier/freertos-monotonic-clock into main
Reviewed-on: #46
Reviewed-by: Robin Müller <muellerr@irs.uni-stuttgart.de>
2025-01-07 10:33:29 +01:00
mikael.senger 69c33587e8 Merge remote-tracking branch 'origin/main' into temperaturesensor-bugfix 2024-12-31 10:51:11 +01:00
Jakob Meier d1bf04cc29 Merge branch 'main' into meier/freertos-monotonic-clock 2024-12-25 10:20:22 +01:00
Jakob Meier 8e3bc1b8aa updated changelog 2024-12-23 12:07:54 +01:00
Jakob Meier 64f97fc3ba implemented freertos monotonic clock 2024-12-23 12:02:29 +01:00
Jakob Meier 1427fbd2fe added monotonic clock which is independend of clock jumps in the system clock 2024-12-22 18:37:44 +01:00
mikael.senger 81cd8bd290 did not build before 2024-12-15 23:52:32 +01:00
274 changed files with 7299 additions and 5214 deletions

No files matched your search

+19 -1
View File
@@ -26,6 +26,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## Added
- functions to configure pus routing
- FreeRTOS monotonic clock which is not subjected to time jumps of the system clock
- add CFDP subsystem ID
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/742
- `PusTmZcWriter` now exposes API to set message counter field.
@@ -34,6 +36,21 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## 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
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.
It takes care of tracking the HK generation using a set specification provided by the user.n
However, it leaves serialization of the HK data completely to the developer. This removes a major
constraint on the format of the HK data, which was previously constrained to implementors of a
certain base class.
- The former set classes and pool objects are still available for HK set specification and
generation. The API has changed, but the general usage and their architecture has not.
- A new set of set classes and helper objects to specify HK sets and data which does not need to be
shared was added as well. The majority of datasets do not need to be shared anyway.
- The non-shared API retain the capability of appending of a validity blob for each piece of set
data at the end of the HK data. For both non-shared and shared data, this capability can be
specified in the constructor, and defaults to true.
- Improved File System Abstraction to be more in line with normal filesystems.
- CFDP implementation was improved, has now even less dependencies on other FSFW components
and allows one inserted packet per state machine call.
@@ -53,7 +70,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
configurable.
- Switched to vendored versions for both the Embedded Template Library (ETL) and the
Catch2 unittesting library.
- Increased maximum number of mode tables from 70 to 100
- Exposed health table mutex via getter function
## Added
- `EventManager`: Add function to print all listeners.
@@ -8,7 +8,8 @@ GyroHandlerL3GD20H::GyroHandlerL3GD20H(object_id_t objectId, object_id_t deviceC
CookieIF *comCookie, uint32_t transitionDelayMs)
: DeviceHandlerBase(objectId, deviceCommunication, comCookie),
transitionDelayMs(transitionDelayMs),
dataset(this) {}
sharedPool(DeviceHandlerBase::getObjectId()),
dataset(sharedPool) {}
GyroHandlerL3GD20H::~GyroHandlerL3GD20H() {}
@@ -210,27 +211,27 @@ ReturnValue_t GyroHandlerL3GD20H::interpretDeviceReply(DeviceCommandId_t id,
if (readSet.getReadResult() == returnvalue::OK) {
if (std::abs(angVelocX) < this->absLimitX) {
dataset.angVelocX = angVelocX;
dataset.angVelocX.setValid(true);
// dataset.angVelocX.setValid(true);
} else {
dataset.angVelocX.setValid(false);
// dataset.angVelocX.setValid(false);
}
if (std::abs(angVelocY) < this->absLimitY) {
dataset.angVelocY = angVelocY;
dataset.angVelocY.setValid(true);
// dataset.angVelocY.setValid(true);
} else {
dataset.angVelocY.setValid(false);
// dataset.angVelocY.setValid(false);
}
if (std::abs(angVelocZ) < this->absLimitZ) {
dataset.angVelocZ = angVelocZ;
dataset.angVelocZ.setValid(true);
// dataset.angVelocZ.setValid(true);
} else {
dataset.angVelocZ.setValid(false);
// dataset.angVelocZ.setValid(false);
}
dataset.temperature = temperature;
dataset.temperature.setValid(true);
// dataset.temperature.setValid(true);
}
break;
}
@@ -246,16 +247,19 @@ uint32_t GyroHandlerL3GD20H::getTransitionDelayMs(Mode_t from, Mode_t to) {
void GyroHandlerL3GD20H::setToGoToNormalMode(bool enable) { this->goNormalModeImmediately = true; }
// TODO
/*
ReturnValue_t GyroHandlerL3GD20H::initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
LocalDataPoolManager &poolManager) {
PeriodicHkGenerationHelper &hkGenHelper) {
localDataPoolMap.emplace(l3gd20h::ANG_VELOC_X, new PoolEntry<float>({0.0}));
localDataPoolMap.emplace(l3gd20h::ANG_VELOC_Y, new PoolEntry<float>({0.0}));
localDataPoolMap.emplace(l3gd20h::ANG_VELOC_Z, new PoolEntry<float>({0.0}));
localDataPoolMap.emplace(l3gd20h::TEMPERATURE, new PoolEntry<float>({0.0}));
poolManager.subscribeForRegularPeriodicPacket(
hkGenHelper.enableRegularPeriodicPacket(
subdp::RegularHkPeriodicParams(dataset.getSid(), false, 10.0));
return returnvalue::OK;
}
*/
void GyroHandlerL3GD20H::fillCommandAndReplyMap() {
insertInCommandAndReplyMap(l3gd20h::READ_REGS, 1, &dataset);
@@ -1,9 +1,9 @@
#ifndef MISSION_DEVICES_GYROL3GD20HANDLER_H_
#define MISSION_DEVICES_GYROL3GD20HANDLER_H_
#include <fsfw/devicehandlers/DeviceHandlerBase.h>
#include <fsfw/globalfunctions/PeriodicOperationDivider.h>
#include <fsfw_hal/devicehandlers/devicedefinitions/gyroL3gHelpers.h>
#include "fsfw/devicehandlers/DeviceHandlerBase.h"
#include "fsfw/globalfunctions/PeriodicOperationDivider.h"
#include "gyroL3gHelpers.h"
/**
* @brief Device Handler for the L3GD20H gyroscope sensor
@@ -51,11 +51,12 @@ class GyroHandlerL3GD20H : public DeviceHandlerBase {
void fillCommandAndReplyMap() override;
void modeChanged() override;
virtual uint32_t getTransitionDelayMs(Mode_t from, Mode_t to) override;
ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
LocalDataPoolManager &poolManager) override;
// ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
// PeriodicHkGenerationHelper &hkGenHelper) override;
private:
uint32_t transitionDelayMs = 0;
localpool::SharedPool sharedPool;
GyroPrimaryDataset dataset;
float absLimitX = l3gd20h::RANGE_DPS_00;
@@ -0,0 +1,13 @@
#include "HasLocalDpIFManagerAttorney.h"
#include "fsfw/datapool/LocalPoolObjectBase.h"
#include "fsfw/housekeeping/GeneratesPeriodicHkIF.h"
LocalPoolObjectBase* HasLocalDpIFManagerAttorney::getPoolObjectHandle(
PeriodicHkGenerationIF* clientIF, dp::lp_id_t localPoolId) {
return clientIF->getPoolObjectHandle(localPoolId);
}
object_id_t HasLocalDpIFManagerAttorney::getObjectId(PeriodicHkGenerationIF* clientIF) {
return clientIF->getObjectId();
}
@@ -0,0 +1,17 @@
#pragma once
#include <fsfw/housekeeping/PeriodicHkHelper.h>
#include "fsfw/datapool/definitions.h"
class PeriodicHkGenerationIF;
class LocalPoolDataSetBase;
class LocalPoolObjectBase;
class HasLocalDpIFManagerAttorney {
static LocalPoolObjectBase* getPoolObjectHandle(PeriodicHkGenerationIF* clientIF,
dp::id_t localPoolId);
static object_id_t getObjectId(PeriodicHkGenerationIF* clientIF);
friend class hk::PeriodicHelper;
};
@@ -0,0 +1 @@
#include "HasLocalDpIFUserAttorney.h"
@@ -1,12 +1,12 @@
#ifndef FSFW_DATAPOOLLOCAL_HASLOCALDPIFUSERATTORNEY_H_
#define FSFW_DATAPOOLLOCAL_HASLOCALDPIFUSERATTORNEY_H_
class HasLocalDataPoolIF;
class PeriodicHkGenerationIF;
class AccessPoolManagerIF;
class HasLocalDpIFUserAttorney {
private:
static AccessPoolManagerIF* getAccessorHandle(HasLocalDataPoolIF* clientIF);
// static AccessPoolManagerIF* getAccessorHandle(PeriodicHkGenerationIF* clientIF);
friend class LocalPoolObjectBase;
friend class LocalPoolDataSetBase;
@@ -273,7 +273,7 @@ ReturnValue_t MgmLIS3MDLHandler::interpretDeviceReply(DeviceCommandId_t id, cons
if (readHelper.getReadResult() == returnvalue::OK) {
if (std::abs(mgmX) > absLimitX or std::abs(mgmY) > absLimitY or
std::abs(mgmZ) > absLimitZ) {
dataset.fieldStrengths.setValid(false);
dataset.setIsValid = false;
}
if (std::abs(mgmX) < absLimitX) {
dataset.fieldStrengths[0] = mgmX;
@@ -286,7 +286,7 @@ ReturnValue_t MgmLIS3MDLHandler::interpretDeviceReply(DeviceCommandId_t id, cons
if (std::abs(mgmZ) < absLimitZ) {
dataset.fieldStrengths[2] = mgmZ;
}
dataset.fieldStrengths.setValid(true);
dataset.setIsValid = true;
}
break;
}
@@ -415,13 +415,15 @@ uint32_t MgmLIS3MDLHandler::getTransitionDelayMs(Mode_t from, Mode_t to) { retur
void MgmLIS3MDLHandler::modeChanged(void) { internalState = InternalState::STATE_NONE; }
/*
ReturnValue_t MgmLIS3MDLHandler::initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
LocalDataPoolManager &poolManager) {
PeriodicHkGenerationHelper &poolManager) {
localDataPoolMap.emplace(mgmLis3::FIELD_STRENGTHS, &mgmXYZ);
localDataPoolMap.emplace(mgmLis3::TEMPERATURE_CELCIUS, &temperature);
poolManager.subscribeForRegularPeriodicPacket({dataset.getSid(), false, 10.0});
poolManager.setPeriodicFrequency(dataset.getSid(), 10'000);
return returnvalue::OK;
}
*/
void MgmLIS3MDLHandler::setAbsoluteLimits(float xLimit, float yLimit, float zLimit) {
this->absLimitX = xLimit;
@@ -1,7 +1,7 @@
#ifndef MISSION_DEVICES_MGMLIS3MDLHANDLER_H_
#define MISSION_DEVICES_MGMLIS3MDLHANDLER_H_
#include <fsfw_hal/devicehandlers/devicedefinitions/mgmLis3Helpers.h>
#include "mgmLis3Helpers.h"
#include "fsfw/devicehandlers/DeviceHandlerBase.h"
#include "fsfw/globalfunctions/PeriodicOperationDivider.h"
@@ -63,8 +63,8 @@ class MgmLIS3MDLHandler : public DeviceHandlerBase {
virtual ReturnValue_t interpretDeviceReply(DeviceCommandId_t id, const uint8_t *packet) override;
void fillCommandAndReplyMap() override;
void modeChanged(void) override;
ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
LocalDataPoolManager &poolManager) override;
// ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
// PeriodicHkGenerationHelper &poolManager) override;
private:
mgmLis3::MgmPrimaryDataset dataset;
@@ -9,7 +9,7 @@
MgmRM3100Handler::MgmRM3100Handler(object_id_t objectId, object_id_t deviceCommunication,
CookieIF *comCookie, uint32_t transitionDelay)
: DeviceHandlerBase(objectId, deviceCommunication, comCookie),
primaryDataset(this),
primaryDataset(sharedPool),
transitionDelay(transitionDelay) {}
MgmRM3100Handler::~MgmRM3100Handler() {}
@@ -307,12 +307,15 @@ void MgmRM3100Handler::fillCommandAndReplyMap() {
void MgmRM3100Handler::modeChanged() { internalState = InternalState::NONE; }
// TODO: Fix
/*
ReturnValue_t MgmRM3100Handler::initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
LocalDataPoolManager &poolManager) {
PeriodicHkGenerationHelper &poolManager) {
localDataPoolMap.emplace(mgmRm3100::FIELD_STRENGTHS, &mgmXYZ);
poolManager.subscribeForRegularPeriodicPacket({primaryDataset.getSid(), false, 10.0});
poolManager.setPeriodicFrequency(primaryDataset.getSid(), 10'000);
return returnvalue::OK;
}
*/
uint32_t MgmRM3100Handler::getTransitionDelayMs(Mode_t from, Mode_t to) {
return this->transitionDelay;
@@ -356,7 +359,7 @@ ReturnValue_t MgmRM3100Handler::handleDataReadout(const uint8_t *packet) {
primaryDataset.fieldStrengths[0] = fieldStrengthX;
primaryDataset.fieldStrengths[1] = fieldStrengthY;
primaryDataset.fieldStrengths[2] = fieldStrengthZ;
primaryDataset.setValidity(true, true);
primaryDataset.valid = true;
}
return returnvalue::OK;
}
@@ -1,7 +1,7 @@
#ifndef MISSION_DEVICES_MGMRM3100HANDLER_H_
#define MISSION_DEVICES_MGMRM3100HANDLER_H_
#include <fsfw_hal/devicehandlers/devicedefinitions/mgmRm3100Helpers.h>
#include "mgmRm3100Helpers.h"
#include "fsfw/devicehandlers/DeviceHandlerBase.h"
#include "fsfw/globalfunctions/PeriodicOperationDivider.h"
@@ -18,13 +18,13 @@ class MgmRM3100Handler : public DeviceHandlerBase {
static const uint8_t INTERFACE_ID = CLASS_ID::MGM_RM3100;
//! [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
//! P1: Second two bytes new Cycle Count Y
//! P2: New cycle count Z
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,
uint32_t transitionDelay);
@@ -52,8 +52,8 @@ class MgmRM3100Handler : public DeviceHandlerBase {
void fillCommandAndReplyMap() override;
void modeChanged(void) override;
virtual uint32_t getTransitionDelayMs(Mode_t from, Mode_t to) override;
ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
LocalDataPoolManager &poolManager) override;
// ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
// PeriodicHkGenerationHelper &poolManager) override;
private:
enum class InternalState {
@@ -0,0 +1,46 @@
#ifndef FSFW_DATAPOOLLOCAL_PROVIDESDATAPOOLSUBSCRIPTION_H_
#define FSFW_DATAPOOLLOCAL_PROVIDESDATAPOOLSUBSCRIPTION_H_
#include <fsfw/timemanager/clockDefinitions.h>
#include <optional>
#include "fsfw/housekeeping/AcceptsHkPacketsIF.h"
#include "fsfw/ipc/MessageQueueIF.h"
#include "fsfw/ipc/messageQueueDefinitions.h"
#include "fsfw/returnvalues/returnvalue.h"
#include "localPoolDefinitions.h"
namespace subdp {
struct ParamsBase {
ParamsBase(sid_t sid, bool enableReporting, dur_millis_t collectionIntervalMs)
: sid(sid), enableReporting(enableReporting), collectionIntervalMs(collectionIntervalMs) {}
[[nodiscard]] bool isDiagnostics() const { return diagnostics; }
sid_t sid;
bool enableReporting;
dur_millis_t collectionIntervalMs;
MessageQueueId_t receiver = MessageQueueIF::NO_QUEUE;
protected:
bool diagnostics;
};
struct RegularHkPeriodicParams : public ParamsBase {
RegularHkPeriodicParams(sid_t sid, bool enableReporting, dur_millis_t collectionIntervalMs)
: ParamsBase(sid, enableReporting, collectionIntervalMs) {}
};
struct RegularHkUpdateParams : public ParamsBase {
RegularHkUpdateParams(sid_t sid, bool enableReporting) : ParamsBase(sid, enableReporting, 0) {}
};
struct DiagnosticsHkUpdateParams : public ParamsBase {
DiagnosticsHkUpdateParams(sid_t sid, bool enableReporting)
: ParamsBase(sid, enableReporting, 0) {}
};
} // namespace subdp
#endif /* FSFW_DATAPOOLLOCAL_PROVIDESDATAPOOLSUBSCRIPTION_H_ */
@@ -1,21 +1,23 @@
#include "fsfw/datapoollocal/SharedLocalDataSet.h"
#include "fsfw/datapool/SharedLocalDataset.h"
SharedLocalDataSet::SharedLocalDataSet(object_id_t objectId, sid_t sid, const size_t maxSize)
: SystemObject(objectId), LocalPoolDataSetBase(sid, nullptr, maxSize), poolVarVector(maxSize) {
SharedLocalDataset::SharedLocalDataset(object_id_t objectId, structure_id_t sid, const size_t maxSize)
: SystemObject(objectId), SharedDatasetBase(sid, nullptr, maxSize), poolVarVector(maxSize) {
this->setContainer(poolVarVector.data());
datasetLock = MutexFactory::instance()->createMutex();
}
SharedLocalDataSet::SharedLocalDataSet(object_id_t objectId, HasLocalDataPoolIF *owner,
SharedLocalDataset::SharedLocalDataset(object_id_t objectId, localpool::SharedPool& sharedPool,
uint32_t setId, const size_t maxSize)
: SystemObject(objectId),
LocalPoolDataSetBase(owner, setId, nullptr, maxSize),
SharedDatasetBase(sharedPool, setId, nullptr, maxSize),
poolVarVector(maxSize) {
this->setContainer(poolVarVector.data());
datasetLock = MutexFactory::instance()->createMutex();
}
ReturnValue_t SharedLocalDataSet::lockDataset(MutexIF::TimeoutType timeoutType,
SharedLocalDataset::~SharedLocalDataset() { MutexFactory::instance()->deleteMutex(datasetLock); }
ReturnValue_t SharedLocalDataset::lockDataset(MutexIF::TimeoutType timeoutType,
dur_millis_t mutexTimeout) {
if (datasetLock != nullptr) {
return datasetLock->lockMutex(timeoutType, mutexTimeout);
@@ -23,9 +25,7 @@ ReturnValue_t SharedLocalDataSet::lockDataset(MutexIF::TimeoutType timeoutType,
return returnvalue::FAILED;
}
SharedLocalDataSet::~SharedLocalDataSet() { MutexFactory::instance()->deleteMutex(datasetLock); }
ReturnValue_t SharedLocalDataSet::unlockDataset() {
ReturnValue_t SharedLocalDataset::unlockDataset() {
if (datasetLock != nullptr) {
return datasetLock->unlockMutex();
}
+37
View File
@@ -0,0 +1,37 @@
#pragma once
#include <vector>
#include "fsfw/datapool/SharedDataSetIF.h"
#include "fsfw/objectmanager/SystemObject.h"
#include "SharedSetBase.h"
#include "SharedPool.h"
namespace datapool {
/**
* This local dataset variation can be used if the dataset is used concurrently across
* multiple threads. It provides a lock in addition to all other functionalities provided
* by the LocalPoolDataSetBase class.
*
* The user is completely responsible for locking and unlocking the dataset when using the
* shared dataset.
*/
class SharedLocalDataset : public SystemObject, public SharedSetBase, public SharedDataSetIF {
public:
SharedLocalDataset(object_id_t objectId, SharedPool& sharedPool, uint32_t setId,
size_t maxSize);
SharedLocalDataset(object_id_t objectId, sid_t sid, size_t maxSize);
~SharedLocalDataset() override;
ReturnValue_t lockDataset(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
dur_millis_t mutexTimeout = 20) override;
ReturnValue_t unlockDataset() override;
private:
MutexIF* datasetLock = nullptr;
std::vector<PoolVariableIF*> poolVarVector;
};
}
@@ -1,4 +1,4 @@
#include <fsfw_hal/devicehandlers/devicedefinitions/gyroL3gHelpers.h>
#include "gyroL3gHelpers.h"
float l3gd20h::ctrlReg4ToSensitivity(uint8_t reg) {
bool fsH = reg & l3gd20h::SET_FS_1;
@@ -1,8 +1,8 @@
#ifndef MISSION_DEVICES_DEVICEDEFINITIONS_GYROL3GD20DEFINITIONS_H_
#define MISSION_DEVICES_DEVICEDEFINITIONS_GYROL3GD20DEFINITIONS_H_
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
#include <fsfw/devicehandlers/DeviceHandlerIF.h>
#include "fsfw/datapoollocal/StaticLocalDataSet.h"
#include "fsfw/devicehandlers/DeviceHandlerIF.h"
#include <cstdint>
@@ -124,8 +124,8 @@ class GyroPrimaryDataset : public StaticLocalDataSet<5> {
setAllVariablesReadOnly();
}
/** Constructor for the data creator */
GyroPrimaryDataset(HasLocalDataPoolIF* hkOwner)
: StaticLocalDataSet(hkOwner, l3gd20h::GYRO_DATASET_ID) {}
GyroPrimaryDataset(localpool::SharedPool& sharedPool)
: StaticLocalDataSet(sharedPool, l3gd20h::GYRO_DATASET_ID) {}
/* Angular velocities in degrees per second (DPS) */
lp_var_t<float> angVelocX = lp_var_t<float>(sid.objectId, l3gd20h::ANG_VELOC_X, this);
@@ -1,9 +1,9 @@
#ifndef MISSION_DEVICES_DEVICEDEFINITIONS_MGMLIS3HANDLERDEFS_H_
#define MISSION_DEVICES_DEVICEDEFINITIONS_MGMLIS3HANDLERDEFS_H_
#include <fsfw/datapoollocal/LocalPoolVariable.h>
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
#include <fsfw/devicehandlers/DeviceHandlerIF.h>
#include "fsfw/datapoollocal/LocalPoolVariable.h"
#include "fsfw/datapoollocal/StaticLocalDataSet.h"
#include "fsfw/devicehandlers/DeviceHandlerIF.h"
#include <cstdint>
@@ -169,11 +169,12 @@ static const uint8_t CTRL_REG5_DEFAULT = 0;
static const uint32_t MGM_DATA_SET_ID = READ_CONFIG_AND_DATA;
enum MgmPoolIds : lp_id_t { FIELD_STRENGTHS, TEMPERATURE_CELCIUS };
enum MgmPoolIds : lp_id_t { FIELD_STRENGTHS = 0, TEMPERATURE_CELCIUS = 1, SET_IS_VALID = 2 };
class MgmPrimaryDataset : public StaticLocalDataSet<4> {
public:
MgmPrimaryDataset(HasLocalDataPoolIF* hkOwner) : StaticLocalDataSet(hkOwner, MGM_DATA_SET_ID) {}
MgmPrimaryDataset(localpool::SharedPool& sharedPool)
: StaticLocalDataSet(sharedPool, MGM_DATA_SET_ID) {}
MgmPrimaryDataset(object_id_t mgmId) : StaticLocalDataSet(sid_t(mgmId, MGM_DATA_SET_ID)) {}
@@ -182,6 +183,7 @@ class MgmPrimaryDataset : public StaticLocalDataSet<4> {
*/
lp_vec_t<float, 3> fieldStrengths = lp_vec_t<float, 3>(sid.objectId, FIELD_STRENGTHS, this);
lp_var_t<float> temperature = lp_var_t<float>(sid.objectId, TEMPERATURE_CELCIUS, this);
lp_var_t<uint8_t> setIsValid = lp_var_t<uint8_t>(sid.objectId, SET_IS_VALID, this);
};
} // namespace mgmLis3
@@ -1,13 +1,13 @@
#ifndef MISSION_DEVICES_DEVICEDEFINITIONS_MGMHANDLERRM3100DEFINITIONS_H_
#define MISSION_DEVICES_DEVICEDEFINITIONS_MGMHANDLERRM3100DEFINITIONS_H_
#include <fsfw/datapoollocal/LocalPoolVariable.h>
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
#include <fsfw/devicehandlers/DeviceHandlerIF.h>
#include <fsfw/serialize/SerialLinkedListAdapter.h>
#include <cstdint>
#include "fsfw/datapoollocal/LocalPoolVariable.h"
#include "fsfw/datapoollocal/StaticLocalDataSet.h"
#include "fsfw/devicehandlers/DeviceHandlerIF.h"
#include "fsfw/serialize/SerialLinkedListAdapter.h"
namespace mgmRm3100 {
/* Actually 10, we round up a little bit */
@@ -101,11 +101,12 @@ class CycleCountCommand : public SerialLinkedListAdapter<SerializeIF> {
static constexpr uint32_t MGM_DATASET_ID = READ_DATA;
enum MgmPoolIds : lp_id_t { FIELD_STRENGTHS };
enum MgmPoolIds : lp_id_t { FIELD_STRENGTHS = 0, VALID = 1 };
class Rm3100PrimaryDataset : public StaticLocalDataSet<3> {
public:
Rm3100PrimaryDataset(HasLocalDataPoolIF* hkOwner) : StaticLocalDataSet(hkOwner, MGM_DATASET_ID) {}
Rm3100PrimaryDataset(localpool::SharedPool& sharedPool)
: StaticLocalDataSet(sharedPool, MGM_DATASET_ID) {}
Rm3100PrimaryDataset(object_id_t mgmId) : StaticLocalDataSet(sid_t(mgmId, MGM_DATASET_ID)) {}
@@ -113,6 +114,7 @@ class Rm3100PrimaryDataset : public StaticLocalDataSet<3> {
* Field strenghts in uT
*/
lp_vec_t<float, 3> fieldStrengths = lp_vec_t<float, 3>(sid.objectId, FIELD_STRENGTHS, this);
lp_var_t<uint8_t> valid = lp_var_t<uint8_t>(sid.objectId, VALID, this);
};
} // namespace mgmRm3100
+1
View File
@@ -71,6 +71,7 @@ static constexpr size_t FSFW_EVENTMGMR_RANGEMATCHERS = 120;
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_AMOUNT = 32;
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"
fi
cpp_format="clang-format"
cpp_format="clang-format-19"
file_selectors="-iname *.h -o -iname *.cpp -o -iname *.c -o -iname *.tpp"
if command -v ${cpp_format} &> /dev/null; then
for dir in ${folder_list[@]}; do
echo "Auto-formatting ${dir} recursively"
find ${dir} ${file_selectors} | xargs clang-format --style=file -i
find ${dir} ${file_selectors} | xargs ${cpp_format} --style=file -i
done
else
echo "No ${cpp_format} tool found, not formatting C++/C files"
-1
View File
@@ -7,7 +7,6 @@ add_subdirectory(cfdp)
add_subdirectory(container)
add_subdirectory(controller)
add_subdirectory(datapool)
add_subdirectory(datapoollocal)
add_subdirectory(devicehandlers)
add_subdirectory(events)
add_subdirectory(fdir)
+596 -35
View File
@@ -2,14 +2,20 @@
#include <etl/crc32.h>
#include <algorithm>
#include <utility>
#include "fsfw/FSFW.h"
#include "fsfw/cfdp/pdu/AckPduCreator.h"
#include "fsfw/cfdp/pdu/AckPduReader.h"
#include "fsfw/cfdp/pdu/EofPduReader.h"
#include "fsfw/cfdp/pdu/FileDataReader.h"
#include "fsfw/cfdp/pdu/FinishedPduCreator.h"
#include "fsfw/cfdp/pdu/HeaderReader.h"
#include "fsfw/cfdp/pdu/KeepAlivePduCreator.h"
#include "fsfw/cfdp/pdu/NakPduCreator.h"
#include "fsfw/objectmanager.h"
#include "fsfw/returnvalues/returnvalue.h"
#include "fsfw/tmtcservices/TmTcMessage.h"
using namespace returnvalue;
@@ -23,7 +29,13 @@ cfdp::DestHandler::DestHandler(PduSenderIF& pduSender, size_t pduBufSize, DestHa
msgToUserVec(params.maxTlvsInOnePdu),
transactionParams(params.maxFilenameLen),
destParams(std::move(params)),
fsfwParams(fsfwParams) {
fsfwParams(fsfwParams),
// At least one request has to fit, otherwise sendNakSequence() indexes an empty buffer and
// the deferred lost segment procedure could not make progress anyway.
nakSegmentBuf(std::max<size_t>(destParams.maxSegmentRequestsPerNakPdu, 1)),
positiveAckTimer(0, false),
nakTimer(0, false),
checkTimer(0, false) {
transactionParams.pduConf.direction = cfdp::Direction::TOWARDS_SENDER;
}
@@ -40,8 +52,31 @@ const cfdp::DestHandler::FsmResult& cfdp::DestHandler::stateMachine(
return fsmRes;
}
PduPacketIF& pduPacket = *optPduPacket;
if (pduPacket.getPduType() == FILE_DATA or
(pduPacket.getPduType() == FILE_DIRECTIVE and *pduPacket.getFileDirective() != METADATA)) {
if (pduPacket.getPduType() == FILE_DIRECTIVE and
*pduPacket.getFileDirective() == EOF_DIRECTIVE) {
// D7 of the class 2 plan: an EOF PDU retransmitted after we already finished the
// transaction still has to be acknowledged. Without this the sender keeps retransmitting
// until its positive ACK limit and then declares a fault at the end of a transfer that
// actually succeeded.
result = ackInactiveEofPdu(pduPacket);
if (result != OK) {
fsmRes.result = result;
return fsmRes;
}
return updateFsmRes(errorIdx);
}
if (pduPacket.getPduType() == FILE_DATA) {
// In acknowledged mode a lost metadata PDU is recoverable: start the transaction from the
// PDU header and ask for the metadata with a NAK of scope 0..0. Only acknowledged mode
// transactions can do this, everything else stays an error.
result = startMetadatalessTransaction(pduPacket);
if (result == OK) {
return updateFsmRes(errorIdx);
}
fsmRes.result = DEST_NON_METADATA_PDU_AS_FIRST_PDU;
return fsmRes;
}
if (pduPacket.getPduType() == FILE_DIRECTIVE and *pduPacket.getFileDirective() != METADATA) {
fsmRes.result = DEST_NON_METADATA_PDU_AS_FIRST_PDU;
return fsmRes;
}
@@ -50,6 +85,11 @@ const cfdp::DestHandler::FsmResult& cfdp::DestHandler::stateMachine(
return updateFsmRes(errorIdx);
}
if (fsmRes.state == CfdpState::BUSY_CLASS_2_ACKED) {
fsmAcked(optPduPacket, errorIdx);
return updateFsmRes(errorIdx);
}
if (fsmRes.state == CfdpState::BUSY_CLASS_1_NACKED) {
if (fsmRes.step == TransactionStep::RECEIVING_FILE_DATA_PDUS) {
if (!optPduPacket.has_value()) {
@@ -73,20 +113,88 @@ const cfdp::DestHandler::FsmResult& cfdp::DestHandler::stateMachine(
checkAndHandleError(result, errorIdx);
}
if (fsmRes.step == TransactionStep::SENDING_FINISHED_PDU) {
result = sendFinishedPdu();
checkAndHandleError(result, errorIdx);
finish();
// Class 1 has no ACK for the Finished PDU, so the transaction is done the moment it goes
// out. It must actually go out first though: finishing on a failed send means the peer never
// hears that a transfer which did succeed completed, and it has no way to ask again.
if (trySendingFinishedPdu(errorIdx)) {
finish();
}
return updateFsmRes(errorIdx);
}
if (fsmRes.state == CfdpState::BUSY_CLASS_2_ACKED) {
// TODO: Will be implemented at a later stage
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "CFDP state machine for acknowledged mode not implemented yet" << std::endl;
#endif
}
return updateFsmRes(errorIdx);
}
void cfdp::DestHandler::fsmAcked(
const std::optional<std::reference_wrapper<PduPacketIF>> optPduPacket, uint8_t& errorIdx) {
ReturnValue_t result;
if (optPduPacket.has_value()) {
PduPacketIF& pduPacket = *optPduPacket;
if (pduPacket.getPduType() == FILE_DATA) {
// Retransmitted segments are only expected while the file is still being received.
// handleFileDataPdu writes at the PDU offset, so out of order writes are fine.
if (fsmRes.step == TransactionStep::RECEIVING_FILE_DATA_PDUS or
fsmRes.step == TransactionStep::WAITING_FOR_MISSING_DATA) {
result = handleFileDataPdu(pduPacket);
checkAndHandleError(result, errorIdx);
}
} else if (pduPacket.getPduType() == FILE_DIRECTIVE) {
switch (*pduPacket.getFileDirective()) {
case (METADATA): {
// Either a duplicate, which startTransaction discards, or the retransmission we asked
// for after a lost metadata PDU.
result = handleMetadataPdu(pduPacket);
checkAndHandleError(result, errorIdx);
break;
}
case (EOF_DIRECTIVE): {
result = handleEofPdu(pduPacket);
checkAndHandleError(result, errorIdx);
break;
}
case (ACK): {
result = handleAckPdu(pduPacket);
checkAndHandleError(result, errorIdx);
break;
}
default:
break;
}
}
}
// D2: the ACK for the EOF PDU is emitted here, before TRANSFER_COMPLETION runs the checksum
// pass over the whole received file. On an iOBC that pass takes seconds for a large file,
// which is long enough for the sender's positive ACK timer to fire.
if (fsmRes.step == TransactionStep::SENDING_ACK_PDU) {
result = handleSendingAckPdu();
checkAndHandleError(result, errorIdx);
}
if (fsmRes.step == TransactionStep::WAITING_FOR_MISSING_DATA) {
result = handleWaitingForMissingData();
checkAndHandleError(result, errorIdx);
}
if (fsmRes.step == TransactionStep::TRANSFER_COMPLETION) {
result = handleTransferCompletion();
checkAndHandleError(result, errorIdx);
}
if (fsmRes.step == TransactionStep::SENDING_FINISHED_PDU) {
if (not trySendingFinishedPdu(errorIdx)) {
// Nothing went out, so there is no ACK to wait for: either the send is retried on the next
// call, or the attempt budget ran out and the transaction has already been released.
return;
}
// In acknowledged mode the Finished PDU is itself acknowledged, so the transaction is
// retained until the ACK arrives instead of being finished right here.
transactionParams.positiveAckCounter = 0;
positiveAckTimer.setTimeout(transactionParams.remoteCfg->positiveAckTimerIntervalMs);
fsmRes.step = TransactionStep::WAITING_FOR_FINISHED_ACK;
return;
}
if (fsmRes.step == TransactionStep::WAITING_FOR_FINISHED_ACK) {
result = handleWaitingForFinishedAck();
checkAndHandleError(result, errorIdx);
}
}
ReturnValue_t cfdp::DestHandler::handleMetadataPdu(const PduPacketIF& pduPacket) {
// Process metadata PDU
cfdp::StringLv sourceFileName;
@@ -116,6 +224,11 @@ ReturnValue_t cfdp::DestHandler::handleFileDataPdu(const PduPacketIF& info) {
if (result != OK) {
return result;
}
if (fsmRes.state == CfdpState::BUSY_CLASS_2_ACKED and not transactionParams.metadataReceived) {
// The metadata PDU was lost, so there is no destination file name to write to yet. Discard
// the payload; it is re-requested by the NAK sequence once the metadata has arrived.
return OK;
}
size_t fileSegmentLen = 0;
const uint8_t* fileData = fdInfo.getFileData(&fileSegmentLen);
if (destParams.cfg.indicCfg.fileSegmentRecvIndicRequired) {
@@ -148,8 +261,13 @@ ReturnValue_t cfdp::DestHandler::handleFileDataPdu(const PduPacketIF& info) {
}
transactionParams.deliveryStatus = FileDeliveryStatus::RETAINED_IN_FILESTORE;
transactionParams.vfsErrorCount = 0;
if (fdInfo.getOffset().value() + fileSegmentLen > transactionParams.progress) {
transactionParams.progress = fdInfo.getOffset().value() + fileSegmentLen;
const uint64_t offset = fdInfo.getOffset().value();
const uint64_t endOfSegment = offset + fileSegmentLen;
if (fsmRes.state == CfdpState::BUSY_CLASS_2_ACKED) {
trackReceivedSegment(offset, endOfSegment);
}
if (endOfSegment > transactionParams.progress) {
transactionParams.progress = endOfSegment;
}
return result;
}
@@ -158,7 +276,7 @@ ReturnValue_t cfdp::DestHandler::handleEofPdu(const cfdp::PduPacketIF& info) {
size_t pduSize = 0;
const auto rawPdu = info.getRawPduData(pduSize);
// Process EOF PDU
EofInfo eofInfo(nullptr);
EofInfo eofInfo(&eofFaultLocation);
EofPduReader reader(rawPdu, pduSize, eofInfo);
ReturnValue_t result = reader.parseData();
if (result != OK) {
@@ -177,12 +295,39 @@ ReturnValue_t cfdp::DestHandler::handleEofPdu(const cfdp::PduPacketIF& info) {
if (destParams.cfg.indicCfg.eofRecvIndicRequired) {
destParams.user.eofRecvIndication(getTransactionId());
}
if (fsmRes.step == TransactionStep::RECEIVING_FILE_DATA_PDUS) {
if (fsmRes.state == CfdpState::BUSY_CLASS_1_NACKED) {
if (fsmRes.state == CfdpState::BUSY_CLASS_1_NACKED) {
if (fsmRes.step == TransactionStep::RECEIVING_FILE_DATA_PDUS) {
fsmRes.step = TransactionStep::TRANSFER_COMPLETION;
} else if (fsmRes.state == CfdpState::BUSY_CLASS_2_ACKED) {
fsmRes.step = TransactionStep::SENDING_ACK_PDU;
}
return returnvalue::OK;
}
if (fsmRes.state != CfdpState::BUSY_CLASS_2_ACKED) {
return returnvalue::OK;
}
transactionParams.eofReceived = true;
transactionParams.eofConditionCode = eofInfo.getConditionCode();
if (eofInfo.getConditionCode() != ConditionCode::NO_ERROR) {
// A Cancel EOF ends the transaction. Adopt its condition code so that transfer completion
// reports the cancellation, instead of running a checksum pass over a file the sender has
// already abandoned and then reporting a checksum failure for it.
transactionParams.conditionCode = eofInfo.getConditionCode();
}
if (eofInfo.getConditionCode() == ConditionCode::NO_ERROR and
transactionParams.fileSize.value() > transactionParams.progress) {
// Everything between the highest offset seen so far and the file size announced by the EOF
// PDU is missing. This is also the only gap that exists for a transfer which lost its tail.
insertLostSegment(transactionParams.progress, transactionParams.fileSize.value());
}
if (fsmRes.step == TransactionStep::RECEIVING_FILE_DATA_PDUS or
fsmRes.step == TransactionStep::WAITING_FOR_MISSING_DATA) {
// A duplicate EOF arriving during the lost segment procedure means our ACK did not make it
// back, so re-enter the ACK step, which also re-issues the NAK sequence.
fsmRes.step = TransactionStep::SENDING_ACK_PDU;
} else {
// Duplicate EOF for a transaction we are already completing. Re-acknowledge it without
// disturbing the step we are in.
return sendAckPdu(transactionParams.pduConf, FileDirective::EOF_DIRECTIVE,
transactionParams.eofConditionCode, AckTransactionStatus::ACTIVE);
}
return returnvalue::OK;
}
@@ -226,8 +371,12 @@ ReturnValue_t cfdp::DestHandler::handleMetadataParseError(ReturnValue_t result,
}
ReturnValue_t cfdp::DestHandler::startTransaction(const MetadataPduReader& reader) {
if (fsmRes.state != CfdpState::IDLE) {
// According to standard, discard metadata PDU if we are busy
// A metadata PDU received while busy is normally a duplicate and is discarded per the standard.
// The one exception is an acknowledged transaction which was started from a file data or EOF
// PDU because the metadata was lost: this is the retransmission our NAK of scope 0..0 asked for.
const bool lateMetadata =
fsmRes.state == CfdpState::BUSY_CLASS_2_ACKED and not transactionParams.metadataReceived;
if (fsmRes.state != CfdpState::IDLE and not lateMetadata) {
return OK;
}
ReturnValue_t result = OK;
@@ -296,18 +445,21 @@ ReturnValue_t cfdp::DestHandler::startTransaction(const MetadataPduReader& reade
#endif
return FAILED;
}
if (reader.getTransmissionMode() == TransmissionMode::UNACKNOWLEDGED) {
fsmRes.state = CfdpState::BUSY_CLASS_1_NACKED;
} else if (reader.getTransmissionMode() == TransmissionMode::ACKNOWLEDGED) {
fsmRes.state = CfdpState::BUSY_CLASS_2_ACKED;
}
if (transactionParams.metadataOnly) {
fsmRes.step = TransactionStep::TRANSFER_COMPLETION;
} else {
// Kind of ugly, make FSM working on packet per packet basis..
fsmRes.step = TransactionStep::TRANSACTION_START;
fsmRes.step = TransactionStep::RECEIVING_FILE_DATA_PDUS;
if (not lateMetadata) {
if (reader.getTransmissionMode() == TransmissionMode::UNACKNOWLEDGED) {
fsmRes.state = CfdpState::BUSY_CLASS_1_NACKED;
} else if (reader.getTransmissionMode() == TransmissionMode::ACKNOWLEDGED) {
fsmRes.state = CfdpState::BUSY_CLASS_2_ACKED;
}
if (transactionParams.metadataOnly) {
fsmRes.step = TransactionStep::TRANSFER_COMPLETION;
} else {
// Kind of ugly, make FSM working on packet per packet basis..
fsmRes.step = TransactionStep::TRANSACTION_START;
fsmRes.step = TransactionStep::RECEIVING_FILE_DATA_PDUS;
}
}
transactionParams.metadataReceived = true;
auto& info = reader.getGenericInfo();
transactionParams.checksumType = info.getChecksumType();
transactionParams.closureRequested = info.isClosureRequested();
@@ -338,13 +490,27 @@ cfdp::CfdpState cfdp::DestHandler::getCfdpState() const { return fsmRes.state; }
ReturnValue_t cfdp::DestHandler::handleTransferCompletion() {
ReturnValue_t result;
if (transactionParams.checksumType != ChecksumType::NULL_CHECKSUM) {
if (transactionParams.conditionCode != ConditionCode::NO_ERROR) {
// The transaction was cancelled, for example because the NAK or check limit was reached.
// Report that condition code in the Finished PDU rather than running a checksum pass over a
// file which is known to be incomplete.
transactionParams.deliveryCode = FileDeliveryCode::DATA_INCOMPLETE;
} else if (transactionParams.checksumType != ChecksumType::NULL_CHECKSUM) {
result = checksumVerification();
if (result != OK) {
// TODO: Warning / error handling?
}
} else {
transactionParams.conditionCode = ConditionCode::NO_ERROR;
if (transactionParams.metadataOnly) {
// Nothing was expected, so nothing is missing: a metadata only transaction - a proxy put
// request, say - carries no file data and completes the moment its metadata arrives. Both
// fields are otherwise left at the reset defaults, which report a successful transaction as
// "Data Incomplete" and "Discard deliberately". That contradicts the NO_ERROR condition code
// beside it, and it goes out in the Finished PDU, not just into the log.
transactionParams.deliveryCode = FileDeliveryCode::DATA_COMPLETE;
transactionParams.deliveryStatus = FileDeliveryStatus::FILE_STATUS_UNREPORTED;
}
}
result = noticeOfCompletion();
if (result != OK) {
@@ -394,6 +560,7 @@ void cfdp::DestHandler::fileErrorHandler(Event event, ReturnValue_t result,
}
void cfdp::DestHandler::finish() {
destParams.lostSegmentsContainer.clear();
transactionParams.reset();
fsmRes.state = CfdpState::IDLE;
fsmRes.step = TransactionStep::IDLE;
@@ -450,6 +617,19 @@ ReturnValue_t cfdp::DestHandler::noticeOfCompletion() {
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() {
FinishedInfo info(transactionParams.conditionCode, transactionParams.deliveryCode,
transactionParams.deliveryStatus);
@@ -467,7 +647,7 @@ ReturnValue_t cfdp::DestHandler::sendFinishedPdu() {
fsfwParams.eventReporter->forwardEvent(events::SERIALIZATION_ERROR, result, 0);
return result;
}
pduSender.sendPdu(PduType::FILE_DIRECTIVE, FileDirective::FINISH, pduBuf.data(), serLen);
result = pduSender.sendPdu(PduType::FILE_DIRECTIVE, FileDirective::FINISH, pduBuf.data(), serLen);
if (result != OK) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "cfdp::DestHandler::sendFinishedPdu: Sending PDU failed" << std::endl;
@@ -479,6 +659,33 @@ ReturnValue_t cfdp::DestHandler::sendFinishedPdu() {
return OK;
}
bool cfdp::DestHandler::trySendingFinishedPdu(uint8_t& errorIdx) {
transactionParams.finishedSendAttempts++;
const ReturnValue_t result = sendFinishedPdu();
checkAndHandleError(result, errorIdx);
if (result == OK) {
return true;
}
if (transactionParams.finishedSendAttempts < destParams.maxFinishedPduSendAttempts) {
// Leave the step where it is so the next state machine call retries the same PDU. A send
// failure here is usually a full TM store, which drains on its own.
return false;
}
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "cfdp::DestHandler: giving up on the Finished PDU after "
<< transactionParams.finishedSendAttempts << " attempts" << std::endl;
#else
sif::printWarning("cfdp::DestHandler: giving up on the Finished PDU after %u attempts\n",
static_cast<unsigned>(transactionParams.finishedSendAttempts));
#endif
// Only the peer's notification is lost: the file is complete on disk and the local user
// already got its indication from noticeOfCompletion(). So this is not a delivery fault, and
// releasing the handler matters more than the notification - while a transaction is held, every
// incoming metadata PDU is discarded and no new uplink can start.
finish();
return false;
}
cfdp::DestHandler::TransactionStep cfdp::DestHandler::getTransactionStep() const {
return fsmRes.step;
}
@@ -496,6 +703,8 @@ const cfdp::TransactionId& cfdp::DestHandler::getTransactionId() const {
return transactionParams.transactionId;
}
uint64_t cfdp::DestHandler::getProgress() const { return transactionParams.progress; }
void cfdp::DestHandler::checkAndHandleError(ReturnValue_t result, uint8_t& errorIdx) {
if (result != OK and errorIdx < 3) {
fsmRes.errorCodes[errorIdx] = result;
@@ -509,4 +718,356 @@ void cfdp::DestHandler::setEventReporter(EventReportingProxyIF& reporter) {
const cfdp::DestHandlerParams& cfdp::DestHandler::getDestHandlerParams() const {
return destParams;
}
}
bool cfdp::DestHandler::pduBelongsToTransaction(const PduPacketIF& pduPacket) const {
size_t pduSize = 0;
const auto rawPdu = pduPacket.getRawPduData(pduSize);
PduHeaderReader reader(rawPdu, pduSize);
if (reader.parseData() != OK) {
return false;
}
EntityId sourceId;
reader.getSourceId(sourceId);
TransactionSeqNum seqNum;
reader.getTransactionSeqNum(seqNum);
// Compared by value rather than with operator==, which also compares the encoded width: the
// peer is free to use a different width than we do for the same number.
return sourceId.getValue() == transactionParams.transactionId.entityId.getValue() and
seqNum.getValue() == transactionParams.transactionId.seqNum.getValue();
}
ReturnValue_t cfdp::DestHandler::handleAckPdu(const cfdp::PduPacketIF& info) {
size_t pduSize = 0;
const auto rawPdu = info.getRawPduData(pduSize);
AckInfo ackInfo;
AckPduReader reader(rawPdu, pduSize, ackInfo);
ReturnValue_t result = reader.parseData();
if (result != OK) {
return result;
}
// ACKs for EOF PDUs belong to the source handler and are routed there, so the only ACK which
// can legitimately reach the destination handler is the one for our Finished PDU.
if (ackInfo.getAckedDirective() != FileDirective::FINISH) {
return OK;
}
if (not pduBelongsToTransaction(info)) {
// The CFDP handler routes ACK PDUs on the acknowledged directive alone, so a late ACK from
// an earlier transaction would otherwise finish whichever one is running now.
return OK;
}
if (fsmRes.step == TransactionStep::WAITING_FOR_FINISHED_ACK) {
finish();
}
return OK;
}
ReturnValue_t cfdp::DestHandler::handleSendingAckPdu() {
ReturnValue_t result =
sendAckPdu(transactionParams.pduConf, FileDirective::EOF_DIRECTIVE,
transactionParams.eofConditionCode, AckTransactionStatus::ACTIVE);
if (result != OK) {
return result;
}
if (transactionParams.eofConditionCode != ConditionCode::NO_ERROR) {
// The sender cancelled the transaction. Nothing left to request, report what we have.
transactionParams.conditionCode = transactionParams.eofConditionCode;
fsmRes.step = TransactionStep::TRANSFER_COMPLETION;
return OK;
}
if (isFileComplete()) {
fsmRes.step = TransactionStep::TRANSFER_COMPLETION;
return OK;
}
// D3: deferred lost segment procedure. The NAK sequence for everything known to be missing is
// issued once, here, and then re-issued on NAK timer expiry.
fsmRes.step = TransactionStep::WAITING_FOR_MISSING_DATA;
transactionParams.nakCounter = 0;
transactionParams.checkCounter = 0;
result = sendNakSequence();
nakTimer.setTimeout(transactionParams.remoteCfg->nakTimerIntervalMs);
checkTimer.setTimeout(transactionParams.remoteCfg->checkTimerIntervalMs);
return result;
}
ReturnValue_t cfdp::DestHandler::handleWaitingForMissingData() {
if (isFileComplete()) {
fsmRes.step = TransactionStep::TRANSFER_COMPLETION;
return OK;
}
if (checkTimer.hasTimedOut()) {
transactionParams.checkCounter++;
checkTimer.resetTimer();
if (transactionParams.checkCounter > transactionParams.remoteCfg->checkLimit) {
// A7: without this an incomplete transfer pins the handler forever and no later uplink
// can start.
declareFault(ConditionCode::CHECK_LIMIT_REACHED);
fsmRes.step = TransactionStep::TRANSFER_COMPLETION;
return OK;
}
}
if (nakTimer.hasTimedOut()) {
transactionParams.nakCounter++;
nakTimer.resetTimer();
if (transactionParams.nakCounter > transactionParams.remoteCfg->nakTimerExpirationLimit) {
declareFault(ConditionCode::NAK_LIMIT_REACHED);
fsmRes.step = TransactionStep::TRANSFER_COMPLETION;
return OK;
}
return sendNakSequence();
}
return OK;
}
ReturnValue_t cfdp::DestHandler::handleWaitingForFinishedAck() {
if (not positiveAckTimer.hasTimedOut()) {
return OK;
}
transactionParams.positiveAckCounter++;
positiveAckTimer.resetTimer();
if (transactionParams.positiveAckCounter >
transactionParams.remoteCfg->positiveAckTimerExpirationLimit) {
// The sender is not acknowledging our Finished PDU. The file itself is already written, so
// report the fault and release the handler instead of holding the transaction forever.
declareFault(ConditionCode::POSITIVE_ACK_LIMIT_REACHED);
finish();
return OK;
}
return sendFinishedPdu();
}
ReturnValue_t cfdp::DestHandler::startMetadatalessTransaction(const PduPacketIF& pduPacket) {
size_t pduSize = 0;
const auto rawPdu = pduPacket.getRawPduData(pduSize);
PduHeaderReader headerReader(rawPdu, pduSize);
ReturnValue_t result = headerReader.parseData();
if (result != OK) {
return result;
}
if (headerReader.getTransmissionMode() != TransmissionMode::ACKNOWLEDGED) {
// Unacknowledged mode has no way of recovering the metadata PDU, so this stays an error.
return FAILED;
}
EntityId sourceId;
headerReader.getSourceId(sourceId);
if (not destParams.remoteCfgTable.getRemoteCfg(sourceId, &transactionParams.remoteCfg)) {
return FAILED;
}
headerReader.fillConfig(transactionParams.pduConf);
transactionParams.pduConf.crcFlag = transactionParams.remoteCfg->crcOnTransmission;
transactionParams.pduConf.direction = Direction::TOWARDS_SENDER;
transactionParams.transactionId.entityId = transactionParams.pduConf.sourceId;
transactionParams.transactionId.seqNum = transactionParams.pduConf.seqNum;
transactionParams.metadataReceived = false;
transactionParams.metadataOnly = false;
fsmRes.state = CfdpState::BUSY_CLASS_2_ACKED;
fsmRes.step = TransactionStep::RECEIVING_FILE_DATA_PDUS;
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "cfdp::DestHandler: file data PDU without metadata, requesting metadata"
<< std::endl;
#else
sif::printWarning("cfdp::DestHandler: file data PDU without metadata, requesting metadata\n");
#endif
// Nothing at all can be done before the metadata arrives, so this one NAK is not deferred.
result = sendNakSequence();
transactionParams.nakCounter = 0;
transactionParams.checkCounter = 0;
nakTimer.setTimeout(transactionParams.remoteCfg->nakTimerIntervalMs);
checkTimer.setTimeout(transactionParams.remoteCfg->checkTimerIntervalMs);
return result;
}
ReturnValue_t cfdp::DestHandler::ackInactiveEofPdu(const PduPacketIF& pduPacket) {
size_t pduSize = 0;
const auto rawPdu = pduPacket.getRawPduData(pduSize);
PduHeaderReader headerReader(rawPdu, pduSize);
ReturnValue_t result = headerReader.parseData();
if (result != OK) {
return result;
}
if (headerReader.getTransmissionMode() != TransmissionMode::ACKNOWLEDGED) {
return DEST_NON_METADATA_PDU_AS_FIRST_PDU;
}
EofInfo eofInfo(&eofFaultLocation);
EofPduReader eofReader(rawPdu, pduSize, eofInfo);
result = eofReader.parseData();
if (result != OK) {
return result;
}
PduConfig conf;
headerReader.fillConfig(conf);
conf.direction = Direction::TOWARDS_SENDER;
RemoteEntityCfg* remoteCfg = nullptr;
EntityId sourceId;
headerReader.getSourceId(sourceId);
if (destParams.remoteCfgTable.getRemoteCfg(sourceId, &remoteCfg) and remoteCfg != nullptr) {
conf.crcFlag = remoteCfg->crcOnTransmission;
} else {
conf.crcFlag = false;
}
return sendAckPdu(conf, FileDirective::EOF_DIRECTIVE, eofInfo.getConditionCode(),
AckTransactionStatus::UNRECOGNIZED);
}
ReturnValue_t cfdp::DestHandler::sendAckPdu(PduConfig& conf, FileDirective ackedDirective,
ConditionCode conditionCode,
AckTransactionStatus status) {
// CFDP 5.2.4: the directive subtype code is 0b0001 for an acknowledged Finished PDU and
// 0b0000 for every other acknowledged directive.
AckInfo ackInfo(ackedDirective, conditionCode, status,
ackedDirective == FileDirective::FINISH ? 1 : 0);
AckPduCreator ackPdu(ackInfo, conf);
size_t serLen = 0;
ReturnValue_t result = ackPdu.serialize(pduBuf.data(), serLen, ackPdu.getSerializedSize());
if (result != OK) {
fsfwParams.eventReporter->forwardEvent(events::SERIALIZATION_ERROR, result, 0);
return result;
}
result = pduSender.sendPdu(PduType::FILE_DIRECTIVE, FileDirective::ACK, pduBuf.data(), serLen);
if (result != OK) {
fsfwParams.eventReporter->forwardEvent(events::PDU_SEND_ERROR, result, 0);
return result;
}
fsmRes.packetsSent++;
return OK;
}
ReturnValue_t cfdp::DestHandler::sendNakSequence() {
const bool largeFile = transactionParams.pduConf.largeFile;
const uint64_t endOfScope = transactionParams.eofReceived ? transactionParams.fileSize.value()
: transactionParams.progress;
ReturnValue_t worstResult = OK;
size_t idx = 0;
// D5: the segment requests of one NAK PDU are bounded. What does not fit is carried into the
// next PDU of the sequence instead of being dropped or declared a fault.
auto flushNak = [&]() {
if (idx == 0) {
return;
}
NakInfo nakInfo(Fss(0, largeFile), Fss(endOfScope, largeFile));
size_t segLen = idx;
size_t maxSegLen = nakSegmentBuf.size();
nakInfo.setSegmentRequests(nakSegmentBuf.data(), &segLen, &maxSegLen);
NakPduCreator nakPdu(transactionParams.pduConf, nakInfo);
size_t serLen = 0;
ReturnValue_t result = nakPdu.serialize(pduBuf.data(), serLen, nakPdu.getSerializedSize());
if (result != OK) {
fsfwParams.eventReporter->forwardEvent(events::SERIALIZATION_ERROR, result, 0);
worstResult = result;
idx = 0;
return;
}
result = pduSender.sendPdu(PduType::FILE_DIRECTIVE, FileDirective::NAK, pduBuf.data(), serLen);
if (result != OK) {
fsfwParams.eventReporter->forwardEvent(events::PDU_SEND_ERROR, result, 0);
worstResult = result;
} else {
fsmRes.packetsSent++;
}
idx = 0;
};
if (not transactionParams.metadataReceived) {
// CFDP 5.2.6: a segment request of 0 to 0 requests the metadata PDU.
nakSegmentBuf[idx++] = {Fss(0, largeFile), Fss(0, largeFile)};
if (idx == nakSegmentBuf.size()) {
flushNak();
}
}
for (const auto& lostSegment : destParams.lostSegmentsContainer) {
nakSegmentBuf[idx++] = {Fss(lostSegment.first, largeFile), Fss(lostSegment.second, largeFile)};
if (idx == nakSegmentBuf.size()) {
flushNak();
}
}
flushNak();
return worstResult;
}
bool cfdp::DestHandler::isFileComplete() const {
// Class 2 completion is "no gaps left and EOF received", not the class 1 "progress reached the
// file size": a transfer can pass the announced file size with a retransmission while a gap in
// the middle is still outstanding.
return transactionParams.metadataReceived and transactionParams.eofReceived and
destParams.lostSegmentsContainer.empty() and
transactionParams.progress >= transactionParams.fileSize.value();
}
void cfdp::DestHandler::trackReceivedSegment(uint64_t offset, uint64_t endOfSegment) {
if (offset > transactionParams.progress) {
// Everything between the high water mark and this segment was skipped over.
insertLostSegment(transactionParams.progress, offset);
}
removeReceivedRange(offset, endOfSegment);
}
void cfdp::DestHandler::insertLostSegment(uint64_t start, uint64_t end) {
if (end <= start) {
return;
}
auto& container = destParams.lostSegmentsContainer;
// Merge with every entry this range touches so the list cannot accumulate duplicate or
// overlapping gaps when an EOF PDU is retransmitted.
for (auto it = container.begin(); it != container.end();) {
if (it->second < start or it->first > end) {
++it;
continue;
}
start = std::min(start, it->first);
end = std::max(end, it->second);
it = container.erase(it);
}
if (container.full()) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "cfdp::DestHandler: lost segment list full, gap dropped" << std::endl;
#else
sif::printWarning("cfdp::DestHandler: lost segment list full, gap dropped\n");
#endif
return;
}
container.insert({start, end});
}
void cfdp::DestHandler::removeReceivedRange(uint64_t start, uint64_t end) {
if (end <= start) {
return;
}
auto& container = destParams.lostSegmentsContainer;
// A received range can span several gaps, but only the first and the last of them can be left
// with a remainder, so two pending re-insertions are always enough.
std::array<etl::pair<uint64_t, uint64_t>, 2> pending{};
size_t pendingLen = 0;
for (auto it = container.begin(); it != container.end();) {
if (it->second <= start or it->first >= end) {
++it;
continue;
}
const uint64_t gapStart = it->first;
const uint64_t gapEnd = it->second;
it = container.erase(it);
if (gapStart < start and pendingLen < pending.size()) {
pending[pendingLen++] = {gapStart, start};
}
if (gapEnd > end and pendingLen < pending.size()) {
pending[pendingLen++] = {end, gapEnd};
}
}
for (size_t pendingIdx = 0; pendingIdx < pendingLen; pendingIdx++) {
if (container.full()) {
break;
}
container.insert(pending[pendingIdx]);
}
}
void cfdp::DestHandler::declareFault(ConditionCode code) {
transactionParams.conditionCode = code;
transactionParams.deliveryCode = FileDeliveryCode::DATA_INCOMPLETE;
destParams.cfg.fhBase.reportFault(transactionParams.transactionId, code);
}
size_t cfdp::DestHandler::getNumLostSegments() const {
return destParams.lostSegmentsContainer.size();
}
uint32_t cfdp::DestHandler::getNakCounter() const { return transactionParams.nakCounter; }
+91 -1
View File
@@ -14,10 +14,14 @@
#include "fsfw/cfdp/handler/PduPacketIF.h"
#include "fsfw/cfdp/handler/PduSenderIF.h"
#include "fsfw/cfdp/handler/mib.h"
#include "fsfw/cfdp/pdu/HeaderReader.h"
#include "fsfw/cfdp/pdu/MetadataPduReader.h"
#include "fsfw/cfdp/pdu/NakInfo.h"
#include "fsfw/cfdp/pdu/PduConfig.h"
#include "fsfw/cfdp/tlv/EntityIdTlv.h"
#include "fsfw/cfdp/tlv/MessageToUserTlv.h"
#include "fsfw/storagemanager/StorageManagerIF.h"
#include "fsfw/timemanager/Countdown.h"
#include "fsfw/tmtcservices/AcceptsTelemetryIF.h"
namespace cfdp {
@@ -47,6 +51,17 @@ struct DestHandlerParams {
LostSegmentsListBase& lostSegmentsContainer;
uint8_t maxTlvsInOnePdu = 20;
size_t maxFilenameLen = 255;
//! Upper bound on the number of segment requests packed into a single NAK PDU. The remaining
//! lost segments are carried over into the next NAK of the sequence, so this bounds the PDU
//! size without bounding what can be requested. Each segment request is 8 bytes for a small
//! file and 16 bytes for a large one.
size_t maxSegmentRequestsPerNakPdu = 20;
//! Attempts at sending the Finished PDU before the transaction is released without it. The
//! send can fail on a transient downstream condition (a full TM store), and treating that as
//! sent loses the peer's only notification that the transfer completed. The retry has to be
//! bounded though: a busy destination handler discards incoming metadata PDUs, so holding a
//! transaction forever means no later uplink can start.
uint32_t maxFinishedPduSendAttempts = 5;
};
class DestHandler {
@@ -57,7 +72,13 @@ class DestHandler {
RECEIVING_FILE_DATA_PDUS = 2,
SENDING_ACK_PDU = 3,
TRANSFER_COMPLETION = 4,
SENDING_FINISHED_PDU = 5
SENDING_FINISHED_PDU = 5,
//! Class 2 only: the deferred lost segment procedure is running. NAK PDUs have been issued
//! for the known gaps and the handler is waiting for the retransmissions.
WAITING_FOR_MISSING_DATA = 6,
//! Class 2 only: the Finished PDU was sent and its ACK is outstanding. The transaction is
//! retained until the ACK arrives or the positive ACK limit is reached.
WAITING_FOR_FINISHED_ACK = 7
};
struct FsmResult {
@@ -101,8 +122,14 @@ class DestHandler {
[[nodiscard]] CfdpState getCfdpState() const;
[[nodiscard]] TransactionStep getTransactionStep() const;
[[nodiscard]] uint64_t getProgress() const;
ReturnValue_t sendKeepAlivePdu();
[[nodiscard]] const TransactionId& getTransactionId() const;
[[nodiscard]] const DestHandlerParams& getDestHandlerParams() const;
//! Number of gaps currently tracked by the deferred lost segment procedure. Always 0 in class 1.
[[nodiscard]] size_t getNumLostSegments() const;
//! Number of NAK sequences issued for the current transaction.
[[nodiscard]] uint32_t getNakCounter() const;
private:
struct TransactionParams {
@@ -126,8 +153,29 @@ class DestHandler {
closureRequested = false;
vfsErrorCount = 0;
checksumType = ChecksumType::NULL_CHECKSUM;
metadataReceived = false;
eofReceived = false;
eofConditionCode = ConditionCode::NO_ERROR;
positiveAckCounter = 0;
nakCounter = 0;
checkCounter = 0;
finishedSendAttempts = 0;
}
//! Attempts made at sending the Finished PDU, see
//! DestHandlerParams::maxFinishedPduSendAttempts. Used by both transmission modes.
uint32_t finishedSendAttempts = 0;
//! Class 2 only: false while the transaction was started from a file data or EOF PDU because
//! the metadata PDU was lost. The metadata is then requested with a NAK of scope 0..0.
bool metadataReceived = false;
bool eofReceived = false;
//! Condition code of the received EOF PDU, needed to build the matching ACK PDU.
ConditionCode eofConditionCode = ConditionCode::NO_ERROR;
uint32_t positiveAckCounter = 0;
uint32_t nakCounter = 0;
uint32_t checkCounter = 0;
bool metadataOnly = false;
ChecksumType checksumType = ChecksumType::NULL_CHECKSUM;
bool closureRequested = false;
@@ -153,6 +201,20 @@ class DestHandler {
DestHandlerParams destParams;
cfdp::FsfwParams fsfwParams;
FsmResult fsmRes;
//! Scratch space for the segment requests of one NAK PDU. Sized from
//! DestHandlerParams::maxSegmentRequestsPerNakPdu.
std::vector<NakInfo::SegmentRequest> nakSegmentBuf;
//! Guards the Finished PDU, see RemoteEntityCfg::positiveAckTimerIntervalMs.
Countdown positiveAckTimer;
//! Drives the deferred lost segment procedure, see RemoteEntityCfg::nakTimerIntervalMs.
Countdown nakTimer;
//! Guards an incomplete transaction after EOF reception, see RemoteEntityCfg::checkLimit.
Countdown checkTimer;
//! Receives the fault location of an incoming EOF PDU. EofPduReader refuses to parse any EOF
//! whose condition code is not NO_ERROR unless it has somewhere to put that TLV, so without
//! these every Cancel EOF the sender emits would be dropped as unparseable.
cfdp::EntityId eofFaultLocationId;
EntityIdTlv eofFaultLocation{eofFaultLocationId};
ReturnValue_t startTransaction(const MetadataPduReader& reader);
ReturnValue_t handleMetadataPdu(const PduPacketIF& pduPacket);
@@ -160,9 +222,37 @@ class DestHandler {
ReturnValue_t handleEofPdu(const PduPacketIF& info);
ReturnValue_t handleMetadataParseError(ReturnValue_t result, const uint8_t* rawData,
size_t maxSize);
ReturnValue_t handleAckPdu(const PduPacketIF& info);
//! True if the PDU's source entity ID and sequence number match the running transaction. The
//! CFDP handler routes PDUs by direction only, so this is the only transaction level filter.
[[nodiscard]] bool pduBelongsToTransaction(const PduPacketIF& pduPacket) const;
ReturnValue_t handleTransferCompletion();
//! Class 2 substates, driven from stateMachine().
void fsmAcked(std::optional<std::reference_wrapper<PduPacketIF>> optPduPacket, uint8_t& errorIdx);
ReturnValue_t handleSendingAckPdu();
ReturnValue_t handleWaitingForMissingData();
ReturnValue_t handleWaitingForFinishedAck();
ReturnValue_t startMetadatalessTransaction(const PduPacketIF& pduPacket);
ReturnValue_t ackInactiveEofPdu(const PduPacketIF& pduPacket);
ReturnValue_t sendAckPdu(PduConfig& conf, FileDirective ackedDirective,
ConditionCode conditionCode, AckTransactionStatus status);
ReturnValue_t sendNakSequence();
[[nodiscard]] bool isFileComplete() const;
void trackReceivedSegment(uint64_t offset, uint64_t endOfSegment);
void insertLostSegment(uint64_t start, uint64_t end);
void removeReceivedRange(uint64_t start, uint64_t end);
void declareFault(ConditionCode code);
ReturnValue_t tryBuildingAbsoluteDestName(size_t destNameSize);
ReturnValue_t sendFinishedPdu();
/**
* Sends the Finished PDU, retrying a bounded number of times on a failed send.
*
* @return True if the PDU went out and the caller should advance. False means the caller must
* leave the step alone and do nothing else this iteration: either the send is being
* retried on the next call, or the attempt budget ran out and the transaction was
* already released with finish().
*/
bool trySendingFinishedPdu(uint8_t& errorIdx);
ReturnValue_t noticeOfCompletion();
ReturnValue_t checksumVerification();
void fileErrorHandler(Event event, ReturnValue_t result, const char* info) const;
+4 -1
View File
@@ -14,7 +14,10 @@ cfdp::PutRequest::PutRequest(cfdp::EntityId destId, const uint8_t *msgsToUser,
cfdp::PutRequest::PutRequest(cfdp::EntityId destId, cfdp::StringLv &sourceName,
cfdp::StringLv &destName)
: destId(std::move(destId)), sourceName(std::move(sourceName)), destName(std::move(destName)) {}
: destId(std::move(destId)),
metadataOnly(false),
sourceName(std::move(sourceName)),
destName(std::move(destName)) {}
[[nodiscard]] bool cfdp::PutRequest::isMetadataOnly() const { return metadataOnly; }
+412 -36
View File
@@ -4,9 +4,15 @@
#include <array>
#include "fsfw/cfdp/pdu/AckPduCreator.h"
#include "fsfw/cfdp/pdu/AckPduReader.h"
#include "fsfw/cfdp/pdu/EofPduCreator.h"
#include "fsfw/cfdp/pdu/FileDataCreator.h"
#include "fsfw/cfdp/pdu/FileDirectiveReader.h"
#include "fsfw/cfdp/pdu/FinishedPduReader.h"
#include "fsfw/cfdp/pdu/HeaderReader.h"
#include "fsfw/cfdp/pdu/MetadataPduCreator.h"
#include "fsfw/cfdp/pdu/NakPduReader.h"
#include "fsfw/filesystem/HasFileSystemIF.h"
#include "fsfw/globalfunctions/arrayprinter.h"
#include "fsfw/objectmanager.h"
@@ -16,12 +22,28 @@
using namespace returnvalue;
namespace {
//! True if the PDU is long enough to hold the Finished PDU fields which are mandatory, i.e. the
//! directive byte plus the byte carrying the condition code, delivery code and file status.
//! Anything shorter cannot be trusted even partially.
bool mandatoryFinishedFieldsPresent(const uint8_t* rawPdu, size_t pduSize) {
FileDirectiveReader directiveReader(rawPdu, pduSize);
if (directiveReader.parseData() != returnvalue::OK) {
return false;
}
return directiveReader.getWholePduSize() > directiveReader.getHeaderSize();
}
} // namespace
cfdp::SourceHandler::SourceHandler(PduSenderIF& pduSender, size_t pduBufferSize,
SourceHandlerParams params, FsfwParams fsfwParams)
: pduSender(pduSender),
pduBuf(pduBufferSize),
sourceParams(std::move(params)),
fsfwParams(fsfwParams) {
fsfwParams(fsfwParams),
positiveAckTimer(0, false) {
// The entity ID portion of the transaction ID will always remain fixed.
transactionParams.id.entityId = sourceParams.cfg.localId;
transactionParams.pduConf.sourceId = sourceParams.cfg.localId;
@@ -47,8 +69,19 @@ cfdp::SourceHandler::SourceHandler(PduSenderIF& pduSender, size_t pduBufferSize,
transactionParams.pduConf.seqNum.setValue(0);
}
cfdp::SourceHandler::FsmResult& cfdp::SourceHandler::fsmNacked() {
cfdp::SourceHandler::FsmResult& cfdp::SourceHandler::fsmNacked(
const std::optional<std::reference_wrapper<PduPacketIF>> optPduPacket) {
ReturnValue_t result;
if (optPduPacket.has_value() and step == TransactionStep::WAIT_FOR_FINISH) {
PduPacketIF& pduPacket = *optPduPacket;
if (pduPacket.getPduType() == FILE_DIRECTIVE and *pduPacket.getFileDirective() == FINISH and
pduBelongsToTransaction(pduPacket)) {
result = handleFinishedPdu(pduPacket);
if (result != OK) {
addError(result);
}
}
}
if (step == TransactionStep::IDLE) {
step = TransactionStep::TRANSACTION_START;
}
@@ -86,6 +119,11 @@ cfdp::SourceHandler::FsmResult& cfdp::SourceHandler::fsmNacked() {
}
if (transactionParams.closureRequested) {
step = TransactionStep::WAIT_FOR_FINISH;
// The Finished PDU is now actually waited for. Bound the wait with the positive ACK timer
// configuration so a lost Finished PDU cannot pin the handler: without closure the
// transaction completed immediately, so a hang here would be a regression.
transactionParams.positiveAckCounter = 0;
positiveAckTimer.setTimeout(transactionParams.remoteCfg.positiveAckTimerIntervalMs);
// fsmResult.callStatus = CallStatus::CALL_AFTER_DELAY;
} else {
step = TransactionStep::NOTICE_OF_COMPLETION;
@@ -94,7 +132,20 @@ cfdp::SourceHandler::FsmResult& cfdp::SourceHandler::fsmNacked() {
return fsmResult;
}
if (step == TransactionStep::WAIT_FOR_FINISH) {
// TODO: In case this is a request with closure, wait for finish.
if (not transactionParams.finishedReceived) {
if (not positiveAckTimer.hasTimedOut()) {
return fsmResult;
}
transactionParams.positiveAckCounter++;
positiveAckTimer.resetTimer();
if (transactionParams.positiveAckCounter <=
transactionParams.remoteCfg.positiveAckTimerExpirationLimit) {
return fsmResult;
}
// Unacknowledged mode has no retransmission, so the only thing left is to report that the
// peer never confirmed the transfer.
declareFault(ConditionCode::INACTIVITY_DETECTED);
}
// Done, issue notice of completion
step = TransactionStep::NOTICE_OF_COMPLETION;
}
@@ -117,7 +168,10 @@ const cfdp::SourceHandler::FsmResult& cfdp::SourceHandler::stateMachine(
return fsmResult;
}
if (state == cfdp::CfdpState::BUSY_CLASS_1_NACKED) {
return fsmNacked();
return fsmNacked(optPduPacket);
}
if (state == cfdp::CfdpState::BUSY_CLASS_2_ACKED) {
return fsmAcked(optPduPacket);
}
return fsmResult;
}
@@ -210,7 +264,10 @@ ReturnValue_t cfdp::SourceHandler::transactionStart(PutRequest& putRequest, Remo
state = cfdp::CfdpState::BUSY_CLASS_2_ACKED;
} else if (transactionParams.pduConf.mode == TransmissionMode::UNACKNOWLEDGED) {
state = cfdp::CfdpState::BUSY_CLASS_1_NACKED;
} else {
return TRANSMISSION_MODE_NOT_SUPPORTED;
}
retransmitState.reset();
step = TransactionStep::IDLE;
uint64_t fileSize = 0;
sourceParams.user.vfs.getFileSize(transactionParams.sourceName.data(), fileSize);
@@ -230,15 +287,7 @@ ReturnValue_t cfdp::SourceHandler::transactionStart(PutRequest& putRequest, Remo
}
ReturnValue_t cfdp::SourceHandler::prepareAndSendMetadataPdu() {
cfdp::StringLv sourceName(transactionParams.sourceName.data(), transactionParams.sourceNameSize);
cfdp::StringLv destName(transactionParams.destName.data(), transactionParams.destNameSize);
auto metadataInfo =
MetadataGenericInfo(transactionParams.closureRequested, transactionParams.checksumType,
transactionParams.fileSize);
auto metadataPdu =
MetadataPduCreator(transactionParams.pduConf, metadataInfo, sourceName, destName, nullptr, 0);
ReturnValue_t result =
sendGenericPdu(PduType::FILE_DIRECTIVE, FileDirective::METADATA, metadataPdu);
ReturnValue_t result = sendMetadataPdu();
if (result != OK) {
return result;
}
@@ -247,8 +296,18 @@ ReturnValue_t cfdp::SourceHandler::prepareAndSendMetadataPdu() {
return OK;
}
ReturnValue_t cfdp::SourceHandler::sendMetadataPdu() {
cfdp::StringLv sourceName(transactionParams.sourceName.data(), transactionParams.sourceNameSize);
cfdp::StringLv destName(transactionParams.destName.data(), transactionParams.destNameSize);
auto metadataInfo =
MetadataGenericInfo(transactionParams.closureRequested, transactionParams.checksumType,
transactionParams.fileSize);
auto metadataPdu =
MetadataPduCreator(transactionParams.pduConf, metadataInfo, sourceName, destName, nullptr, 0);
return sendGenericPdu(PduType::FILE_DIRECTIVE, FileDirective::METADATA, metadataPdu);
}
ReturnValue_t cfdp::SourceHandler::prepareAndSendNextFileDataPdu(bool& noFileDataPdu) {
cfdp::Fss offset(transactionParams.progress);
uint64_t lenToRead;
uint64_t fileSize = transactionParams.fileSize.value();
noFileDataPdu = false;
@@ -267,19 +326,7 @@ ReturnValue_t cfdp::SourceHandler::prepareAndSendNextFileDataPdu(bool& noFileDat
lenToRead = transactionParams.remoteCfg.maxFileSegmentLen;
}
}
FileOpParams fileParams(transactionParams.sourceName.data(), lenToRead);
fileParams.offset = transactionParams.progress;
size_t readLen = 0;
ReturnValue_t result = sourceParams.user.vfs.readFromFile(
transactionParams.sourceName.data(), transactionParams.progress, lenToRead, fileBuf.data(),
readLen, fileBuf.size());
if (result != returnvalue::OK) {
addError(result);
return result;
}
auto fileDataInfo = FileDataInfo(offset, fileBuf.data(), lenToRead);
auto fileDataPdu = FileDataCreator(transactionParams.pduConf, fileDataInfo);
result = sendGenericPdu(PduType::FILE_DATA, std::nullopt, fileDataPdu);
ReturnValue_t result = sendFileDataPdu(transactionParams.progress, lenToRead);
if (result != OK) {
return result;
}
@@ -319,20 +366,23 @@ ReturnValue_t cfdp::SourceHandler::sendGenericPdu(PduType pduType,
addError(result);
return result;
}
pduSender.sendPdu(pduType, fileDirective, pduBuf.data(), serializedLen);
result = pduSender.sendPdu(pduType, fileDirective, pduBuf.data(), serializedLen);
if (result != OK) {
addError(result);
return result;
}
fsmResult.packetsSent += 1;
return result;
return OK;
}
ReturnValue_t cfdp::SourceHandler::noticeOfCompletion() {
if (sourceParams.cfg.indicCfg.transactionFinishedIndicRequired) {
// TODO: This could still be improved by caching the Finished PDU parameters.
FileDeliveryStatus deliveryStatus = FileDeliveryStatus::FILE_STATUS_UNREPORTED;
if (transactionParams.closureRequested) {
deliveryStatus = FileDeliveryStatus::RETAINED_IN_FILESTORE;
}
cfdp::TransactionFinishedParams params(transactionParams.id, ConditionCode::NO_ERROR,
FileDeliveryCode::DATA_COMPLETE, deliveryStatus);
// The values reported by the peer's Finished PDU, if one was received. Reporting a hardcoded
// NO_ERROR / DATA_COMPLETE here meant a transfer the receiver rejected still looked
// successful on this side.
cfdp::TransactionFinishedParams params(
transactionParams.id, transactionParams.finishedConditionCode,
transactionParams.finishedDeliveryCode, transactionParams.finishedDeliveryStatus);
sourceParams.user.transactionFinishedIndication(params);
}
return OK;
@@ -341,6 +391,7 @@ ReturnValue_t cfdp::SourceHandler::noticeOfCompletion() {
ReturnValue_t cfdp::SourceHandler::reset() {
step = TransactionStep::IDLE;
state = cfdp::CfdpState::IDLE;
retransmitState.reset();
// fsmResult.callStatus = CallStatus::DONE;
transactionParams.reset();
return OK;
@@ -356,3 +407,328 @@ void cfdp::SourceHandler::addError(ReturnValue_t error) {
fsmResult.result = error;
}
}
cfdp::SourceHandler::FsmResult& cfdp::SourceHandler::fsmAcked(
const std::optional<std::reference_wrapper<PduPacketIF>> optPduPacket) {
ReturnValue_t result;
if (optPduPacket.has_value()) {
handleAckedPdu(*optPduPacket);
}
if (step == TransactionStep::IDLE) {
step = TransactionStep::TRANSACTION_START;
}
if (step == TransactionStep::TRANSACTION_START) {
sourceParams.user.transactionIndication(transactionParams.id);
result = checksumGeneration();
if (result != OK) {
addError(result);
}
step = TransactionStep::SENDING_METADATA;
}
if (step == TransactionStep::SENDING_METADATA) {
result = prepareAndSendMetadataPdu();
if (result != OK) {
addError(result);
}
return fsmResult;
}
if (step == TransactionStep::SENDING_FILE_DATA) {
bool noFdPdu = false;
result = prepareAndSendNextFileDataPdu(noFdPdu);
if (result == OK and !noFdPdu) {
return fsmResult;
}
}
if (step == TransactionStep::SENDING_EOF) {
result = prepareAndSendEofPdu();
if (result != OK) {
addError(result);
}
if (sourceParams.cfg.indicCfg.eofSentIndicRequired) {
sourceParams.user.eofSentIndication(transactionParams.id);
}
// The EOF PDU is acknowledged in class 2. Closure is meaningless here (D8 of the class 2
// plan): the Finished PDU is mandatory, so the transaction always runs to WAIT_FOR_FINISH.
transactionParams.positiveAckCounter = 0;
positiveAckTimer.setTimeout(transactionParams.remoteCfg.positiveAckTimerIntervalMs);
step = TransactionStep::WAIT_FOR_ACK;
return fsmResult;
}
// Retransmissions requested by the peer take priority over any timer work: a source which
// cannot answer a NAK deadlocks the transfer. One PDU per call keeps the burst bounded in
// exactly the same way the regular file data phase is.
if (servicePendingRetransmissions(result)) {
if (result != OK) {
addError(result);
}
return fsmResult;
}
if (step == TransactionStep::WAIT_FOR_ACK) {
if (not positiveAckTimer.hasTimedOut()) {
return fsmResult;
}
transactionParams.positiveAckCounter++;
positiveAckTimer.resetTimer();
if (transactionParams.positiveAckCounter >
transactionParams.remoteCfg.positiveAckTimerExpirationLimit) {
declareFault(ConditionCode::POSITIVE_ACK_LIMIT_REACHED);
noticeOfCompletion();
reset();
return fsmResult;
}
result = prepareAndSendEofPdu();
if (result != OK) {
addError(result);
}
return fsmResult;
}
if (step == TransactionStep::WAIT_FOR_FINISH) {
if (not transactionParams.finishedReceived) {
if (not positiveAckTimer.hasTimedOut()) {
return fsmResult;
}
transactionParams.positiveAckCounter++;
positiveAckTimer.resetTimer();
if (transactionParams.positiveAckCounter <=
transactionParams.remoteCfg.positiveAckTimerExpirationLimit) {
return fsmResult;
}
// The receiver retransmits its Finished PDU on its own positive ACK timer, so reaching
// this point means the downlink is gone rather than a single PDU being lost.
declareFault(ConditionCode::INACTIVITY_DETECTED);
}
step = TransactionStep::NOTICE_OF_COMPLETION;
}
if (step == TransactionStep::NOTICE_OF_COMPLETION) {
noticeOfCompletion();
reset();
}
return fsmResult;
}
bool cfdp::SourceHandler::pduBelongsToTransaction(const PduPacketIF& pduPacket) const {
size_t pduSize = 0;
const auto rawPdu = pduPacket.getRawPduData(pduSize);
PduHeaderReader reader(rawPdu, pduSize);
if (reader.parseData() != OK) {
return false;
}
EntityId sourceId;
reader.getSourceId(sourceId);
TransactionSeqNum seqNum;
reader.getTransactionSeqNum(seqNum);
// Compared by value rather than with operator==, which also compares the encoded width: the
// peer is free to use a different width than we do for the same number.
return sourceId.getValue() == transactionParams.id.entityId.getValue() and
seqNum.getValue() == transactionParams.id.seqNum.getValue();
}
void cfdp::SourceHandler::handleAckedPdu(PduPacketIF& pduPacket) {
if (pduPacket.getPduType() != FILE_DIRECTIVE) {
return;
}
if (not pduBelongsToTransaction(pduPacket)) {
// Nothing upstream filters by transaction: the CFDP handler routes on the PDU direction
// alone. A late ACK or Finished PDU from an earlier transaction would otherwise drive
// whichever transaction is running now.
return;
}
ReturnValue_t result = OK;
switch (*pduPacket.getFileDirective()) {
case (FileDirective::ACK): {
result = handleAckPdu(pduPacket);
break;
}
case (FileDirective::NAK): {
result = handleNakPdu(pduPacket);
break;
}
case (FileDirective::FINISH): {
result = handleFinishedPdu(pduPacket);
break;
}
default:
// Keep Alive PDUs carry progress information only, there is nothing to drive from them.
break;
}
if (result != OK) {
addError(result);
}
}
ReturnValue_t cfdp::SourceHandler::handleAckPdu(const PduPacketIF& pduPacket) {
size_t pduSize = 0;
const auto rawPdu = pduPacket.getRawPduData(pduSize);
AckInfo ackInfo;
AckPduReader reader(rawPdu, pduSize, ackInfo);
ReturnValue_t result = reader.parseData();
if (result != OK) {
return result;
}
// ACKs for Finished PDUs are routed to the destination handler, so only the EOF ACK can
// legitimately arrive here.
if (ackInfo.getAckedDirective() != FileDirective::EOF_DIRECTIVE) {
return OK;
}
if (step == TransactionStep::WAIT_FOR_ACK) {
step = TransactionStep::WAIT_FOR_FINISH;
// Re-arm the same timer as an inactivity guard for the Finished PDU.
transactionParams.positiveAckCounter = 0;
positiveAckTimer.setTimeout(transactionParams.remoteCfg.positiveAckTimerIntervalMs);
}
return OK;
}
ReturnValue_t cfdp::SourceHandler::handleFinishedPdu(const PduPacketIF& pduPacket) {
size_t pduSize = 0;
const auto rawPdu = pduPacket.getRawPduData(pduSize);
FinishedInfo finishedInfo;
FinishPduReader reader(rawPdu, pduSize, finishedInfo);
// The condition code, delivery code and file status are parsed before any TLV, so they are
// usable even if this handler cannot hold the optional filestore responses.
ReturnValue_t result = reader.parseData();
if (result != OK and not mandatoryFinishedFieldsPresent(rawPdu, pduSize)) {
// The PDU is truncated before those fields, so it says nothing at all about how the transfer
// went. Completing the transaction on it would report the default constructed DATA_COMPLETE,
// i.e. a fabricated success, and would discard the state the peer's retransmission needs.
return result;
}
transactionParams.finishedReceived = true;
transactionParams.finishedConditionCode = finishedInfo.getConditionCode();
transactionParams.finishedDeliveryCode = finishedInfo.getDeliveryCode();
transactionParams.finishedDeliveryStatus = finishedInfo.getFileStatus();
if (state == CfdpState::BUSY_CLASS_2_ACKED) {
// The Finished PDU is acknowledged in class 2. This has to happen even for a duplicate,
// because a duplicate means our previous ACK was lost.
ReturnValue_t ackResult =
sendAckPdu(FileDirective::FINISH, transactionParams.finishedConditionCode);
if (ackResult != OK) {
return ackResult;
}
}
if (result != OK) {
// Only the optional TLVs failed to parse, the delivery result above is still valid.
return OK;
}
return OK;
}
ReturnValue_t cfdp::SourceHandler::handleNakPdu(const PduPacketIF& pduPacket) {
size_t pduSize = 0;
const auto rawPdu = pduPacket.getRawPduData(pduSize);
NakInfo nakInfo(Fss(0), Fss(0));
size_t maxSegments = retransmitState.segments.size();
size_t segmentLen = 0;
nakInfo.setSegmentRequests(retransmitState.segments.data(), &segmentLen, &maxSegments);
// The reader writes straight into the segment array, so the previous NAK's bookkeeping is
// invalid the moment parsing starts, whether or not it succeeds. Drop it up front rather than
// leaving indices pointing into a half overwritten array on an error return.
retransmitState.reset();
NakPduReader reader(rawPdu, pduSize, nakInfo);
ReturnValue_t result = reader.parseData();
// The segment requests may have been truncated, but whatever was parsed completely into the
// array is still worth retransmitting: the receiver re-NAKs what it does not get.
retransmitState.numSegments = nakInfo.getSegmentRequestsLen();
retransmitState.currentIdx = 0;
retransmitState.cursor = 0;
retransmitState.metadataPending = false;
// CFDP 5.2.6: a segment request of 0 to 0 asks for the metadata PDU rather than file data.
// Compact it out of the list so the file data path does not have to special case it.
size_t writeIdx = 0;
for (size_t readIdx = 0; readIdx < retransmitState.numSegments; readIdx++) {
const auto& segment = retransmitState.segments[readIdx];
if (segment.first.value() == 0 and segment.second.value() == 0) {
retransmitState.metadataPending = true;
continue;
}
retransmitState.segments[writeIdx++] = segment;
}
retransmitState.numSegments = writeIdx;
// Reported so a truncated NAK is visible as an error even though the requests which did parse
// are serviced normally.
return result;
}
bool cfdp::SourceHandler::servicePendingRetransmissions(ReturnValue_t& result) {
result = OK;
if (retransmitState.metadataPending) {
result = sendMetadataPdu();
if (result == OK) {
// Only clear the request once it actually went out. The peer asked for the metadata
// because it cannot write the file at all without it, and it will not ask again until its
// NAK timer expires.
retransmitState.metadataPending = false;
}
return true;
}
const uint64_t fileSize = transactionParams.fileSize.value();
while (retransmitState.currentIdx < retransmitState.numSegments) {
const auto& segment = retransmitState.segments[retransmitState.currentIdx];
uint64_t start = segment.first.value();
uint64_t end = segment.second.value();
if (retransmitState.cursor > start) {
start = retransmitState.cursor;
}
if (end > fileSize) {
end = fileSize;
}
if (start >= end) {
retransmitState.currentIdx++;
retransmitState.cursor = 0;
continue;
}
uint64_t lenToSend = end - start;
if (lenToSend > transactionParams.remoteCfg.maxFileSegmentLen) {
lenToSend = transactionParams.remoteCfg.maxFileSegmentLen;
}
result = sendFileDataPdu(start, lenToSend);
if (result != OK) {
// The cursor must not move past data which was never enqueued for downlink, exactly as in
// the forward-only path: the next call retries this same offset. Advancing here would drop
// the segment until the peer's NAK timer re-requests it, and that costs a NAK limit credit.
return true;
}
retransmitState.cursor = start + lenToSend;
if (retransmitState.cursor >= end) {
retransmitState.currentIdx++;
retransmitState.cursor = 0;
}
return true;
}
return false;
}
ReturnValue_t cfdp::SourceHandler::sendFileDataPdu(uint64_t offset, size_t lenToRead) {
if (lenToRead > fileBuf.size()) {
addError(FILE_SEGMENT_LEN_INVALID);
return FILE_SEGMENT_LEN_INVALID;
}
size_t readLen = 0;
ReturnValue_t result =
sourceParams.user.vfs.readFromFile(transactionParams.sourceName.data(), offset, lenToRead,
fileBuf.data(), readLen, fileBuf.size());
if (result != returnvalue::OK) {
addError(result);
return result;
}
cfdp::Fss offsetFss(offset, transactionParams.pduConf.largeFile);
auto fileDataInfo = FileDataInfo(offsetFss, fileBuf.data(), lenToRead);
auto fileDataPdu = FileDataCreator(transactionParams.pduConf, fileDataInfo);
return sendGenericPdu(PduType::FILE_DATA, std::nullopt, fileDataPdu);
}
ReturnValue_t cfdp::SourceHandler::sendAckPdu(FileDirective ackedDirective,
ConditionCode conditionCode) {
// CFDP 5.2.4: the directive subtype code is 0b0001 for an acknowledged Finished PDU and
// 0b0000 for every other acknowledged directive.
AckInfo ackInfo(ackedDirective, conditionCode, AckTransactionStatus::ACTIVE,
ackedDirective == FileDirective::FINISH ? 1 : 0);
AckPduCreator ackPdu(ackInfo, transactionParams.pduConf);
return sendGenericPdu(PduType::FILE_DIRECTIVE, FileDirective::ACK, ackPdu);
}
void cfdp::SourceHandler::declareFault(ConditionCode code) {
transactionParams.finishedConditionCode = code;
transactionParams.finishedDeliveryCode = FileDeliveryCode::DATA_INCOMPLETE;
sourceParams.cfg.fhBase.reportFault(transactionParams.id, code);
}
+57 -1
View File
@@ -1,6 +1,7 @@
#ifndef FSFW_CFDP_CFDPSOURCEHANDLER_H
#define FSFW_CFDP_CFDPSOURCEHANDLER_H
#include <array>
#include <cstdint>
#include <vector>
@@ -11,8 +12,10 @@
#include "fsfw/cfdp/Fss.h"
#include "fsfw/cfdp/handler/PutRequest.h"
#include "fsfw/cfdp/handler/mib.h"
#include "fsfw/cfdp/pdu/NakInfo.h"
#include "fsfw/events/EventReportingProxyIF.h"
#include "fsfw/storagemanager/StorageManagerIF.h"
#include "fsfw/timemanager/Countdown.h"
#include "fsfw/tmtcservices/AcceptsTelemetryIF.h"
#include "fsfw/util/ProvidesSeqCountIF.h"
@@ -81,15 +84,52 @@ class SourceHandler {
PduConfig pduConf;
cfdp::TransactionId id{};
//! Number of positive ACK timer expirations for the EOF PDU, or of inactivity timer
//! expirations while waiting for the Finished PDU.
uint32_t positiveAckCounter = 0;
bool finishedReceived = false;
//! Delivery result reported by the peer in its Finished PDU. The defaults are what a
//! transfer without closure reports, which is what the handler did unconditionally before
//! the Finished PDU was actually parsed.
ConditionCode finishedConditionCode = ConditionCode::NO_ERROR;
FileDeliveryCode finishedDeliveryCode = FileDeliveryCode::DATA_COMPLETE;
FileDeliveryStatus finishedDeliveryStatus = FileDeliveryStatus::FILE_STATUS_UNREPORTED;
void reset() {
sourceNameSize = 0;
destNameSize = 0;
fileSize.setFileSize(0, false);
progress = 0;
closureRequested = false;
positiveAckCounter = 0;
finishedReceived = false;
finishedConditionCode = ConditionCode::NO_ERROR;
finishedDeliveryCode = FileDeliveryCode::DATA_COMPLETE;
finishedDeliveryStatus = FileDeliveryStatus::FILE_STATUS_UNREPORTED;
}
} transactionParams;
//! Pending retransmissions requested by the last received NAK PDU. Only the most recent NAK is
//! kept: it is the peer's authoritative statement about what is still missing, and bounding the
//! state this way keeps a NAK storm from growing the handler's memory footprint.
struct RetransmitState {
static constexpr size_t MAX_SEGMENTS = 32;
std::array<NakInfo::SegmentRequest, MAX_SEGMENTS> segments{};
size_t numSegments = 0;
size_t currentIdx = 0;
//! Absolute file offset reached inside the segment at currentIdx, 0 if it was not started.
uint64_t cursor = 0;
bool metadataPending = false;
void reset() {
numSegments = 0;
currentIdx = 0;
cursor = 0;
metadataPending = false;
}
[[nodiscard]] bool empty() const { return not metadataPending and currentIdx >= numSegments; }
} retransmitState;
PduSenderIF& pduSender;
std::vector<uint8_t> pduBuf;
cfdp::CfdpState state = cfdp::CfdpState::IDLE;
@@ -98,13 +138,29 @@ class SourceHandler {
SourceHandlerParams sourceParams;
cfdp::FsfwParams fsfwParams;
FsmResult fsmResult;
//! Guards the EOF PDU in acknowledged mode and the wait for the Finished PDU in both modes.
Countdown positiveAckTimer;
FsmResult& fsmNacked();
FsmResult& fsmNacked(std::optional<std::reference_wrapper<PduPacketIF>> optPduPacket);
FsmResult& fsmAcked(std::optional<std::reference_wrapper<PduPacketIF>> optPduPacket);
//! True if the PDU's source entity ID and sequence number match the running transaction. The
//! CFDP handler routes PDUs by direction only, so this is the only transaction level filter.
[[nodiscard]] bool pduBelongsToTransaction(const PduPacketIF& pduPacket) const;
void handleAckedPdu(PduPacketIF& pduPacket);
ReturnValue_t handleFinishedPdu(const PduPacketIF& pduPacket);
ReturnValue_t handleAckPdu(const PduPacketIF& pduPacket);
ReturnValue_t handleNakPdu(const PduPacketIF& pduPacket);
//! Sends one PDU of the outstanding retransmissions, if there are any.
bool servicePendingRetransmissions(ReturnValue_t& result);
ReturnValue_t sendAckPdu(FileDirective ackedDirective, ConditionCode conditionCode);
ReturnValue_t checksumGeneration();
ReturnValue_t sendMetadataPdu();
ReturnValue_t prepareAndSendMetadataPdu();
ReturnValue_t sendFileDataPdu(uint64_t offset, size_t lenToRead);
ReturnValue_t prepareAndSendNextFileDataPdu(bool& noFileDataPdu);
ReturnValue_t prepareAndSendEofPdu();
ReturnValue_t noticeOfCompletion();
void declareFault(ConditionCode code);
ReturnValue_t reset();
[[nodiscard]] ReturnValue_t sendGenericPdu(PduType pduType,
+7 -5
View File
@@ -19,13 +19,13 @@ struct FsfwParams {
};
namespace events {
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 FILESTORE_ERROR = event::makeEvent(SSID, 3, 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 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
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.
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
static constexpr ReturnValue_t SOURCE_TRANSACTION_PENDING = returnvalue::makeCode(CID, 0);
@@ -38,4 +38,6 @@ static constexpr ReturnValue_t TARGET_MSG_QUEUE_FULL = returnvalue::makeCode(CID
static constexpr ReturnValue_t TM_STORE_FULL = returnvalue::makeCode(CID, 7);
static constexpr ReturnValue_t DEST_NON_METADATA_PDU_AS_FIRST_PDU = returnvalue::makeCode(CID, 8);
static constexpr ReturnValue_t PDU_BUFFER_TOO_SMALL = returnvalue::makeCode(CID, 9);
//! The resolved transmission mode of a request is not supported by this handler (yet).
static constexpr ReturnValue_t TRANSMISSION_MODE_NOT_SUPPORTED = returnvalue::makeCode(CID, 10);
} // namespace cfdp
+25
View File
@@ -36,6 +36,31 @@ struct RemoteEntityCfg {
TransmissionMode defaultTransmissionMode = TransmissionMode::UNACKNOWLEDGED;
ChecksumType defaultChecksum = ChecksumType::NULL_CHECKSUM;
uint8_t version = CFDP_VERSION_2;
// Acknowledged mode (class 2) parameters. The names mirror cfdppy.mib.RemoteEntityConfig field
// for field so both ends of a link can be configured from the same set of numbers. The defaults
// are inert for class 1, which uses none of them.
//! Interval of the positive acknowledgment timer, which guards EOF (source side) and Finished
//! (destination side) PDUs. Must be larger than the peer's worst case time to answer with the
//! matching ACK PDU, or both sides retransmit over each other.
uint32_t positiveAckTimerIntervalMs = 10000;
//! Number of positive ACK timer expirations after which POSITIVE_ACK_LIMIT_REACHED is declared.
uint32_t positiveAckTimerExpirationLimit = 2;
//! Interval of the NAK timer used by the deferred lost segment procedure.
uint32_t nakTimerIntervalMs = 10000;
//! Number of NAK timer expirations after which NAK_LIMIT_REACHED is declared.
uint32_t nakTimerExpirationLimit = 2;
//! If true, a NAK is issued as soon as a gap is detected. If false, the deferred procedure is
//! used and the NAK sequence is only issued once the EOF PDU has arrived. Deferred is the
//! default: on a lossy link the immediate procedure produces a burst of NAK PDUs in the
//! opposite direction exactly when the link is already struggling.
bool immediateNakMode = false;
//! Number of check timer expirations after EOF reception after which CHECK_LIMIT_REACHED is
//! declared and an incomplete transaction is cancelled instead of pinning the handler.
uint32_t checkLimit = 2;
//! Interval of the check timer, see checkLimit.
uint32_t checkTimerIntervalMs = 10000;
};
} // namespace cfdp
+3
View File
@@ -23,6 +23,9 @@ class AckPduCreator : public FileDirectiveCreator {
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
Endianness streamEndianness) const override;
//! Un-hide the convenience overloads of the base class, same as FinishedPduCreator does.
using FileDirectiveCreator::serialize;
private:
AckInfo& ackInfo;
};
+9
View File
@@ -11,6 +11,15 @@ ReturnValue_t FinishPduReader::parseData() {
size_t currentIdx = FileDirectiveReader::getHeaderSize();
const uint8_t* buf = pointers.rawPtr + currentIdx;
size_t remSize = FileDirectiveReader::getWholePduSize() - currentIdx;
// Drop the PDU CRC from the parsed range before parseTlvs below, which runs until the range is
// exhausted. Without this a Finished PDU carrying a CRC is rejected as an invalid TLV type -
// including the common NO_ERROR case, where the CRC is the only thing left after the first byte.
if (getCrcFlag()) {
if (remSize < 2) {
return SerializeIF::STREAM_TOO_SHORT;
}
remSize -= 2;
}
if (remSize < 1) {
return SerializeIF::STREAM_TOO_SHORT;
}
+1
View File
@@ -14,6 +14,7 @@ class KeepAlivePduCreator : public FileDirectiveCreator {
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
Endianness streamEndianness) const override;
using FileDirectiveCreator::serialize;
private:
cfdp::Fss& progress;
+9 -4
View File
@@ -17,6 +17,15 @@ ReturnValue_t MetadataPduReader::parseData() {
size_t currentIdx = FileDirectiveReader::getHeaderSize();
const uint8_t* buf = pointers.rawPtr + currentIdx;
size_t remSize = FileDirectiveReader::getWholePduSize() - currentIdx;
// The PDU CRC occupies the last two bytes of the PDU. Take it out of the parsed range up front,
// like FileDataReader does: the option loop below consumes bytes until the range is exhausted,
// so a CRC left in place would be deserialized as another TLV and rejected as an invalid type.
if (getCrcFlag()) {
if (remSize < 2) {
return SerializeIF::STREAM_TOO_SHORT;
}
remSize -= 2;
}
if (remSize < 1) {
return SerializeIF::STREAM_TOO_SHORT;
}
@@ -38,10 +47,6 @@ ReturnValue_t MetadataPduReader::parseData() {
return result;
}
if (getCrcFlag() && remSize == 2) {
return returnvalue::OK;
}
if (remSize > 0) {
if (optionArrayMaxSize == 0 or optionArray == nullptr) {
return cfdp::METADATA_CANT_PARSE_OPTIONS;
+3
View File
@@ -25,6 +25,9 @@ class NakPduCreator : public FileDirectiveCreator {
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
Endianness streamEndianness) const override;
//! Un-hide the convenience overloads of the base class, same as FinishedPduCreator does.
using FileDirectiveCreator::serialize;
/**
* If you change the info struct, you might need to update the directive field length
* manually
+15
View File
@@ -11,6 +11,14 @@ ReturnValue_t NakPduReader::parseData() {
size_t currentIdx = FileDirectiveReader::getHeaderSize();
const uint8_t* buffer = pointers.rawPtr + currentIdx;
size_t remSize = FileDirectiveReader::getWholePduSize() - currentIdx;
// Drop the PDU CRC from the parsed range before the segment request loop below, which runs
// until the range is exhausted and would otherwise read the CRC as a truncated segment request.
if (getCrcFlag()) {
if (remSize < 2) {
return SerializeIF::STREAM_TOO_SHORT;
}
remSize -= 2;
}
if (remSize < 1) {
return SerializeIF::STREAM_TOO_SHORT;
}
@@ -34,17 +42,24 @@ ReturnValue_t NakPduReader::parseData() {
if (segReqs != nullptr) {
size_t idx = 0;
while (remSize > 0) {
// Every early return below reports the number of *complete* segment requests written so
// far. Leaving the length at 0 would make a partially parsed NAK indistinguishable from
// an empty one, and a caller which tolerates the error code would then act on nothing.
if (idx == maxSegReqs) {
nakInfo.setSegmentRequestLen(idx);
return cfdp::NAK_CANT_PARSE_OPTIONS;
}
result =
segReqs[idx].first.deSerialize(&buffer, &remSize, SerializeIF::Endianness::NETWORK);
if (result != returnvalue::OK) {
nakInfo.setSegmentRequestLen(idx);
return result;
}
result =
segReqs[idx].second.deSerialize(&buffer, &remSize, SerializeIF::Endianness::NETWORK);
if (result != returnvalue::OK) {
// The entry at idx is half written, so it is not counted.
nakInfo.setSegmentRequestLen(idx);
return result;
}
idx++;
+9 -17
View File
@@ -2,7 +2,7 @@
ExtendedControllerBase::ExtendedControllerBase(object_id_t objectId, size_t commandQueueDepth)
: ControllerBase(objectId, commandQueueDepth),
poolManager(this, commandQueue),
hkHelper(this, commandQueue),
actionHelper(this, commandQueue) {}
ExtendedControllerBase::~ExtendedControllerBase() = default;
@@ -10,22 +10,18 @@ ExtendedControllerBase::~ExtendedControllerBase() = default;
ReturnValue_t ExtendedControllerBase::executeAction(ActionId_t actionId,
MessageQueueId_t commandedBy,
const uint8_t *data, size_t size) {
/* Needs to be overriden and implemented by child class. */
// Needs to be overriden and implemented by child class.
return returnvalue::OK;
}
object_id_t ExtendedControllerBase::getObjectId() const { return SystemObject::getObjectId(); }
uint32_t ExtendedControllerBase::getPeriodicOperationFrequency() const {
return this->executingTask->getPeriodMs();
}
ReturnValue_t ExtendedControllerBase::handleCommandMessage(CommandMessage *message) {
ReturnValue_t result = actionHelper.handleActionMessage(message);
if (result == returnvalue::OK) {
return result;
}
return poolManager.handleHousekeepingMessage(message);
return hkHelper.handleHousekeepingMessage(message);
}
void ExtendedControllerBase::handleQueue() {
@@ -48,7 +44,7 @@ void ExtendedControllerBase::handleQueue() {
continue;
}
result = poolManager.handleHousekeepingMessage(&command);
result = hkHelper.handleHousekeepingMessage(&command);
if (result == returnvalue::OK) {
continue;
}
@@ -72,22 +68,18 @@ ReturnValue_t ExtendedControllerBase::initialize() {
return result;
}
return poolManager.initialize(commandQueue);
}
ReturnValue_t ExtendedControllerBase::initializeAfterTaskCreation() {
return poolManager.initializeAfterTaskCreation();
return hkHelper.initialize(commandQueue);
}
ReturnValue_t ExtendedControllerBase::performOperation(uint8_t opCode) {
handleQueue();
performControlOperation(opCode);
/* We do this after performing control operation because variables will be set changed
in this function. */
poolManager.performHkOperation();
// We do this after performing control operation because variables will be set changed
// in this function.
hkHelper.performHkOperation();
return returnvalue::OK;
}
MessageQueueId_t ExtendedControllerBase::getCommandQueue() const { return commandQueue->getId(); }
LocalDataPoolManager *ExtendedControllerBase::getHkManagerHandle() { return &poolManager; }
datapool::SharedPool *ExtendedControllerBase::getOptionalSharedPool() { return nullptr; }
+16 -14
View File
@@ -1,10 +1,11 @@
#ifndef FSFW_CONTROLLER_EXTENDEDCONTROLLERBASE_H_
#define FSFW_CONTROLLER_EXTENDEDCONTROLLERBASE_H_
#include <fsfw/housekeeping/GeneratesPeriodicHkIF.h>
#include "ControllerBase.h"
#include "fsfw/action.h"
#include "fsfw/datapoollocal/HasLocalDataPoolIF.h"
#include "fsfw/datapoollocal/LocalDataPoolManager.h"
#include "fsfw/housekeeping/PeriodicHkHelper.h"
/**
* @brief Extends the basic ControllerBase with commonly used components
@@ -15,9 +16,9 @@
*/
class ExtendedControllerBase : public ControllerBase,
public HasActionsIF,
public HasLocalDataPoolIF {
public hk::GeneratesPeriodicHkIF {
public:
ExtendedControllerBase(object_id_t objectId, size_t commandQueueDepth = 3);
explicit ExtendedControllerBase(object_id_t objectId, size_t commandQueueDepth = 3);
~ExtendedControllerBase() override;
/* SystemObjectIF overrides */
@@ -27,12 +28,19 @@ class ExtendedControllerBase : public ControllerBase,
/* ExecutableObjectIF overrides */
ReturnValue_t performOperation(uint8_t opCode) override;
ReturnValue_t initializeAfterTaskCreation() override;
protected:
LocalDataPoolManager poolManager;
hk::PeriodicHelper hkHelper;
ActionHelper actionHelper;
// Periodic HK methods, default method assumes that no shared pool is required.
datapool::SharedPool* getOptionalSharedPool() override = 0;
// Periodic HK abstract methods.
ReturnValue_t serializeHkDataset(dp::sid_t structureId, uint8_t* buf,
size_t maxSize) override = 0;
ReturnValue_t specifyHkDatasets(std::vector<hk::SetSpecification>& setList) override = 0;
/**
* Implemented by child class. Handle all command messages which are
* not health, mode, action or housekeeping messages.
@@ -49,18 +57,12 @@ class ExtendedControllerBase : public ControllerBase,
// Handle the four messages mentioned above
void handleQueue() override;
/* HasActionsIF overrides */
// HasActionsIF overrides
ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy,
const uint8_t* data, size_t size) override;
/* HasLocalDatapoolIF overrides */
LocalDataPoolManager* getHkManagerHandle() override;
// HasLocalDatapoolIF overrides
[[nodiscard]] object_id_t getObjectId() const override;
[[nodiscard]] uint32_t getPeriodicOperationFrequency() const override;
ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
LocalDataPoolManager& poolManager) override = 0;
LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override = 0;
// Mode abstract functions
ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode,
+11
View File
@@ -0,0 +1,11 @@
#ifndef FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_
#define FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_
// Collected related headers
#include "fsfw/datapool/LocalPoolVector.h"
#include "fsfw/datapool/PoolVariable.h"
#include "fsfw/datapool/SharedPool.h"
#include "fsfw/datapool/SharedSet.h"
#include "fsfw/datapool/StaticSharedSet.h"
#endif /* FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_ */
+6 -1
View File
@@ -1 +1,6 @@
target_sources(${LIB_FSFW_NAME} PRIVATE PoolDataSetBase.cpp PoolEntry.cpp)
target_sources(
${LIB_FSFW_NAME}
PRIVATE PoolDataSetBase.cpp PoolEntry.cpp SharedPool.cpp SharedSet.cpp
SharedSetBase.cpp LocalPoolObjectBase.cpp)
add_subdirectory(internal)
@@ -1,37 +1,26 @@
#include "fsfw/datapoollocal/LocalPoolObjectBase.h"
#include "fsfw/datapool/LocalPoolObjectBase.h"
#include "fsfw/datapoollocal/AccessLocalPoolF.h"
#include "fsfw/datapoollocal/HasLocalDataPoolIF.h"
#include "fsfw/datapoollocal/LocalDataPoolManager.h"
#include "fsfw/housekeeping/PeriodicHkHelper.h"
#include "fsfw/objectmanager/ObjectManager.h"
#include "internal/HasLocalDpIFUserAttorney.h"
LocalPoolObjectBase::LocalPoolObjectBase(lp_id_t poolId, HasLocalDataPoolIF* hkOwner,
DataSetIF* dataSet, pool_rwm_t setReadWriteMode)
: localPoolId(poolId), readWriteMode(setReadWriteMode) {
using namespace dp;
PoolObjectBase::PoolObjectBase(SharedPool& sharedPool, dp::id_t poolId, DataSetIF* dataSet,
pool_rwm_t setReadWriteMode)
: localPoolId(poolId), readWriteMode(setReadWriteMode), sharedPool(&sharedPool) {
if (poolId == PoolVariableIF::NO_PARAMETER) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "LocalPoolVar<T>::LocalPoolVar: 0 passed as pool ID, "
<< "which is the NO_PARAMETER value!" << std::endl;
#endif
}
if (hkOwner == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "LocalPoolVar<T>::LocalPoolVar: The supplied pool "
<< "owner is a invalid!" << std::endl;
#endif
return;
}
AccessPoolManagerIF* poolManAccessor = HasLocalDpIFUserAttorney::getAccessorHandle(hkOwner);
hkManager = poolManAccessor->getPoolManagerHandle();
if (dataSet != nullptr) {
dataSet->registerVariable(this);
}
}
LocalPoolObjectBase::LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId, DataSetIF* dataSet,
pool_rwm_t setReadWriteMode)
PoolObjectBase::PoolObjectBase(object_id_t poolOwner, id_t poolId, DataSetIF* dataSet,
pool_rwm_t setReadWriteMode)
: localPoolId(poolId), readWriteMode(setReadWriteMode) {
if (poolId == PoolVariableIF::NO_PARAMETER) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
@@ -44,7 +33,7 @@ LocalPoolObjectBase::LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId,
"which is the NO_PARAMETER value!\n");
#endif
}
HasLocalDataPoolIF* hkOwner = ObjectManager::instance()->get<HasLocalDataPoolIF>(poolOwner);
auto* hkOwner = ObjectManager::instance()->get<hk::GeneratesPeriodicHkIF>(poolOwner);
if (hkOwner == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "LocalPoolVariable: The supplied pool owner 0x" << std::hex << poolOwner
@@ -58,10 +47,14 @@ LocalPoolObjectBase::LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId,
#endif
return;
}
AccessPoolManagerIF* accessor = HasLocalDpIFUserAttorney::getAccessorHandle(hkOwner);
if (accessor != nullptr) {
hkManager = accessor->getPoolManagerHandle();
sharedPool = hkOwner->getOptionalSharedPool();
if (sharedPool == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PoolObjectBase: HK owner 0x" << std::hex << poolOwner << std::dec
<< "does not have a shared pool " << std::endl;
#else
sif::printError("PoolObjectBase: HK owner 0x%08x does not have a shared pool\n", poolOwner);
#endif
}
if (dataSet != nullptr) {
@@ -69,26 +62,22 @@ LocalPoolObjectBase::LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId,
}
}
pool_rwm_t LocalPoolObjectBase::getReadWriteMode() const { return readWriteMode; }
pool_rwm_t PoolObjectBase ::getReadWriteMode() const { return readWriteMode; }
bool LocalPoolObjectBase::isValid() const { return valid; }
id_t PoolObjectBase ::getDataPoolId() const { return localPoolId; }
void LocalPoolObjectBase::setValid(bool valid) { this->valid = valid; }
void PoolObjectBase::setDataPoolId(id_t poolId) { this->localPoolId = poolId; }
lp_id_t LocalPoolObjectBase::getDataPoolId() const { return localPoolId; }
void LocalPoolObjectBase::setDataPoolId(lp_id_t poolId) { this->localPoolId = poolId; }
void LocalPoolObjectBase::setChanged(bool changed) { this->changed = changed; }
bool LocalPoolObjectBase::hasChanged() const { return changed; }
void LocalPoolObjectBase::setReadWriteMode(pool_rwm_t newReadWriteMode) {
void PoolObjectBase::setReadWriteMode(pool_rwm_t newReadWriteMode) {
this->readWriteMode = newReadWriteMode;
}
void LocalPoolObjectBase::reportReadCommitError(const char* variableType, ReturnValue_t error,
bool read, object_id_t objectId, lp_id_t lpId) {
[[nodiscard]] bool PoolObjectBase::isValid() const { return valid; }
void PoolObjectBase::setValid(bool valid) { this->valid = valid; }
void PoolObjectBase::reportReadCommitError(const char* variableType, ReturnValue_t error, bool read,
object_id_t objectId, id_t lpId) {
#if FSFW_DISABLE_PRINTOUT == 0
const char* variablePrintout = variableType;
if (variablePrintout == nullptr) {
@@ -102,9 +91,9 @@ void LocalPoolObjectBase::reportReadCommitError(const char* variableType, Return
}
const char* errMsg = nullptr;
if (error == localpool::POOL_ENTRY_NOT_FOUND) {
if (error == POOL_ENTRY_NOT_FOUND) {
errMsg = "Pool entry not found";
} else if (error == localpool::POOL_ENTRY_TYPE_CONFLICT) {
} else if (error == POOL_ENTRY_TYPE_CONFLICT) {
errMsg = "Pool entry type conflict";
} else if (error == PoolVariableIF::INVALID_READ_WRITE_MODE) {
errMsg = "Pool variable wrong read-write mode";
+55
View File
@@ -0,0 +1,55 @@
#pragma once
#include "SharedPool.h"
#include "fsfw/datapool/PoolVariableIF.h"
#include "fsfw/datapool/definitions.h"
#include "fsfw/objectmanager/SystemObjectIF.h"
#include "fsfw/returnvalues/returnvalue.h"
class DataSetIF;
namespace datapool {
/**
* @brief This class serves as a non-template base for pool objects like pool variables
* or pool vectors.
*/
class PoolObjectBase : public PoolVariableIF {
public:
PoolObjectBase(dp::SharedPool& sharedPool, dp::id_t poolId, DataSetIF* dataSet,
pool_rwm_t setReadWriteMode);
PoolObjectBase(object_id_t poolOwner, dp::id_t poolId, DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
void setReadWriteMode(pool_rwm_t newReadWriteMode) override;
[[nodiscard]] pool_rwm_t getReadWriteMode() const override;
[[nodiscard]] dp::id_t getDataPoolId() const override;
void setDataPoolId(dp::id_t poolId);
[[nodiscard]] bool isValid() const;
void setValid(bool valid);
protected:
/**
* @brief To access the correct data pool entry on read and commit calls,
* the data pool id is stored.
*/
uint32_t localPoolId = PoolVariableIF::NO_PARAMETER;
/**
* @brief The information whether the class is read-write or
* read-only is stored here.
*/
ReadWriteMode_t readWriteMode = pool_rwm_t::VAR_READ_WRITE;
bool valid = false;
//! @brief Pointer to the class which manages the HK pool.
dp::SharedPool* sharedPool = nullptr;
void reportReadCommitError(const char* variableType, ReturnValue_t error, bool read,
object_id_t objectId, dp::id_t lpId);
};
} // namespace datapool
@@ -1,14 +1,15 @@
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_
#define FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_
#pragma once
#include <fsfw/ipc/MutexGuard.h>
#include "../datapool/DataSetIF.h"
#include "../datapool/PoolEntry.h"
#include "../datapool/PoolVariableIF.h"
#include "../datapoollocal/LocalDataPoolManager.h"
#include "../serialize/SerializeAdapter.h"
#include "../serviceinterface/ServiceInterface.h"
#include "LocalPoolObjectBase.h"
#include "internal/LocalDpManagerAttorney.h"
#include "fsfw/datapool/DataSetIF.h"
#include "fsfw/datapool/PoolEntry.h"
#include "fsfw/datapool/PoolVariableIF.h"
#include "fsfw/serialize/SerializeAdapter.h"
#include "fsfw/serviceinterface/ServiceInterface.h"
namespace datapool {
/**
* @brief This is the access class for array-type data pool entries.
@@ -24,16 +25,16 @@
* @tparam T
* This template parameter specifies the data type of an array entry. Currently,
* all plain data types are supported, but in principle any type is possible.
* @tparam vector_size
* @tparam N
* This template parameter specifies the vector size of this entry. Using a
* template parameter for this is not perfect, but avoids
* dynamic memory allocation.
* @ingroup data_pool
*/
template <typename T, uint16_t vectorSize>
class LocalPoolVector : public LocalPoolObjectBase {
template <typename T, size_t N>
class PoolVector : public PoolObjectBase {
public:
LocalPoolVector() = delete;
PoolVector() = delete;
/**
* This constructor is used by the data creators to have pool variable
* instances which can also be stored in datasets.
@@ -47,8 +48,9 @@ class LocalPoolVector : public LocalPoolObjectBase {
* @param dataSet The data set in which the variable shall register itself.
* If nullptr, the variable is not registered.
*/
LocalPoolVector(HasLocalDataPoolIF* hkOwner, lp_id_t poolId, DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
PoolVector(SharedPool& sharedPool, id_t poolId, DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE)
: PoolObjectBase(sharedPool, poolId, dataSet, setReadWriteMode) {}
/**
* This constructor is used by data users like controllers to have
@@ -57,15 +59,15 @@ class LocalPoolVector : public LocalPoolObjectBase {
* It does not fetch the current value from the data pool. This is performed
* by the read() operation (which is not thread-safe).
* Datasets can be used to access local pool entires in a thread-safe way.
* @param poolOwner Owner of the shared pool.
* @param poolId ID of the local pool entry.
* @param hkOwner Pointer of the owner. This will generally be the calling
* class itself which passes "this".
* @param setReadWriteMode Specify the read-write mode of the pool variable.
* @param dataSet The data set in which the variable shall register itself.
* If nullptr, the variable is not registered.
*/
LocalPoolVector(object_id_t poolOwner, lp_id_t poolId, DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
PoolVector(object_id_t poolOwner, id_t poolId, DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE)
: PoolObjectBase(poolOwner, poolId, dataSet, setReadWriteMode) {}
/**
* Variation which takes the unique global identifier of a local pool
* vector.
@@ -73,8 +75,10 @@ class LocalPoolVector : public LocalPoolObjectBase {
* @param dataSet
* @param setReadWriteMode
*/
LocalPoolVector(gp_id_t globalPoolId, DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
PoolVector(g_id_t globalPoolId, DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE)
: PoolObjectBase(globalPoolId.objectId, globalPoolId.localPoolId, dataSet, setReadWriteMode) {
}
/**
* @brief This is the local copy of the data pool entry.
@@ -82,27 +86,80 @@ class LocalPoolVector : public LocalPoolObjectBase {
* The user can work on this attribute just like he would on a local
* array of this type.
*/
T value[vectorSize] = {};
T value[N] = {};
/**
* @brief The classes destructor is empty.
* @details If commit() was not called, the local value is
* discarded and not written back to the data pool.
*/
~LocalPoolVector() {};
~PoolVector() override {};
/**
* @brief The operation returns the number of array entries
* in this variable.
*/
uint8_t getSize() { return vectorSize; }
size_t getSize() { return N; }
T& operator[](size_t i);
const T& operator[](size_t i) const;
T& operator[](size_t i) {
if (i < N) {
return value[i];
}
// If this happens, I have to set some value. I consider this
// a configuration error, but I wont exit here.
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "PoolVector: Invalid index. Setting or returning"
" last value!"
<< std::endl;
#else
sif::printWarning(
"PoolVector: Invalid index. Setting or returning"
" last value!\n");
#endif
return value[N - 1];
}
const T& operator[](size_t i) const {
if (i < N) {
return value[i];
}
// If this happens, I have to set some value. I consider this
// a configuration error, but I wont exit here.
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "PoolVector: Invalid index. Setting or returning"
" last value!"
<< std::endl;
#else
sif::printWarning(
"PoolVector: Invalid index. Setting or returning"
" last value!\n");
#endif
return value[N - 1];
}
ReturnValue_t serialize(uint8_t** buffer, size_t* size, const size_t maxSize,
SerializeIF::Endianness streamEndiannes) const override;
size_t getSerializedSize() const override;
SerializeIF::Endianness streamEndianness) const override {
ReturnValue_t result = returnvalue::FAILED;
for (uint16_t i = 0; i < N; i++) {
result = SerializeAdapter::serialize(&(value[i]), buffer, size, maxSize, streamEndianness);
if (result != returnvalue::OK) {
break;
}
}
return result;
}
[[nodiscard]] size_t getSerializedSize() const override {
return N * SerializeAdapter::getSerializedSize(value);
}
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
SerializeIF::Endianness streamEndianness) override;
SerializeIF::Endianness streamEndianness) override {
ReturnValue_t result = returnvalue::FAILED;
for (uint16_t i = 0; i < N; i++) {
result = SerializeAdapter::deSerialize(&(value[i]), buffer, size, streamEndianness);
if (result != returnvalue::OK) {
break;
}
}
return result;
}
/**
* @brief This is a call to read the array's values
@@ -118,7 +175,10 @@ class LocalPoolVector : public LocalPoolObjectBase {
* at once to avoid the overhead of unnecessary lock und unlock operations.
*/
ReturnValue_t read(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20) override;
uint32_t timeoutMs = 20) override {
MutexGuard mg(sharedPool->getPoolMutex(), timeoutType, timeoutMs);
return readWithoutLock();
}
/**
* @brief The commit call copies the array values back to the data pool.
@@ -129,15 +189,11 @@ class LocalPoolVector : public LocalPoolObjectBase {
* It is recommended to use DataSets to read and commit multiple variables
* at once to avoid the overhead of unnecessary lock und unlock operations.
*/
ReturnValue_t commit(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20) override;
/**
* @brief This commit call also sets the validity of the pool entry.
* @details
*/
ReturnValue_t commit(bool valid, MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20);
ReturnValue_t commit(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) override {
MutexGuard mg(sharedPool->getPoolMutex(), timeoutType, timeoutMs);
return commitWithoutLock();
}
ReturnValue_t commit() { return commit(MutexIF::TimeoutType::WAITING, 20); }
protected:
/**
@@ -148,7 +204,23 @@ class LocalPoolVector : public LocalPoolObjectBase {
* of consecutive lock und unlock operations.
* Declared protected to discourage free public usage.
*/
ReturnValue_t readWithoutLock() override;
ReturnValue_t readWithoutLock() override {
if (readWriteMode == pool_rwm_t::VAR_WRITE) {
return PoolVariableIF::INVALID_READ_WRITE_MODE;
}
PoolEntry<T>* poolEntry = nullptr;
ReturnValue_t result = sharedPool->fetchPoolEntry(localPoolId, &poolEntry);
memset(this->value, 0, N * sizeof(T));
if (result != returnvalue::OK) {
return result;
}
this->valid = poolEntry->getValid();
std::memcpy(this->value, poolEntry->getDataPtr(), poolEntry->getByteSize());
return returnvalue::OK;
}
/**
* @brief Like #commit, but without a lock protection of the global pool.
* @details
@@ -157,19 +229,37 @@ class LocalPoolVector : public LocalPoolObjectBase {
* of consecutive lock und unlock operations.
* Declared protected to discourage free public usage.
*/
ReturnValue_t commitWithoutLock() override;
ReturnValue_t commitWithoutLock() override {
if (readWriteMode == pool_rwm_t::VAR_READ) {
return PoolVariableIF::INVALID_READ_WRITE_MODE;
}
PoolEntry<T>* poolEntry = nullptr;
ReturnValue_t result = sharedPool->fetchPoolEntry(localPoolId, &poolEntry);
if (result != returnvalue::OK) {
return result;
}
poolEntry->setValid(this->valid);
std::memcpy(poolEntry->getDataPtr(), this->value, poolEntry->getByteSize());
return returnvalue::OK;
}
private:
#if FSFW_CPP_OSTREAM_ENABLED == 1
// std::ostream is the type for object std::cout
template <typename U, uint16_t otherSize>
friend std::ostream& operator<<(std::ostream& out, const LocalPoolVector<U, otherSize>& var);
std::ostream& operator<<(std::ostream& out, const PoolVector<T, N>& var) {
out << "Vector: [";
for (int i = 0; i < N; i++) {
out << var.value[i];
if (i < N - 1) {
out << ", ";
}
}
out << "]";
return out;
}
#endif
};
#include "LocalPoolVector.tpp"
template <typename T, size_t N>
using vec_t = PoolVector<T, N>;
template <typename T, uint16_t vectorSize>
using lp_vec_t = LocalPoolVector<T, vectorSize>;
#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ */
} // namespace datapool
Whitespace-only changes.
File renamed without changes.
+68 -7
View File
@@ -1,15 +1,19 @@
#include "fsfw/datapool/PoolDataSetBase.h"
#include <cmath>
#include <cstring>
#include "fsfw/datapool/ReadCommitIFAttorney.h"
#include "fsfw/globalfunctions/bitutility.h"
#include "fsfw/serviceinterface/ServiceInterface.h"
PoolDataSetBase::PoolDataSetBase(PoolVariableIF** registeredVariablesArray,
const size_t maxFillCount)
: registeredVariables(registeredVariablesArray), maxFillCount(maxFillCount) {}
const size_t maxFillCount, bool serializeWithValidityBlob)
: serializeWithValidityBlob(serializeWithValidityBlob),
registeredVariables(registeredVariablesArray),
maxFillCount(maxFillCount) {}
PoolDataSetBase::~PoolDataSetBase() {}
PoolDataSetBase::~PoolDataSetBase() = default;
ReturnValue_t PoolDataSetBase::registerVariable(PoolVariableIF* variable) {
if (registeredVariables == nullptr) {
@@ -82,19 +86,19 @@ uint16_t PoolDataSetBase::getFillCount() const { return fillCount; }
ReturnValue_t PoolDataSetBase::readVariable(uint16_t count) {
ReturnValue_t result = returnvalue::OK;
if (registeredVariables[count] == nullptr) {
/* Configuration error. */
// Configuration error.
return returnvalue::FAILED;
}
/* These checks are often performed by the respective variable implementation too, but I guess
a double check does not hurt. */
// These checks are often performed by the respective variable implementation too, but I guess
// a double check does not hurt.
if (registeredVariables[count]->getReadWriteMode() != PoolVariableIF::VAR_WRITE and
registeredVariables[count]->getDataPoolId() != PoolVariableIF::NO_PARAMETER) {
if (protectEveryReadCommitCall) {
result =
registeredVariables[count]->read(timeoutTypeForSingleVars, mutexTimeoutForSingleVars);
} else {
/* The readWithoutLock function is protected, so we use the attorney here */
// The readWithoutLock function is protected, so we use the attorney here
result = ReadCommitIFAttorney::readWithoutLock(registeredVariables[count]);
}
@@ -172,6 +176,9 @@ ReturnValue_t PoolDataSetBase::unlockDataPool() { return returnvalue::OK; }
ReturnValue_t PoolDataSetBase::serialize(uint8_t** buffer, size_t* size, const size_t maxSize,
SerializeIF::Endianness streamEndianness) const {
if (this->serializeWithValidityBlob) {
return doSerializeWithValidityBlob(buffer, size, maxSize, streamEndianness);
}
ReturnValue_t result = returnvalue::FAILED;
for (uint16_t count = 0; count < fillCount; count++) {
result = registeredVariables[count]->serialize(buffer, size, maxSize, streamEndianness);
@@ -182,6 +189,51 @@ ReturnValue_t PoolDataSetBase::serialize(uint8_t** buffer, size_t* size, const s
return result;
}
ReturnValue_t PoolDataSetBase::doSerializeWithValidityBlob(
uint8_t** buffer, size_t* size, const size_t maxSize,
SerializeIF::Endianness streamEndianness) const {
ReturnValue_t result = returnvalue::FAILED;
const uint8_t validityMaskSize = std::ceil(static_cast<float>(fillCount) / 8.0);
uint8_t* validityPtr = nullptr;
#if defined(_MSC_VER) || defined(__clang__)
// Use a std::vector here because MSVC will (rightly) not create a fixed size array
// with a non constant size specifier. The Apple compiler (LLVM) will not accept
// the initialization of a variable sized array
std::vector<uint8_t> validityMask(validityMaskSize, 0);
validityPtr = validityMask.data();
#else
uint8_t validityMask[validityMaskSize] = {};
validityPtr = validityMask;
#endif
uint8_t validBufferIndex = 0;
uint8_t validBufferIndexBit = 0;
for (uint16_t count = 0; count < fillCount; count++) {
if (registeredVariables[count]->isValid()) {
// Set bit at correct position
bitutil::set(validityPtr + validBufferIndex, validBufferIndexBit);
}
if (validBufferIndexBit == 7) {
validBufferIndex++;
validBufferIndexBit = 0;
} else {
validBufferIndexBit++;
}
result = registeredVariables[count]->serialize(buffer, size, maxSize, streamEndianness);
if (result != returnvalue::OK) {
return result;
}
}
if (*size + validityMaskSize > maxSize) {
return SerializeIF::BUFFER_TOO_SHORT;
}
// copy validity buffer to end
std::memcpy(*buffer, validityPtr, validityMaskSize);
*size += validityMaskSize;
return result;
}
ReturnValue_t PoolDataSetBase::deSerialize(const uint8_t** buffer, size_t* size,
SerializeIF::Endianness streamEndianness) {
ReturnValue_t result = returnvalue::FAILED;
@@ -199,6 +251,9 @@ size_t PoolDataSetBase::getSerializedSize() const {
for (uint16_t count = 0; count < fillCount; count++) {
size += registeredVariables[count]->getSerializedSize();
}
if (serializeWithValidityBlob) {
size += std::ceil(static_cast<float>(fillCount) / 8.0);
}
return size;
}
@@ -215,3 +270,9 @@ void PoolDataSetBase::setReadCommitProtectionBehaviour(bool protectEveryReadComm
this->timeoutTypeForSingleVars = timeoutType;
this->mutexTimeoutForSingleVars = mutexTimeout;
}
void PoolDataSetBase::setChildrenValidity(bool valid) {
for (uint16_t count = 0; count < fillCount; count++) {
registeredVariables[count]->setValid(valid);
}
}
+29 -17
View File
@@ -1,11 +1,14 @@
#ifndef FSFW_DATAPOOL_POOLDATASETBASE_H_
#define FSFW_DATAPOOL_POOLDATASETBASE_H_
#pragma once
#include "PoolDataSetIF.h"
#include "PoolVariableIF.h"
#include "fsfw/ipc/MutexIF.h"
#include "fsfw/serialize/SerializeIF.h"
namespace datapool {
class SharedSetBase;
}
/**
* @brief The DataSetBase class manages a set of locally checked out variables.
* @details
@@ -30,13 +33,16 @@
* @ingroup data_pool
*/
class PoolDataSetBase : public PoolDataSetIF, public SerializeIF {
friend class datapool::SharedSetBase;
public:
/**
* @brief Creates an empty dataset. Use registerVariable or
* supply a pointer to this dataset to PoolVariable
* initializations to register pool variables.
*/
PoolDataSetBase(PoolVariableIF** registeredVariablesArray, size_t maxFillCount);
PoolDataSetBase(PoolVariableIF** registeredVariablesArray, size_t maxFillCount,
bool serializeWithValidityBlob = true);
/* Forbidden for now */
PoolDataSetBase(const PoolDataSetBase& otherSet) = delete;
@@ -99,25 +105,26 @@ class PoolDataSetBase : public PoolDataSetIF, public SerializeIF {
* thread-safety. Default implementation is empty
* @return Always returns -@c returnvalue::OK
*/
virtual ReturnValue_t lockDataPool(
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20) override;
ReturnValue_t lockDataPool(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20) override;
/**
* Provides the means to unlock the underlying data structure to ensure
* thread-safety. Default implementation is empty
* @return Always returns -@c returnvalue::OK
*/
virtual ReturnValue_t unlockDataPool() override;
ReturnValue_t unlockDataPool() override;
virtual uint16_t getFillCount() const override;
[[nodiscard]] uint16_t getFillCount() const override;
/* SerializeIF implementations */
virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, const size_t maxSize,
SerializeIF::Endianness streamEndianness) const override;
virtual size_t getSerializedSize() const override;
virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
SerializeIF::Endianness streamEndianness) override;
// SerializeIF implementations
ReturnValue_t serialize(uint8_t** buffer, size_t* size, const size_t maxSize,
SerializeIF::Endianness streamEndianness) const override;
[[nodiscard]] size_t getSerializedSize() const override;
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
SerializeIF::Endianness streamEndianness) override;
ReturnValue_t doSerializeWithValidityBlob(uint8_t** buffer, size_t* size, const size_t maxSize,
SerializeIF::Endianness streamEndianness) const;
/**
* Can be used to individually protect every read and commit call.
* @param protectEveryReadCommit
@@ -127,6 +134,13 @@ class PoolDataSetBase : public PoolDataSetIF, public SerializeIF {
bool protectEveryReadCommit, MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t mutexTimeout = 20);
/**
* Set the validity of all children
*/
void setChildrenValidity(bool valid);
bool serializeWithValidityBlob = false;
protected:
/**
* @brief The fill_count attribute ensures that the variables
@@ -168,6 +182,4 @@ class PoolDataSetBase : public PoolDataSetIF, public SerializeIF {
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, uint32_t timeoutMs = 20);
ReturnValue_t handleUnreadDatasetCommit(
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, uint32_t timeoutMs = 20);
};
#endif /* FSFW_DATAPOOL_POOLDATASETBASE_H_ */
};
+2 -6
View File
@@ -10,7 +10,7 @@
*/
class PoolDataSetIF : virtual public DataSetIF, virtual public ReadCommitIF {
public:
virtual ~PoolDataSetIF() {};
~PoolDataSetIF() override = default;
/**
* @brief Most underlying data structures will have a pool like structure
@@ -18,17 +18,13 @@ class PoolDataSetIF : virtual public DataSetIF, virtual public ReadCommitIF {
* thread-safety
* @return Lock operation result
*/
virtual ReturnValue_t lockDataPool(
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20) = 0;
virtual ReturnValue_t lockDataPool(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) = 0;
/**
* @brief Unlock call corresponding to the lock call.
* @return Unlock operation result
*/
virtual ReturnValue_t unlockDataPool() = 0;
virtual bool isValid() const = 0;
};
#endif /* FSFW_DATAPOOL_POOLDATASETIF_H_ */
+4 -2
View File
@@ -104,16 +104,18 @@ class PoolEntry : public PoolEntryIF {
* @brief This operation returns a the address pointer casted to void*.
*/
void* getRawData();
/**
* @brief This method allows to set the valid information
* of the pool entry.
*/
void setValid(bool isValid);
void setValid(bool isValid) override;
/**
* @brief This method allows to get the valid information
* of the pool entry.
*/
bool getValid();
bool getValid() override;
/**
* @brief This is a debug method that prints all values and the valid
* information to the screen. It prints all array entries in a row.
+10 -8
View File
@@ -39,14 +39,7 @@ class PoolEntryIF {
* @brief This operation returns a the address pointer casted to void*.
*/
virtual void* getRawData() = 0;
/**
* @brief This method allows to set the valid information of the pool entry.
*/
virtual void setValid(bool isValid) = 0;
/**
* @brief This method allows to set the valid information of the pool entry.
*/
virtual bool getValid() = 0;
/**
* @brief This is a debug method that prints all values and the valid
* information to the screen. It prints all array entries in a row.
@@ -58,6 +51,15 @@ class PoolEntryIF {
* Returns the type of the entry.
*/
virtual Type getType() = 0;
/**
* @brief This method allows to set the valid information of the pool entry.
*/
virtual void setValid(bool isValid) = 0;
/**
* @brief This method allows to set the valid information of the pool entry.
*/
virtual bool getValid() = 0;
};
#endif /* FSFW_DATAPOOL_POOLENTRYIF_H_ */
+2 -2
View File
@@ -20,9 +20,9 @@ class PoolReadGuard {
if (readResult != returnvalue::OK) {
#if FSFW_VERBOSE_LEVEL == 1
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PoolReadHelper: Read failed!" << std::endl;
sif::error << "PoolReadGuard: Read failed!" << std::endl;
#else
sif::printError("PoolReadHelper: Read failed!\n");
sif::printError("PoolReadGuard: Read failed!\n");
#endif /* FSFW_PRINT_VERBOSITY_LEVEL == 1 */
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
}
+350
View File
@@ -0,0 +1,350 @@
#pragma once
#include <fsfw/datapool/internal/SharedPoolAttorney.h>
#include "FSFWConfig.h"
#include "LocalPoolObjectBase.h"
#include "fsfw/datapool/DataSetIF.h"
#include "fsfw/datapool/PoolVariableIF.h"
#include "fsfw/serialize/SerializeAdapter.h"
namespace datapool {
/**
* @brief Local Pool Variable class which is used to access the local pools.
* @details
* This class is not stored in the map. Instead, it is used to access
* the pool entries by using a pointer to the map storing the pool
* entries. It can also be used to organize these pool entries into data sets.
*
* @tparam T The template parameter sets the type of the variable. Currently,
* all plain data types are supported, but in principle any type is possible.
* @ingroup data_pool
*/
template <typename T>
class PoolVariable : public PoolObjectBase {
public:
//! Default ctor is forbidden.
PoolVariable() = delete;
/**
* This constructor is used by the data creators to have pool variable
* instances which can also be stored in datasets.
*
* It does not fetch the current value from the data pool, which
* has to be done by calling the read() operation.
* Datasets can be used to access multiple local pool entries in an
* efficient way. A pointer to a dataset can be passed to register
* the pool variable in that dataset directly.
* @param poolId ID of the local pool entry.
* @param hkOwner Pointer of the owner. This will generally be the calling
* class itself which passes "this".
* @param dataSet The data set in which the variable shall register itself.
* If nullptr, the variable is not registered.
* @param setReadWriteMode Specify the read-write mode of the pool variable.
*/
PoolVariable(SharedPool& sharedPool, dp::id_t poolId, DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
/**
* This constructor is used by data users like controllers to have
* access to the local pool variables of data creators by supplying
* the respective creator object ID.
*
* It does not fetch the current value from the data pool, which
* has to be done by calling the read() operation.
* Datasets can be used to access multiple local pool entries in an
* efficient way. A pointer to a dataset can be passed to register
* the pool variable in that dataset directly.
* @param poolId ID of the local pool entry.
* @param hkOwner object ID of the pool owner.
* @param dataSet The data set in which the variable shall register itself.
* If nullptr, the variable is not registered.
* @param setReadWriteMode Specify the read-write mode of the pool variable.
*
*/
PoolVariable(object_id_t poolOwner, dp::id_t poolId, DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
/**
* Variation which takes the global unique identifier of a pool variable.
* @param globalPoolId
* @param dataSet
* @param setReadWriteMode
*/
PoolVariable(g_id_t globalPoolId, DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
virtual ~PoolVariable() {};
T get() const;
/**
* @brief This is the local copy of the data pool entry.
* @details The user can work on this attribute
* just like he would on a simple local variable.
*/
T value = 0;
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
SerializeIF::Endianness streamEndianness) const override;
size_t getSerializedSize() const override;
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
SerializeIF::Endianness streamEndianness) override;
/**
* @brief This is a call to read the array's values
* from the global data pool.
* @details
* When executed, this operation tries to fetch the pool entry with matching
* data pool id from the data pool and copies all array values and the valid
* information to its local attributes.
* In case of a failure (wrong type, size or pool id not found), the
* variable is set to zero and invalid.
* The read call is protected with a lock.
* It is recommended to use DataSets to read and commit multiple variables
* at once to avoid the overhead of unnecessary lock und unlock operations.
*
*/
ReturnValue_t read(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20) override;
/**
* @brief The commit call copies the array values back to the data pool.
* @details
* It checks type and size, as well as if the variable is writable. If so,
* the value is copied and the local valid flag is written back as well.
* The read call is protected with a lock.
* It is recommended to use DataSets to read and commit multiple variables
* at once to avoid the overhead of unnecessary lock und unlock operations.
*/
ReturnValue_t commit(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20) override;
PoolVariable<T>& operator=(const T& newValue);
PoolVariable<T>& operator=(const PoolVariable<T>& newPoolVariable);
//! Explicit type conversion operator. Allows casting the class to
//! its template type to perform operations on value.
explicit operator T() const;
bool operator==(const PoolVariable<T>& other) const;
bool operator==(const T& other) const;
bool operator!=(const PoolVariable<T>& other) const;
bool operator!=(const T& other) const;
bool operator<(const PoolVariable<T>& other) const;
bool operator<(const T& other) const;
bool operator>(const PoolVariable<T>& other) const;
bool operator>(const T& other) const;
protected:
/**
* @brief Like #read, but without a lock protection of the global pool.
* @details
* The operation does NOT provide any mutual exclusive protection by itself.
* This can be used if the lock is handled externally to avoid the overhead
* of consecutive lock und unlock operations.
* Declared protected to discourage free public usage.
*/
ReturnValue_t readWithoutLock() override;
/**
* @brief Like #commit, but without a lock protection of the global pool.
* @details
* The operation does NOT provide any mutual exclusive protection by itself.
* This can be used if the lock is handled externally to avoid the overhead
* of consecutive lock und unlock operations.
* Declared protected to discourage free public usage.
*/
ReturnValue_t commitWithoutLock() override;
#if FSFW_CPP_OSTREAM_ENABLED == 1
// std::ostream is the type for object std::cout
template <typename U>
friend std::ostream& operator<<(std::ostream& out, const LocalPoolVariable<U>& var);
#endif
};
template <typename T>
PoolVariable<T>::PoolVariable(SharedPool& sharedPool, dp::id_t poolId, DataSetIF* dataSet,
pool_rwm_t setReadWriteMode)
: PoolObjectBase(sharedPool, poolId, dataSet, setReadWriteMode) {}
template <typename T>
PoolVariable<T>::PoolVariable(object_id_t poolOwner, dp::id_t poolId, DataSetIF* dataSet,
pool_rwm_t setReadWriteMode)
: PoolObjectBase(poolOwner, poolId, dataSet, setReadWriteMode) {}
template <typename T>
PoolVariable<T>::PoolVariable(g_id_t globalPoolId, DataSetIF* dataSet, pool_rwm_t setReadWriteMode)
: PoolObjectBase(globalPoolId.objectId, globalPoolId.localPoolId, dataSet, setReadWriteMode) {}
template <typename T>
ReturnValue_t PoolVariable<T>::read(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
if (sharedPool == nullptr) {
return readWithoutLock();
}
MutexIF* mutex = sharedPool->getPoolMutex();
ReturnValue_t result = mutex->lockMutex(timeoutType, timeoutMs);
if (result != returnvalue::OK) {
return result;
}
result = readWithoutLock();
mutex->unlockMutex();
return result;
}
template <typename T>
inline ReturnValue_t PoolVariable<T>::readWithoutLock() {
if (sharedPool == nullptr) {
return PoolVariableIF::INVALID_SHARED_POOL;
}
if (readWriteMode == pool_rwm_t::VAR_WRITE) {
return PoolVariableIF::INVALID_READ_WRITE_MODE;
}
PoolEntry<T>* poolEntry = nullptr;
if (ReturnValue_t result = sharedPool->fetchPoolEntry(localPoolId, &poolEntry);
result != returnvalue::OK) {
return result;
}
this->value = *(poolEntry->getDataPtr());
this->valid = poolEntry->getValid();
return returnvalue::OK;
}
template <typename T>
inline ReturnValue_t PoolVariable<T>::commit(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
if (sharedPool == nullptr) {
return commitWithoutLock();
}
MutexIF* mutex = sharedPool->getPoolMutex();
ReturnValue_t result = mutex->lockMutex(timeoutType, timeoutMs);
if (result != returnvalue::OK) {
return result;
}
result = commitWithoutLock();
mutex->unlockMutex();
return result;
}
template <typename T>
ReturnValue_t PoolVariable<T>::commitWithoutLock() {
if (readWriteMode == pool_rwm_t::VAR_READ) {
return PoolVariableIF::INVALID_READ_WRITE_MODE;
}
PoolEntry<T>* poolEntry = nullptr;
ReturnValue_t result = sharedPool->fetchPoolEntry(localPoolId, &poolEntry);
if (result != returnvalue::OK) {
return result;
}
*(poolEntry->getDataPtr()) = this->value;
poolEntry->setValid(this->valid);
return returnvalue::OK;
}
template <typename T>
ReturnValue_t PoolVariable<T>::serialize(uint8_t** buffer, size_t* size, const size_t max_size,
SerializeIF::Endianness streamEndianness) const {
return SerializeAdapter::serialize(&value, buffer, size, max_size, streamEndianness);
}
template <typename T>
size_t PoolVariable<T>::getSerializedSize() const {
return SerializeAdapter::getSerializedSize(&value);
}
template <typename T>
ReturnValue_t PoolVariable<T>::deSerialize(const uint8_t** buffer, size_t* size,
SerializeIF::Endianness streamEndianness) {
return SerializeAdapter::deSerialize(&value, buffer, size, streamEndianness);
}
template <typename T>
T PoolVariable<T>::get() const {
return value;
}
#if FSFW_CPP_OSTREAM_ENABLED == 1
template <typename T>
inline std::ostream& operator<<(std::ostream& out, const PoolVariable<T>& var) {
out << var.value;
return out;
}
#endif
template <typename T>
PoolVariable<T>::operator T() const {
return value;
}
template <typename T>
PoolVariable<T>& PoolVariable<T>::operator=(const T& newValue) {
value = newValue;
return *this;
}
template <typename T>
PoolVariable<T>& PoolVariable<T>::operator=(const PoolVariable<T>& newPoolVariable) {
value = newPoolVariable.value;
return *this;
}
template <typename T>
bool PoolVariable<T>::operator==(const PoolVariable<T>& other) const {
return this->value == other.value;
}
template <typename T>
bool PoolVariable<T>::operator==(const T& other) const {
return this->value == other;
}
template <typename T>
bool PoolVariable<T>::operator!=(const PoolVariable& other) const {
return not(*this == other);
}
template <typename T>
bool PoolVariable<T>::operator!=(const T& other) const {
return not(*this == other);
}
template <typename T>
bool PoolVariable<T>::operator<(const PoolVariable<T>& other) const {
return this->value < other.value;
}
template <typename T>
bool PoolVariable<T>::operator<(const T& other) const {
return this->value < other;
}
template <typename T>
bool PoolVariable<T>::operator>(const PoolVariable<T>& other) const {
return not(*this < other);
}
template <typename T>
bool PoolVariable<T>::operator>(const T& other) const {
return not(*this < other);
}
template <class T>
using var_t = PoolVariable<T>;
using bool_t = PoolVariable<uint8_t>;
using u8_t = PoolVariable<uint8_t>;
using u16_t = PoolVariable<uint16_t>;
using u32_t = PoolVariable<uint32_t>;
using u64_t = PoolVariable<uint64_t>;
using i8_t = PoolVariable<int8_t>;
using i16_t = PoolVariable<int16_t>;
using i32_t = PoolVariable<int32_t>;
using i64_t = PoolVariable<int64_t>;
using f32_t = PoolVariable<float>;
using f64_t = PoolVariable<double>;
} // namespace datapool
+1
View File
@@ -0,0 +1 @@
#pragma once
+3 -8
View File
@@ -23,6 +23,7 @@ class PoolVariableIF : public SerializeIF, public ReadCommitIF {
static constexpr uint8_t INTERFACE_ID = CLASS_ID::POOL_VARIABLE_IF;
static constexpr ReturnValue_t INVALID_READ_WRITE_MODE = MAKE_RETURN_CODE(0xA0);
static constexpr ReturnValue_t INVALID_POOL_ENTRY = MAKE_RETURN_CODE(0xA1);
static constexpr ReturnValue_t INVALID_SHARED_POOL = MAKE_RETURN_CODE(0xA2);
static constexpr bool VALID = 1;
static constexpr bool INVALID = 0;
@@ -46,15 +47,9 @@ class PoolVariableIF : public SerializeIF, public ReadCommitIF {
* @brief This operation shall return the data pool id of the variable.
*/
virtual uint32_t getDataPoolId() const = 0;
/**
* @brief With this call, the valid information of the
* variable is returned.
*/
virtual bool isValid() const = 0;
/**
* @brief With this call, the valid information of the variable is set.
*/
virtual void setValid(bool validity) = 0;
virtual void setValid(bool valid) = 0;
};
using pool_rwm_t = PoolVariableIF::ReadWriteMode_t;
+36
View File
@@ -0,0 +1,36 @@
#include "SharedPool.h"
#include "FSFWConfig.h"
#include "fsfw/ipc/MutexFactory.h"
#include "fsfw/serviceinterface.h"
using namespace dp;
SharedPool::SharedPool(object_id_t ownerId) : ownerId(ownerId) {
mutex = MutexFactory::instance()->createMutex();
if (mutex == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "localpool::Manager: Mutex creation failed" << std::endl;
#else
sif::printError("localpool::Manager: Mutex creation failed");
#endif
}
}
SharedPool::~SharedPool() { MutexFactory::instance()->deleteMutex(mutex); }
object_id_t SharedPool::getOwnerId() const { return ownerId; }
void SharedPool::addPoolEntry(id_t poolId, PoolEntryIF* entry) {
localPoolMap.emplace(poolId, entry);
}
MutexIF* SharedPool::getPoolMutex() { return mutex; }
ReturnValue_t SharedPool::printPoolEntry(id_t localPoolId) {
auto poolIter = localPoolMap.find(localPoolId);
if (poolIter == localPoolMap.end()) {
return POOL_ENTRY_NOT_FOUND;
}
poolIter->second->print();
return returnvalue::OK;
}
+62
View File
@@ -0,0 +1,62 @@
#pragma once
#include "fsfw/datapool/PoolEntry.h"
#include "fsfw/datapool/definitions.h"
#include "fsfw/ipc/MutexIF.h"
namespace datapool {
class SharedPool {
public:
explicit SharedPool(object_id_t ownerId);
~SharedPool();
[[nodiscard]] object_id_t getOwnerId() const;
/**
* Read a variable by supplying its local pool ID and assign the pool
* entry to the supplied PoolEntry pointer. The type of the pool entry
* is deduced automatically. This call is not thread-safe!
* For now, only classes designated by the LocalDpManagerAttorney may use this function.
* @tparam T Type of the pool entry
* @param localPoolId Pool ID of the variable to read
* @param poolVar [out] Corresponding pool entry will be assigned to the
* supplied pointer.
* @return
*/
template <class T>
ReturnValue_t fetchPoolEntry(id_t localPoolId, PoolEntry<T>** poolEntry);
ReturnValue_t printPoolEntry(id_t localPoolId);
void addPoolEntry(id_t poolId, PoolEntryIF* entry);
MutexIF* getPoolMutex();
private:
object_id_t ownerId;
// Core data structure for the actual pool data
DataPool localPoolMap{};
// Every housekeeping data manager has a mutex to protect access
// to it's data pool.
MutexIF* mutex = nullptr;
};
template <class T>
inline ReturnValue_t datapool::SharedPool::fetchPoolEntry(id_t localPoolId,
PoolEntry<T>** poolEntry) {
if (poolEntry == nullptr) {
return returnvalue::FAILED;
}
auto poolIter = localPoolMap.find(localPoolId);
if (poolIter == localPoolMap.end()) {
return POOL_ENTRY_NOT_FOUND;
}
*poolEntry = dynamic_cast<PoolEntry<T>*>(poolIter->second);
if (*poolEntry == nullptr) {
return POOL_ENTRY_TYPE_CONFLICT;
}
return returnvalue::OK;
}
} // namespace datapool
+19
View File
@@ -0,0 +1,19 @@
#include "SharedSet.h"
using namespace dp;
SharedSet::SharedSet(dp::SharedPool& sharedPool, uint32_t setId, const size_t maxNumberOfVariables,
bool serializeWithValidityBlob)
: SharedSetBase(sharedPool, setId, nullptr, maxNumberOfVariables, serializeWithValidityBlob),
poolVarList(maxNumberOfVariables) {
this->setContainer(poolVarList.data());
}
SharedSet::SharedSet(dp::structure_id_t sid, const size_t maxNumberOfVariables,
bool serializeWithValidityBlob)
: SharedSetBase(sid, nullptr, maxNumberOfVariables, serializeWithValidityBlob),
poolVarList(maxNumberOfVariables) {
this->setContainer(poolVarList.data());
}
SharedSet::~SharedSet() = default;
@@ -1,10 +1,11 @@
#ifndef FSFW_DATAPOOLLOCAL_LOCALDATASET_H_
#define FSFW_DATAPOOLLOCAL_LOCALDATASET_H_
#pragma once
#include <vector>
#include "LocalPoolDataSetBase.h"
#include "SharedPool.h"
#include "SharedSetBase.h"
namespace datapool {
/**
* @brief This dataset type can be used to group related pool variables if the number of
* variables should not be fixed.
@@ -18,20 +19,21 @@
* @tparam capacity Capacity of the static dataset, which is usually known
* beforehand.
*/
class LocalDataSet : public LocalPoolDataSetBase {
class SharedSet : public SharedSetBase {
public:
LocalDataSet(HasLocalDataPoolIF* hkOwner, uint32_t setId, const size_t maxSize);
SharedSet(SharedPool& sharedPool, uint32_t setId, size_t maxSize,
bool serializeWithValidityBlob = true);
LocalDataSet(sid_t sid, const size_t maxSize);
SharedSet(sid_t sid, size_t maxSize, bool serializeWithValidityBlob = true);
virtual ~LocalDataSet();
~SharedSet() override;
//! Copying forbidden for now.
LocalDataSet(const LocalDataSet&) = delete;
LocalDataSet& operator=(const LocalDataSet&) = delete;
SharedSet(const SharedSet&) = delete;
SharedSet& operator=(const SharedSet&) = delete;
private:
std::vector<PoolVariableIF*> poolVarList;
};
#endif /* FSFW_DATAPOOLLOCAL_LOCALDATASET_H_ */
} // namespace datapool
+169
View File
@@ -0,0 +1,169 @@
#include "SharedSetBase.h"
#include <fsfw/housekeeping/GeneratesPeriodicHkIF.h>
#include <cmath>
#include "fsfw/datapool.h"
#include "fsfw/globalfunctions/bitutility.h"
#include "fsfw/objectmanager/ObjectManager.h"
#include "fsfw/serialize/SerializeAdapter.h"
#include "fsfw/serviceinterface/ServiceInterface.h"
using namespace datapool;
SharedSetBase::SharedSetBase(SharedPool &sharedPool, uint32_t setId,
PoolVariableIF **registeredVariablesArray,
const size_t maxNumberOfVariables, bool serializeWithValidityBlob)
: base(registeredVariablesArray, maxNumberOfVariables, serializeWithValidityBlob),
sharedPool(&sharedPool) {
mutexIfSingleDataCreator = sharedPool.getPoolMutex();
this->sid.objectId = sharedPool.getOwnerId();
this->sid.ownerSetId = setId;
}
SharedSetBase::SharedSetBase(structure_id_t sid, PoolVariableIF **registeredVariablesArray,
const size_t maxNumberOfVariables, bool serializeWithValidityBlob)
: base(registeredVariablesArray, maxNumberOfVariables, serializeWithValidityBlob) {
auto *hkOwner = ObjectManager::instance()->get<hk::GeneratesPeriodicHkIF>(sid.objectId);
if (hkOwner != nullptr) {
sharedPool = hkOwner->getOptionalSharedPool();
if (sharedPool != nullptr) {
mutexIfSingleDataCreator = sharedPool->getPoolMutex();
}
}
this->sid = sid;
}
SharedSetBase::SharedSetBase(PoolVariableIF **registeredVariablesArray,
const size_t maxNumberOfVariables, bool protectEveryReadCommitCall,
bool serializeWithValidityBlob)
: base(registeredVariablesArray, maxNumberOfVariables, serializeWithValidityBlob) {
base.setReadCommitProtectionBehaviour(protectEveryReadCommitCall);
}
SharedSetBase::~SharedSetBase() {
// In case set was read but not comitted, we commit all variables with an invalid state
if (base.state == PoolDataSetBase::States::STATE_SET_WAS_READ) {
for (uint16_t count = 0; count < base.getFillCount(); count++) {
if (base.registeredVariables[count] != nullptr) {
base.registeredVariables[count]->commit(MutexIF::TimeoutType::WAITING, 20);
}
}
}
}
ReturnValue_t SharedSetBase::lockDataPool(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
if (mutexIfSingleDataCreator != nullptr) {
return mutexIfSingleDataCreator->lockMutex(timeoutType, timeoutMs);
}
return returnvalue::OK;
}
ReturnValue_t SharedSetBase::unlockDataPool() {
if (mutexIfSingleDataCreator != nullptr) {
return mutexIfSingleDataCreator->unlockMutex();
}
return returnvalue::OK;
}
ReturnValue_t SharedSetBase::serializeLocalPoolIds(uint8_t **buffer, size_t *size, size_t maxSize,
SerializeIF::Endianness streamEndianness) const {
/* Serialize fill count as uint8_t */
auto fillCount = this->getFillCount();
for (uint16_t count = 0; count < fillCount; count++) {
id_t currentPoolId = base.registeredVariables[count]->getDataPoolId();
auto result =
SerializeAdapter::serialize(&currentPoolId, buffer, size, maxSize, streamEndianness);
if (result != returnvalue::OK) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "LocalPoolDataSetBase::serializeLocalPoolIds: " << "Serialization error!"
<< std::endl;
#else
sif::printWarning(
"LocalPoolDataSetBase::serializeLocalPoolIds: "
"Serialization error!\n\r");
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
return result;
}
}
return returnvalue::OK;
}
size_t SharedSetBase::getLocalPoolIdsSerializedSize() const {
return base.getFillCount() * sizeof(id_t);
}
size_t SharedSetBase::getSerializedSize() const { return base.getSerializedSize(); }
ReturnValue_t SharedSetBase::deSerialize(const uint8_t **buffer, size_t *size,
SerializeIF::Endianness streamEndianness) {
return base.deSerialize(buffer, size, streamEndianness);
}
ReturnValue_t SharedSetBase::serialize(uint8_t **buffer, size_t *size, size_t maxSize,
SerializeIF::Endianness streamEndianness) const {
return base.serialize(buffer, size, maxSize, streamEndianness);
}
[[nodiscard]] ReturnValue_t SharedSetBase::serialize(
uint8_t *buffer, size_t &serLen, size_t maxSize,
SerializeIF::Endianness streamEndianness) const {
return SerializeIF::serialize(buffer, serLen, maxSize, streamEndianness);
}
void SharedSetBase::setReportingEnabled(bool reportingEnabled) {
this->reportingEnabled = reportingEnabled;
}
bool SharedSetBase::getReportingEnabled() const { return reportingEnabled; }
structure_id_t SharedSetBase::getStructureId() const { return sid; }
void SharedSetBase::setChildrenValidity(bool valid) { return base.setChildrenValidity(valid); }
object_id_t SharedSetBase::getCreatorObjectId() {
if (sharedPool != nullptr) {
return sharedPool->getOwnerId();
}
return objects::NO_OBJECT;
}
void SharedSetBase::setAllVariablesReadOnly() {
for (size_t idx = 0; idx < this->getFillCount(); idx++) {
base.registeredVariables[idx]->setReadWriteMode(pool_rwm_t::VAR_READ);
}
}
void SharedSetBase::printSet() { return; }
ReturnValue_t SharedSetBase::read(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
lockDataPool(timeoutType, timeoutMs);
ReturnValue_t result = base.read(timeoutType, timeoutMs);
unlockDataPool();
return result;
}
ReturnValue_t SharedSetBase::commit(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
lockDataPool(timeoutType, timeoutMs);
ReturnValue_t result = base.commit(timeoutType, timeoutMs);
unlockDataPool();
return result;
}
uint16_t SharedSetBase::getFillCount() const { return base.getFillCount(); }
ReturnValue_t SharedSetBase::registerVariable(PoolVariableIF *variable) {
return base.registerVariable(variable);
}
void SharedSetBase::setContainer(PoolVariableIF **variablesContainer) {
return base.setContainer(variablesContainer);
}
PoolVariableIF **SharedSetBase::getContainer() const { return base.getContainer(); }
void SharedSetBase::updateValidityBlobSerialization(bool enable) {
base.serializeWithValidityBlob = enable;
}
bool SharedSetBase::getValidityBlobSerialization() const { return base.serializeWithValidityBlob; }
@@ -1,17 +1,16 @@
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETBASE_H_
#define FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETBASE_H_
#pragma once
#include <vector>
#include "MarkChangedIF.h"
#include "SharedPool.h"
#include "definitions.h"
#include "fsfw/datapool/DataSetIF.h"
#include "fsfw/datapool/PoolDataSetBase.h"
#include "localPoolDefinitions.h"
class LocalDataPoolManager;
class HasLocalDataPoolIF;
class PeriodicHkGenerationHelper;
class PeriodicHkGenerationIF;
class PeriodicHousekeepingHelper;
namespace datapool {
/**
* @brief The LocalDataSet class manages a set of locally checked out
* variables for local data pools
@@ -40,10 +39,7 @@ class PeriodicHousekeepingHelper;
*
* @ingroup data_pool
*/
class LocalPoolDataSetBase : public PoolDataSetBase, public MarkChangedIF {
friend class LocalPoolDataSetAttorney;
friend class PeriodicHousekeepingHelper;
class SharedSetBase : public SerializeIF, public PoolDataSetIF {
public:
/**
* @brief Constructor for the creator of local pool data.
@@ -51,9 +47,8 @@ class LocalPoolDataSetBase : public PoolDataSetBase, public MarkChangedIF {
* This constructor also initializes the components required for
* periodic handling.
*/
LocalPoolDataSetBase(HasLocalDataPoolIF* hkOwner, uint32_t setId,
PoolVariableIF** registeredVariablesArray, const size_t maxNumberOfVariables,
bool periodicHandling = true);
SharedSetBase(SharedPool& sharedPool, uint32_t setId, PoolVariableIF** registeredVariablesArray,
size_t maxNumberOfVariables, bool serializeWithValidityBlob = true);
/**
* @brief Constructor for users of the local pool data, which need
@@ -66,8 +61,8 @@ class LocalPoolDataSetBase : public PoolDataSetBase, public MarkChangedIF {
* @param registeredVariablesArray
* @param maxNumberOfVariables
*/
LocalPoolDataSetBase(sid_t sid, PoolVariableIF** registeredVariablesArray,
const size_t maxNumberOfVariables);
SharedSetBase(sid_t sid, PoolVariableIF** registeredVariablesArray, size_t maxNumberOfVariables,
bool serializeWithValidityBlob = true);
/**
* @brief Simple constructor, if the dataset is not the owner by
@@ -87,8 +82,8 @@ class LocalPoolDataSetBase : public PoolDataSetBase, public MarkChangedIF {
* multiple creators, this flag can be set to protect all read and
* commit calls separately.
*/
LocalPoolDataSetBase(PoolVariableIF** registeredVariablesArray, const size_t maxNumberOfVariables,
bool protectEveryReadCommitCall = true);
SharedSetBase(PoolVariableIF** registeredVariablesArray, size_t maxNumberOfVariables,
bool serializeWithValidityBlob, bool protectEveryReadCommitCall = true);
/**
* @brief The destructor automatically manages writing the valid
@@ -98,13 +93,13 @@ class LocalPoolDataSetBase : public PoolDataSetBase, public MarkChangedIF {
* the destructor parses all variables that are still registered to the set.
* For each, the valid flag in the data pool is set to "invalid".
*/
~LocalPoolDataSetBase();
~SharedSetBase() override;
/* The copy constructor and assingment constructor are forbidden for now.
The use-cases are limited and the first step would be to implement them properly for the
base class */
LocalPoolDataSetBase(const LocalPoolDataSetBase& otherSet) = delete;
const LocalPoolDataSetBase& operator=(const LocalPoolDataSetBase& otherSet) = delete;
SharedSetBase(const SharedSetBase& otherSet) = delete;
const SharedSetBase& operator=(const SharedSetBase& otherSet) = delete;
/**
* Helper functions used to set all currently contained variables to read-only.
@@ -112,56 +107,27 @@ class LocalPoolDataSetBase : public PoolDataSetBase, public MarkChangedIF {
* by data consumers to prevent accidentally changing pool data.
*/
void setAllVariablesReadOnly();
void setValidityBufferGeneration(bool withValidityBuffer);
sid_t getSid() const;
[[nodiscard]] virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
Endianness streamEndianness) const override;
/** SerializeIF overrides */
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
SerializeIF::Endianness streamEndianness) const override;
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
SerializeIF::Endianness streamEndianness) override;
size_t getSerializedSize() const override;
[[nodiscard]] ReturnValue_t serialize(uint8_t* buffer, size_t& serLen, size_t maxSize,
SerializeIF::Endianness streamEndianness) const override;
virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
Endianness streamEndianness) override;
[[nodiscard]] dp::sid_t getStructureId() const;
[[nodiscard]] size_t getSerializedSize() const override;
/**
* Special version of the serilization function which appends a
* validity buffer at the end. Each bit of this validity buffer
* denotes whether the container data set entries are valid from left
* to right, MSB first. (length = ceil(N/8), N = number of pool variables)
* @param buffer
* @param size
* @param maxSize
* @param bigEndian
* @param withValidityBuffer
* @return
*/
ReturnValue_t serializeWithValidityBuffer(uint8_t** buffer, size_t* size, size_t maxSize,
SerializeIF::Endianness streamEndianness) const;
ReturnValue_t deSerializeWithValidityBuffer(const uint8_t** buffer, size_t* size,
SerializeIF::Endianness streamEndianness);
ReturnValue_t serializeLocalPoolIds(uint8_t** buffer, size_t* size, size_t maxSize,
SerializeIF::Endianness streamEndianness,
bool serializeFillCount = true) const;
uint8_t getLocalPoolIdsSerializedSize(bool serializeFillCount = true) const;
/**
* Set the dataset valid or invalid. These calls are mutex protected.
* @param setEntriesRecursively
* If this is true, all contained datasets will also be set recursively.
*/
void setValidity(bool valid, bool setEntriesRecursively);
bool isValid() const override;
/**
* These calls are mutex protected.
* @param changed
*/
void setChanged(bool changed) override;
bool hasChanged() const override;
SerializeIF::Endianness streamEndianness) const;
[[nodiscard]] size_t getLocalPoolIdsSerializedSize() const;
object_id_t getCreatorObjectId();
bool getReportingEnabled() const;
[[nodiscard]] bool getReportingEnabled() const;
void setReportingEnabled(bool enabled);
/**
@@ -170,14 +136,35 @@ class LocalPoolDataSetBase : public PoolDataSetBase, public MarkChangedIF {
* returns 0.0
* @return
*/
float getCollectionInterval() const;
[[nodiscard]] float getCollectionInterval() const;
/**
* @brief Can be overwritten by a specific implementation of a dataset to print the set.
*/
virtual void printSet();
ReturnValue_t read(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::BLOCKING,
dur_millis_t timeoutMs = 0) override;
ReturnValue_t commit(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::BLOCKING,
dur_millis_t timeoutMs = 0) override;
uint16_t getFillCount() const override;
ReturnValue_t registerVariable(PoolVariableIF* variable) override;
void setContainer(PoolVariableIF** variablesContainer);
PoolVariableIF** getContainer() const;
/**
* Set the validity of all children
*/
void setChildrenValidity(bool valid);
void updateValidityBlobSerialization(bool enable);
bool getValidityBlobSerialization() const;
protected:
PoolDataSetBase base;
sid_t sid;
//! This mutex is used if the data is created by one object only.
MutexIF* mutexIfSingleDataCreator = nullptr;
@@ -189,27 +176,6 @@ class LocalPoolDataSetBase : public PoolDataSetBase, public MarkChangedIF {
void initializePeriodicHelper(float collectionInterval, dur_millis_t minimumPeriodicInterval);
/**
* If the valid state of a dataset is always relevant to the whole
* data set we can use this flag.
*/
bool valid = false;
/**
* Can be used to mark the dataset as changed, which is used
* by the LocalDataPoolManager to send out update messages.
*/
bool changed = false;
/**
* Specify whether the validity buffer is serialized too when serializing
* or deserializing the packet. Each bit of the validity buffer will
* contain the validity state of the pool variables from left to right.
* The size of validity buffer thus will be ceil(N / 8) with N = number of
* pool variables.
*/
bool withValidityBuffer = true;
/**
* @brief This is a small helper function to facilitate locking
* the global data pool.
@@ -226,8 +192,7 @@ class LocalPoolDataSetBase : public PoolDataSetBase, public MarkChangedIF {
*/
ReturnValue_t unlockDataPool() override;
PeriodicHousekeepingHelper* periodicHelper = nullptr;
LocalDataPoolManager* poolManager = nullptr;
dp::SharedPool* sharedPool = nullptr;
};
#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETBASE_H_ */
} // namespace datapool
@@ -1,13 +1,10 @@
#ifndef FSFW_DATAPOOLLOCAL_STATICLOCALDATASET_H_
#define FSFW_DATAPOOLLOCAL_STATICLOCALDATASET_H_
#pragma once
#include <array>
#include "../objectmanager/SystemObjectIF.h"
#include "LocalPoolDataSetBase.h"
#include "LocalPoolVariable.h"
#include "LocalPoolVector.h"
#include "SharedSetBase.h"
namespace datapool {
/**
* @brief This dataset type can be used to group related pool variables if the number of
* variables is fixed.
@@ -18,30 +15,31 @@
*
* It is recommended to read the documentation of the LocalPoolDataSetBase
* class for more information on how this class works and how to use it.
* @tparam capacity Capacity of the static dataset, which is usually known
* @tparam NUM_VARIABLES Capacity of the static dataset, which is usually known
* beforehand.
*/
template <uint8_t NUM_VARIABLES>
class StaticLocalDataSet : public LocalPoolDataSetBase {
template <size_t NUM_VARIABLES>
class StaticSharedSet : public datapool::SharedSetBase {
public:
/**
* Constructor used by data owner and creator like device handlers.
* This constructor also initialized the components required for
* periodic handling.
* @param hkOwner
* @param sharedPool Shared pool this dataset will read from or write to.
* @param setId
*/
StaticLocalDataSet(HasLocalDataPoolIF* hkOwner, uint32_t setId)
: LocalPoolDataSetBase(hkOwner, setId, nullptr, NUM_VARIABLES) {
StaticSharedSet(SharedPool& sharedPool, const uint32_t setId,
bool serializeWithValidityBlob = true)
: SharedSetBase(sharedPool, setId, nullptr, NUM_VARIABLES, serializeWithValidityBlob) {
this->setContainer(poolVarList.data());
}
/**
* Constructor used by data users like controllers.
* @param hkOwner
* @param setId
* @param sid
*/
StaticLocalDataSet(sid_t sid) : LocalPoolDataSetBase(sid, nullptr, NUM_VARIABLES) {
explicit StaticSharedSet(const structure_id_t sid, bool serializeWithValidityBlob = true)
: SharedSetBase(sid, nullptr, NUM_VARIABLES, serializeWithValidityBlob) {
this->setContainer(poolVarList.data());
}
@@ -49,4 +47,4 @@ class StaticLocalDataSet : public LocalPoolDataSetBase {
std::array<PoolVariableIF*, NUM_VARIABLES> poolVarList = {};
};
#endif /* FSFW_DATAPOOLLOCAL_STATICLOCALDATASET_H_ */
} // namespace datapool
@@ -1,5 +1,4 @@
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLDEFINITIONS_H_
#define FSFW_DATAPOOLLOCAL_LOCALPOOLDEFINITIONS_H_
#pragma once
#include <cstdint>
#include <map>
@@ -8,44 +7,44 @@
#include "../objectmanager/SystemObjectIF.h"
#include "../objectmanager/frameworkObjects.h"
namespace datapool {
/**
* @brief Type definition for local pool entries.
*/
using lp_id_t = uint32_t;
using id_t = uint32_t;
namespace localpool {
static constexpr uint32_t INVALID_LPID = -1;
static constexpr uint8_t INTERFACE_ID = CLASS_ID::LOCAL_POOL_OWNER_IF;
static constexpr uint8_t INTERFACE_ID = CLASS_ID::DATAPOOL_IF;
static constexpr ReturnValue_t POOL_ENTRY_NOT_FOUND = MAKE_RETURN_CODE(0x00);
static constexpr ReturnValue_t POOL_ENTRY_TYPE_CONFLICT = MAKE_RETURN_CODE(0x01);
/** This is the core data structure of the local data pools. Users should insert all desired
pool variables, using the std::map interface. */
using DataPool = std::map<lp_id_t, PoolEntryIF*>;
using DataPoolMapIter = DataPool::iterator;
} // namespace localpool
using DataPool = std::map<id_t, PoolEntryIF*>;
/**
* Used as a unique identifier for data sets. Consists of 4 byte object ID and 4 byte set ID.
*/
union sid_t {
union structure_id_t {
static constexpr size_t SIZE = sizeof(object_id_t) + sizeof(uint32_t);
static constexpr uint64_t INVALID_SID = -1;
static constexpr uint32_t INVALID_OBJECT_ID = objects::NO_OBJECT;
static constexpr uint32_t INVALID_SET_ID = -1;
sid_t() : raw(INVALID_SID) {}
constexpr structure_id_t() : raw(INVALID_SID) {}
sid_t(object_id_t objectId, uint32_t setId) : objectId(objectId), ownerSetId(setId) {}
constexpr structure_id_t(object_id_t objectId, uint32_t setId)
: objectId(objectId), ownerSetId(setId) {}
struct {
object_id_t objectId;
/**
* A generic 32 bit ID to identify unique HK packets for a single
* object. For example, the DeviceCommandId_t is used for
* DeviceHandlers
* A generic 32 bit ID to identify unique HK set packets for a single
* object.
*/
uint32_t ownerSetId;
};
@@ -54,39 +53,41 @@ union sid_t {
*/
uint64_t raw;
bool notSet() const { return raw == INVALID_SID; }
[[nodiscard]] bool notSet() const { return raw == INVALID_SID; }
bool operator==(const sid_t& other) const { return raw == other.raw; }
bool operator==(const structure_id_t& other) const { return raw == other.raw; }
bool operator!=(const sid_t& other) const { return not(raw == other.raw); }
bool operator!=(const structure_id_t& other) const { return not(raw == other.raw); }
};
using sid_t = structure_id_t;
/**
* Used as a global unique identifier for local pool variables. Consists of 4 byte object ID
* and 4 byte local pool ID.
*/
union gp_id_t {
union g_id_t {
static constexpr uint64_t INVALID_GPID = -1;
static constexpr uint32_t INVALID_OBJECT_ID = objects::NO_OBJECT;
static constexpr uint32_t INVALID_LPID = localpool::INVALID_LPID;
gp_id_t() : raw(INVALID_GPID) {}
g_id_t() : raw(INVALID_GPID) {}
gp_id_t(object_id_t objectId, lp_id_t localPoolId)
: objectId(objectId), localPoolId(localPoolId) {}
g_id_t(object_id_t objectId, id_t localPoolId) : objectId(objectId), localPoolId(localPoolId) {}
struct {
object_id_t objectId;
lp_id_t localPoolId;
id_t localPoolId;
};
uint64_t raw;
bool notSet() const { return raw == INVALID_GPID; }
bool operator==(const gp_id_t& other) const { return raw == other.raw; }
bool operator==(const g_id_t& other) const { return raw == other.raw; }
bool operator!=(const gp_id_t& other) const { return not(raw == other.raw); }
bool operator!=(const g_id_t& other) const { return not(raw == other.raw); }
};
#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLDEFINITIONS_H_ */
} // namespace datapool
namespace dp = datapool;
@@ -0,0 +1 @@
target_sources(${LIB_FSFW_NAME} PRIVATE)
@@ -0,0 +1,11 @@
#pragma once
#include "fsfw/datapool/SharedSetBase.h"
class LocalPoolDataSetAttorney {
static void setReportingEnabled(SharedDatasetBase& set, bool enabled) {
set.setReportingEnabled(enabled);
}
static bool getReportingEnabled(SharedDatasetBase& set) { return set.getReportingEnabled(); }
};
@@ -1,8 +1,8 @@
#ifndef FSFW_DATAPOOLLOCAL_LOCALDPMANAGERATTORNEY_H_
#define FSFW_DATAPOOLLOCAL_LOCALDPMANAGERATTORNEY_H_
#pragma once
#include "../LocalDataPoolManager.h"
#include "fsfw/datapool/SharedPool.h"
namespace datapool {
/**
* @brief This is a helper class implements the Attorney-Client idiom for access to
* LocalDataPoolManager internals
@@ -13,15 +13,15 @@
* an explicit subset of the pool manager private/protected functions.
* See: https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Friendship_and_the_Attorney-Client
*/
class LocalDpManagerAttorney {
class SharedPoolAttorney {
private:
template <typename T>
static ReturnValue_t fetchPoolEntry(LocalDataPoolManager& manager, lp_id_t localPoolId,
static ReturnValue_t fetchPoolEntry(SharedPool& manager, dp::id_t localPoolId,
PoolEntry<T>** poolEntry) {
return manager.fetchPoolEntry(localPoolId, poolEntry);
}
static MutexIF* getMutexHandle(LocalDataPoolManager& manager) { return manager.getMutexHandle(); }
static MutexIF* getMutexHandle(SharedPool& manager) { return manager.getPoolMutex(); }
template <typename T>
friend class LocalPoolVariable;
@@ -29,4 +29,4 @@ class LocalDpManagerAttorney {
friend class LocalPoolVector;
};
#endif /* FSFW_DATAPOOLLOCAL_LOCALDPMANAGERATTORNEY_H_ */
} // namespace datapool
-11
View File
@@ -1,11 +0,0 @@
#ifndef FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_
#define FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_
/* Collected related headers */
#include "fsfw/datapoollocal/LocalDataSet.h"
#include "fsfw/datapoollocal/LocalPoolVariable.h"
#include "fsfw/datapoollocal/LocalPoolVector.h"
#include "fsfw/datapoollocal/SharedLocalDataSet.h"
#include "fsfw/datapoollocal/StaticLocalDataSet.h"
#endif /* FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_ */
-26
View File
@@ -1,26 +0,0 @@
#ifndef FSFW_DATAPOOLLOCAL_ACCESSLOCALPOOLF_H_
#define FSFW_DATAPOOLLOCAL_ACCESSLOCALPOOLF_H_
class LocalDataPoolManager;
class MutexIF;
/**
* @brief Accessor class which can be used by classes which like to use the pool manager.
*/
class AccessPoolManagerIF {
public:
virtual ~AccessPoolManagerIF() {};
virtual MutexIF* getLocalPoolMutex() = 0;
/**
* Can be used to get a handle to the local data pool manager.
* This function is protected because it should only be used by the
* class imlementing the interface.
*/
virtual LocalDataPoolManager* getPoolManagerHandle() = 0;
protected:
};
#endif /* FSFW_DATAPOOLLOCAL_ACCESSLOCALPOOLF_H_ */
-6
View File
@@ -1,6 +0,0 @@
target_sources(
${LIB_FSFW_NAME}
PRIVATE LocalDataPoolManager.cpp LocalDataSet.cpp LocalPoolDataSetBase.cpp
LocalPoolObjectBase.cpp SharedLocalDataSet.cpp)
add_subdirectory(internal)
-181
View File
@@ -1,181 +0,0 @@
#ifndef FSFW_DATAPOOLLOCAL_HASLOCALDATAPOOLIF_H_
#define FSFW_DATAPOOLLOCAL_HASLOCALDATAPOOLIF_H_
#include <map>
#include "LocalDataPoolManager.h"
#include "fsfw/datapool/PoolEntryIF.h"
#include "fsfw/housekeeping/HousekeepingMessage.h"
#include "fsfw/ipc/MessageQueueSenderIF.h"
#include "fsfw/serviceinterface.h"
#include "localPoolDefinitions.h"
class AccessPoolManagerIF;
class ProvidesDataPoolSubscriptionIF;
class LocalPoolDataSetBase;
class LocalPoolObjectBase;
/**
* @brief This interface is implemented by classes which posses a local data pool (not
* the managing class). It defines the relationship between the local data pool owner and the
* LocalDataPoolManager.
* @details
* Any class implementing this interface shall also have a LocalDataPoolManager member class which
* contains the actual pool data structure and exposes the public interface for it.
*
* The local data pool can be accessed using helper classes by using the
* LocalPoolVariable, LocalPoolVector or LocalDataSet classes. Every local pool variable can
* be uniquely identified by a global pool ID (gp_id_t) and every dataset tied
* to a pool manager can be uniqely identified by a global structure ID (sid_t).
*
* All software objects which want to use the local pool of another object shall also use this
* interface, for example to get a handle to the subscription interface. The interface
* can be retrieved using the object manager, provided the target object is a SystemObject.
* For example, the following line of code can be used to retrieve the interface
*
* HasLocalDataPoolIF* poolIF = ObjectManager::instance()->
* get<HasLocalDataPoolIF>(objects::SOME_OBJECT);
* if(poolIF != nullptr) {
* doSomething()
* }
*/
class HasLocalDataPoolIF {
friend class HasLocalDpIFManagerAttorney;
friend class HasLocalDpIFUserAttorney;
public:
virtual ~HasLocalDataPoolIF() {};
static constexpr uint32_t INVALID_LPID = localpool::INVALID_LPID;
virtual object_id_t getObjectId() const = 0;
/** Command queue for housekeeping messages. */
virtual MessageQueueId_t getCommandQueue() const = 0;
/**
* Is used by pool owner to initialize the pool map once
* The manager instance shall also be passed to this function.
* It can be used to subscribe for periodic packets for for updates.
*/
virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
LocalDataPoolManager& poolManager) = 0;
/**
* Returns the minimum sampling frequency in milliseconds, which will
* usually be the period the pool owner performs its periodic operation.
* @return
*/
virtual dur_millis_t getPeriodicOperationFrequency() const = 0;
/**
* @brief This function will be called by the manager if an update
* notification is received.
* @details HasLocalDataPoolIF
* Can be overriden by the child class to handle changed datasets.
* @param sid SID of the updated set
* @param storeId If a snapshot was requested, data will be located inside
* the IPC store with this store ID.
* @param clearMessage If this is set to true, the pool manager will take care of
* clearing the store automatically
*/
virtual void handleChangedDataset(sid_t sid, store_address_t storeId = store_address_t::invalid(),
bool* clearMessage = nullptr) {
if (clearMessage != nullptr) {
*clearMessage = true;
}
}
/**
* @brief This function will be called by the manager if an update
* notification is received.
* @details
* Can be overriden by the child class to handle changed pool variables.
* @param gpid GPID of the updated variable.
* @param storeId If a snapshot was requested, data will be located inside
* the IPC store with this store ID.
* @param clearMessage Relevant for snapshots. If the boolean this points to is set to true,
* the pool manager will take care of clearing the store automatically
* after the callback.
*/
virtual void handleChangedPoolVariable(gp_id_t gpid,
store_address_t storeId = store_address_t::invalid(),
bool* clearMessage = nullptr) {
if (clearMessage != nullptr) {
*clearMessage = true;
}
}
/**
* These function can be implemented by pool owner, if they are required
* and used by the housekeeping message interface.
* */
virtual ReturnValue_t addDataSet(sid_t sid) { return returnvalue::FAILED; };
virtual ReturnValue_t removeDataSet(sid_t sid) { return returnvalue::FAILED; };
virtual ReturnValue_t changeCollectionInterval(sid_t sid, float newIntervalSeconds) {
return returnvalue::FAILED;
};
/**
* This function can be used by data pool consumers to retrieve a handle
* which allows subscriptions to dataset and variable updates in form of messages.
* The consumers can then read the most recent variable value by calling read with
* an own pool variable or set instance or using the deserialized snapshot data.
* Returns the HK manager casted to the required interface by default.
* @return
*/
virtual ProvidesDataPoolSubscriptionIF* getSubscriptionInterface() {
return getHkManagerHandle();
}
protected:
/**
* Every class implementing this interface should have a local data pool manager. This
* function will return a reference to the manager.
* @return
*/
virtual LocalDataPoolManager* getHkManagerHandle() = 0;
/**
* Accessor handle required for internal handling. Not intended for users and therefore
* declared protected. Users should instead use pool variables, sets or the subscription
* interface to access pool entries. Returns the HK manager casted to a different interface
* by default.
* @return
*/
virtual AccessPoolManagerIF* getAccessorHandle() { return getHkManagerHandle(); }
/**
* This function is used by the pool manager to get a valid dataset
* from a SID. This function is protected to prevent users from
* using raw data set pointers which could not be thread-safe. Users
* should use the #ProvidesDataPoolSubscriptionIF.
* @param sid Corresponding structure ID
* @return
*/
virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) = 0;
/**
* Similar to the function above, but used to get a local pool variable
* handle. This is only needed for update notifications, so it is not
* defined as abstract. This function is protected to prevent users from
* using raw pool variable pointers which could not be thread-safe.
* Users should use the #ProvidesDataPoolSubscriptionIF.
* @param localPoolId
* @return
*/
virtual LocalPoolObjectBase* getPoolObjectHandle(lp_id_t localPoolId) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "HasLocalDataPoolIF::getPoolObjectHandle: Not overriden. "
"Returning nullptr!"
<< std::endl;
#else
sif::printWarning(
"HasLocalDataPoolIF::getPoolObjectHandle: "
"Not overriden. Returning nullptr!\n");
#endif
return nullptr;
}
};
#endif /* FSFW_DATAPOOLLOCAL_HASLOCALDATAPOOLIF_H_ */
@@ -1,817 +0,0 @@
#include "fsfw/datapoollocal/LocalDataPoolManager.h"
#include <cmath>
#include "fsfw/datapoollocal.h"
#include "fsfw/housekeeping/AcceptsHkPacketsIF.h"
#include "fsfw/housekeeping/HousekeepingSetPacket.h"
#include "fsfw/housekeeping/HousekeepingSnapshot.h"
#include "fsfw/ipc/MutexFactory.h"
#include "fsfw/ipc/MutexGuard.h"
#include "fsfw/ipc/QueueFactory.h"
#include "fsfw/objectmanager/ObjectManager.h"
#include "fsfw/timemanager/CCSDSTime.h"
#include "internal/HasLocalDpIFManagerAttorney.h"
#include "internal/LocalPoolDataSetAttorney.h"
// TODO: Get rid of this. This should be a constructor argument, not something hardcoded in any way
object_id_t LocalDataPoolManager::defaultHkDestination = objects::PUS_SERVICE_3_HOUSEKEEPING;
LocalDataPoolManager::LocalDataPoolManager(HasLocalDataPoolIF* owner, MessageQueueIF* queueToUse,
bool appendValidityBuffer)
: appendValidityBuffer(appendValidityBuffer) {
if (owner == nullptr) {
printWarningOrError(sif::OutputTypes::OUT_WARNING, "LocalDataPoolManager", returnvalue::FAILED,
"Invalid supplied owner");
return;
}
this->owner = owner;
mutex = MutexFactory::instance()->createMutex();
if (mutex == nullptr) {
printWarningOrError(sif::OutputTypes::OUT_ERROR, "LocalDataPoolManager", returnvalue::FAILED,
"Could not create mutex");
}
hkQueue = queueToUse;
}
LocalDataPoolManager::~LocalDataPoolManager() {
if (mutex != nullptr) {
MutexFactory::instance()->deleteMutex(mutex);
}
}
ReturnValue_t LocalDataPoolManager::initialize(MessageQueueIF* queueToUse) {
if (queueToUse == nullptr) {
/* Error, all destinations invalid */
printWarningOrError(sif::OutputTypes::OUT_ERROR, "initialize", QUEUE_OR_DESTINATION_INVALID);
}
hkQueue = queueToUse;
ipcStore = ObjectManager::instance()->get<StorageManagerIF>(objects::IPC_STORE);
if (ipcStore == nullptr) {
/* Error, all destinations invalid */
printWarningOrError(sif::OutputTypes::OUT_ERROR, "initialize", returnvalue::FAILED,
"Could not set IPC store.");
return returnvalue::FAILED;
}
if (defaultHkDestination != objects::NO_OBJECT) {
auto* hkPacketReceiver =
ObjectManager::instance()->get<AcceptsHkPacketsIF>(defaultHkDestination);
if (hkPacketReceiver != nullptr) {
hkDestinationId = hkPacketReceiver->getHkQueue();
} else {
printWarningOrError(sif::OutputTypes::OUT_ERROR, "initialize", QUEUE_OR_DESTINATION_INVALID);
return QUEUE_OR_DESTINATION_INVALID;
}
}
return returnvalue::OK;
}
ReturnValue_t LocalDataPoolManager::initializeAfterTaskCreation() {
return initializeHousekeepingPoolEntriesOnce();
}
ReturnValue_t LocalDataPoolManager::initializeHousekeepingPoolEntriesOnce() {
if (not mapInitialized) {
ReturnValue_t result = owner->initializeLocalDataPool(localPoolMap, *this);
if (result == returnvalue::OK) {
mapInitialized = true;
}
return result;
}
printWarningOrError(sif::OutputTypes::OUT_WARNING, "initializeHousekeepingPoolEntriesOnce",
returnvalue::FAILED, "The map should only be initialized once");
return returnvalue::OK;
}
ReturnValue_t LocalDataPoolManager::performHkOperation() {
ReturnValue_t status = returnvalue::OK;
for (auto& receiver : hkReceivers) {
switch (receiver.reportingType) {
case (ReportingType::PERIODIC): {
if (receiver.dataType == DataType::LOCAL_POOL_VARIABLE) {
/* Periodic packets shall only be generated from datasets */
continue;
}
performPeriodicHkGeneration(receiver);
break;
}
case (ReportingType::UPDATE_HK): {
handleHkUpdate(receiver, status);
break;
}
case (ReportingType::UPDATE_NOTIFICATION): {
handleNotificationUpdate(receiver, status);
break;
}
case (ReportingType::UPDATE_SNAPSHOT): {
handleNotificationSnapshot(receiver, status);
break;
}
default:
// This should never happen.
return returnvalue::FAILED;
}
}
resetHkUpdateResetHelper();
return status;
}
ReturnValue_t LocalDataPoolManager::handleHkUpdate(HkReceiver& receiver, ReturnValue_t& status) {
if (receiver.dataType == DataType::LOCAL_POOL_VARIABLE) {
/* Update packets shall only be generated from datasets. */
return returnvalue::FAILED;
}
LocalPoolDataSetBase* dataSet =
HasLocalDpIFManagerAttorney::getDataSetHandle(owner, receiver.dataId.sid);
if (dataSet == nullptr) {
return DATASET_NOT_FOUND;
}
if (dataSet->hasChanged()) {
/* Prepare and send update notification */
ReturnValue_t result = generateHousekeepingPacket(receiver.dataId.sid, dataSet, true);
if (result != returnvalue::OK) {
status = result;
}
}
handleChangeResetLogic(receiver.dataType, receiver.dataId, dataSet);
return returnvalue::OK;
}
ReturnValue_t LocalDataPoolManager::handleNotificationUpdate(HkReceiver& receiver,
ReturnValue_t& status) {
MarkChangedIF* toReset = nullptr;
if (receiver.dataType == DataType::LOCAL_POOL_VARIABLE) {
LocalPoolObjectBase* poolObj =
HasLocalDpIFManagerAttorney::getPoolObjectHandle(owner, receiver.dataId.localPoolId);
if (poolObj == nullptr) {
printWarningOrError(sif::OutputTypes::OUT_WARNING, "handleNotificationUpdate",
POOLOBJECT_NOT_FOUND);
return POOLOBJECT_NOT_FOUND;
}
if (poolObj->hasChanged()) {
/* Prepare and send update notification. */
CommandMessage notification;
HousekeepingMessage::setUpdateNotificationVariableCommand(
&notification, gp_id_t(owner->getObjectId(), receiver.dataId.localPoolId));
ReturnValue_t result = hkQueue->sendMessage(receiver.destinationQueue, &notification);
if (result != returnvalue::OK) {
status = result;
}
toReset = poolObj;
}
} else {
LocalPoolDataSetBase* dataSet =
HasLocalDpIFManagerAttorney::getDataSetHandle(owner, receiver.dataId.sid);
if (dataSet == nullptr) {
printWarningOrError(sif::OutputTypes::OUT_WARNING, "handleNotificationUpdate",
DATASET_NOT_FOUND);
return DATASET_NOT_FOUND;
}
if (dataSet->hasChanged()) {
/* Prepare and send update notification */
CommandMessage notification;
HousekeepingMessage::setUpdateNotificationSetCommand(&notification, receiver.dataId.sid);
ReturnValue_t result = hkQueue->sendMessage(receiver.destinationQueue, &notification);
if (result != returnvalue::OK) {
status = result;
}
toReset = dataSet;
}
}
if (toReset != nullptr) {
handleChangeResetLogic(receiver.dataType, receiver.dataId, toReset);
}
return returnvalue::OK;
}
ReturnValue_t LocalDataPoolManager::handleNotificationSnapshot(HkReceiver& receiver,
ReturnValue_t& status) {
MarkChangedIF* toReset = nullptr;
/* Check whether data has changed and send messages in case it has */
if (receiver.dataType == DataType::LOCAL_POOL_VARIABLE) {
LocalPoolObjectBase* poolObj =
HasLocalDpIFManagerAttorney::getPoolObjectHandle(owner, receiver.dataId.localPoolId);
if (poolObj == nullptr) {
printWarningOrError(sif::OutputTypes::OUT_WARNING, "handleNotificationSnapshot",
POOLOBJECT_NOT_FOUND);
return POOLOBJECT_NOT_FOUND;
}
if (not poolObj->hasChanged()) {
return returnvalue::OK;
}
/* Prepare and send update snapshot */
timeval now{};
Clock::getClock_timeval(&now);
CCSDSTime::CDS_short cds{};
CCSDSTime::convertToCcsds(&cds, &now);
HousekeepingSnapshot updatePacket(
reinterpret_cast<uint8_t*>(&cds), sizeof(cds),
HasLocalDpIFManagerAttorney::getPoolObjectHandle(owner, receiver.dataId.localPoolId));
store_address_t storeId;
ReturnValue_t result = addUpdateToStore(updatePacket, storeId);
if (result != returnvalue::OK) {
return result;
}
CommandMessage notification;
HousekeepingMessage::setUpdateSnapshotVariableCommand(
&notification, gp_id_t(owner->getObjectId(), receiver.dataId.localPoolId), storeId);
result = hkQueue->sendMessage(receiver.destinationQueue, &notification);
if (result != returnvalue::OK) {
status = result;
}
toReset = poolObj;
} else {
LocalPoolDataSetBase* dataSet =
HasLocalDpIFManagerAttorney::getDataSetHandle(owner, receiver.dataId.sid);
if (dataSet == nullptr) {
printWarningOrError(sif::OutputTypes::OUT_WARNING, "handleNotificationSnapshot",
DATASET_NOT_FOUND);
return DATASET_NOT_FOUND;
}
if (not dataSet->hasChanged()) {
return returnvalue::OK;
}
/* Prepare and send update snapshot */
timeval now{};
Clock::getClock_timeval(&now);
CCSDSTime::CDS_short cds{};
CCSDSTime::convertToCcsds(&cds, &now);
HousekeepingSnapshot updatePacket(
reinterpret_cast<uint8_t*>(&cds), sizeof(cds),
HasLocalDpIFManagerAttorney::getDataSetHandle(owner, receiver.dataId.sid));
store_address_t storeId;
ReturnValue_t result = addUpdateToStore(updatePacket, storeId);
if (result != returnvalue::OK) {
return result;
}
CommandMessage notification;
HousekeepingMessage::setUpdateSnapshotSetCommand(&notification, receiver.dataId.sid, storeId);
result = hkQueue->sendMessage(receiver.destinationQueue, &notification);
if (result != returnvalue::OK) {
status = result;
}
toReset = dataSet;
}
if (toReset != nullptr) {
handleChangeResetLogic(receiver.dataType, receiver.dataId, toReset);
}
return returnvalue::OK;
}
ReturnValue_t LocalDataPoolManager::addUpdateToStore(HousekeepingSnapshot& updatePacket,
store_address_t& storeId) {
size_t updatePacketSize = updatePacket.getSerializedSize();
uint8_t* storePtr = nullptr;
ReturnValue_t result =
ipcStore->getFreeElement(&storeId, updatePacket.getSerializedSize(), &storePtr);
if (result != returnvalue::OK) {
return result;
}
size_t serializedSize = 0;
result = updatePacket.serialize(&storePtr, &serializedSize, updatePacketSize,
SerializeIF::Endianness::MACHINE);
return result;
;
}
void LocalDataPoolManager::handleChangeResetLogic(DataType type, DataId dataId,
MarkChangedIF* toReset) {
for (auto& changeInfo : hkUpdateResetList) {
if (changeInfo.dataType != type) {
continue;
}
if ((changeInfo.dataType == DataType::DATA_SET) and (changeInfo.dataId.sid != dataId.sid)) {
continue;
}
if ((changeInfo.dataType == DataType::LOCAL_POOL_VARIABLE) and
(changeInfo.dataId.localPoolId != dataId.localPoolId)) {
continue;
}
/* Only one update recipient, we can reset changes status immediately */
if (changeInfo.updateCounter <= 1) {
toReset->setChanged(false);
}
/* All recipients have been notified, reset the changed flag */
else if (changeInfo.currentUpdateCounter <= 1) {
toReset->setChanged(false);
changeInfo.currentUpdateCounter = 0;
}
/* Not all recipiens have been notified yet, decrement */
else {
changeInfo.currentUpdateCounter--;
}
return;
}
}
void LocalDataPoolManager::resetHkUpdateResetHelper() {
for (auto& changeInfo : hkUpdateResetList) {
changeInfo.currentUpdateCounter = changeInfo.updateCounter;
}
}
ReturnValue_t LocalDataPoolManager::subscribeForRegularPeriodicPacket(
subdp::RegularHkPeriodicParams params) {
return subscribeForPeriodicPacket(params);
}
ReturnValue_t LocalDataPoolManager::subscribeForDiagPeriodicPacket(
subdp::DiagnosticsHkPeriodicParams params) {
return subscribeForPeriodicPacket(params);
}
ReturnValue_t LocalDataPoolManager::subscribeForPeriodicPacket(subdp::ParamsBase& params) {
struct HkReceiver hkReceiver;
hkReceiver.dataId.sid = params.sid;
hkReceiver.reportingType = ReportingType::PERIODIC;
hkReceiver.dataType = DataType::DATA_SET;
if (params.receiver == MessageQueueIF::NO_QUEUE) {
hkReceiver.destinationQueue = hkDestinationId;
} else {
hkReceiver.destinationQueue = params.receiver;
}
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, params.sid);
if (dataSet != nullptr) {
LocalPoolDataSetAttorney::setReportingEnabled(*dataSet, params.enableReporting);
LocalPoolDataSetAttorney::initializePeriodicHelper(*dataSet, params.collectionInterval,
owner->getPeriodicOperationFrequency());
}
hkReceivers.push_back(hkReceiver);
return returnvalue::OK;
}
ReturnValue_t LocalDataPoolManager::subscribeForRegularUpdatePacket(
subdp::RegularHkUpdateParams params) {
return subscribeForUpdatePacket(params);
}
ReturnValue_t LocalDataPoolManager::subscribeForDiagUpdatePacket(
subdp::DiagnosticsHkUpdateParams params) {
return subscribeForUpdatePacket(params);
}
ReturnValue_t LocalDataPoolManager::subscribeForUpdatePacket(subdp::ParamsBase& params) {
struct HkReceiver hkReceiver;
hkReceiver.dataId.sid = params.sid;
hkReceiver.reportingType = ReportingType::UPDATE_HK;
hkReceiver.dataType = DataType::DATA_SET;
if (params.receiver == MessageQueueIF::NO_QUEUE) {
hkReceiver.destinationQueue = hkDestinationId;
} else {
hkReceiver.destinationQueue = params.receiver;
}
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, params.sid);
if (dataSet != nullptr) {
LocalPoolDataSetAttorney::setReportingEnabled(*dataSet, true);
}
hkReceivers.push_back(hkReceiver);
handleHkUpdateResetListInsertion(hkReceiver.dataType, hkReceiver.dataId);
return returnvalue::OK;
}
ReturnValue_t LocalDataPoolManager::subscribeForSetUpdateMessage(const uint32_t setId,
object_id_t destinationObject,
MessageQueueId_t targetQueueId,
bool generateSnapshot) {
struct HkReceiver hkReceiver;
hkReceiver.dataType = DataType::DATA_SET;
hkReceiver.dataId.sid = sid_t(owner->getObjectId(), setId);
hkReceiver.destinationQueue = targetQueueId;
hkReceiver.objectId = destinationObject;
if (generateSnapshot) {
hkReceiver.reportingType = ReportingType::UPDATE_SNAPSHOT;
} else {
hkReceiver.reportingType = ReportingType::UPDATE_NOTIFICATION;
}
hkReceivers.push_back(hkReceiver);
handleHkUpdateResetListInsertion(hkReceiver.dataType, hkReceiver.dataId);
return returnvalue::OK;
}
ReturnValue_t LocalDataPoolManager::subscribeForVariableUpdateMessage(
const lp_id_t localPoolId, object_id_t destinationObject, MessageQueueId_t targetQueueId,
bool generateSnapshot) {
struct HkReceiver hkReceiver;
hkReceiver.dataType = DataType::LOCAL_POOL_VARIABLE;
hkReceiver.dataId.localPoolId = localPoolId;
hkReceiver.destinationQueue = targetQueueId;
hkReceiver.objectId = destinationObject;
if (generateSnapshot) {
hkReceiver.reportingType = ReportingType::UPDATE_SNAPSHOT;
} else {
hkReceiver.reportingType = ReportingType::UPDATE_NOTIFICATION;
}
hkReceivers.push_back(hkReceiver);
handleHkUpdateResetListInsertion(hkReceiver.dataType, hkReceiver.dataId);
return returnvalue::OK;
}
void LocalDataPoolManager::handleHkUpdateResetListInsertion(DataType dataType, DataId dataId) {
for (auto& updateResetStruct : hkUpdateResetList) {
if (dataType == DataType::DATA_SET) {
if (updateResetStruct.dataId.sid == dataId.sid) {
updateResetStruct.updateCounter++;
updateResetStruct.currentUpdateCounter++;
return;
}
} else {
if (updateResetStruct.dataId.localPoolId == dataId.localPoolId) {
updateResetStruct.updateCounter++;
updateResetStruct.currentUpdateCounter++;
return;
}
}
}
HkUpdateResetHelper hkUpdateResetHelper;
hkUpdateResetHelper.currentUpdateCounter = 1;
hkUpdateResetHelper.updateCounter = 1;
hkUpdateResetHelper.dataType = dataType;
if (dataType == DataType::DATA_SET) {
hkUpdateResetHelper.dataId.sid = dataId.sid;
} else {
hkUpdateResetHelper.dataId.localPoolId = dataId.localPoolId;
}
hkUpdateResetList.push_back(hkUpdateResetHelper);
}
ReturnValue_t LocalDataPoolManager::handleHousekeepingMessage(CommandMessage* message) {
Command_t command = message->getCommand();
sid_t sid = HousekeepingMessage::getSid(message);
ReturnValue_t result = returnvalue::OK;
switch (command) {
// Houskeeping interface handling.
case (HousekeepingMessage::ENABLE_PERIODIC_HK_REPORT_GENERATION): {
result = togglePeriodicGeneration(sid, true);
break;
}
case (HousekeepingMessage::DISABLE_PERIODIC_HK_REPORT_GENERATION): {
result = togglePeriodicGeneration(sid, false);
break;
}
case (HousekeepingMessage::REPORT_HK_REPORT_STRUCTURES): {
result = generateSetStructurePacket(sid);
if (result == returnvalue::OK) {
return result;
}
break;
}
case (HousekeepingMessage::MODIFY_PARAMETER_REPORT_COLLECTION_INTERVAL): {
float newCollIntvl = 0;
HousekeepingMessage::getCollectionIntervalModificationCommand(message, &newCollIntvl);
result = changeCollectionInterval(sid, newCollIntvl);
break;
}
case (HousekeepingMessage::GENERATE_ONE_PARAMETER_REPORT): {
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
if (dataSet == nullptr) {
printWarningOrError(sif::OutputTypes::OUT_WARNING, "handleHousekeepingMessage",
DATASET_NOT_FOUND);
return DATASET_NOT_FOUND;
}
return generateHousekeepingPacket(HousekeepingMessage::getSid(message), dataSet, true);
}
/* Notification handling */
case (HousekeepingMessage::UPDATE_NOTIFICATION_SET): {
owner->handleChangedDataset(sid);
return returnvalue::OK;
}
case (HousekeepingMessage::UPDATE_NOTIFICATION_VARIABLE): {
gp_id_t globPoolId = HousekeepingMessage::getUpdateNotificationVariableCommand(message);
owner->handleChangedPoolVariable(globPoolId);
return returnvalue::OK;
}
case (HousekeepingMessage::UPDATE_SNAPSHOT_SET): {
store_address_t storeId;
HousekeepingMessage::getUpdateSnapshotSetCommand(message, &storeId);
bool clearMessage = true;
owner->handleChangedDataset(sid, storeId, &clearMessage);
if (clearMessage) {
message->clear();
}
return returnvalue::OK;
}
case (HousekeepingMessage::UPDATE_SNAPSHOT_VARIABLE): {
store_address_t storeId;
gp_id_t globPoolId = HousekeepingMessage::getUpdateSnapshotVariableCommand(message, &storeId);
bool clearMessage = true;
owner->handleChangedPoolVariable(globPoolId, storeId, &clearMessage);
if (clearMessage) {
message->clear();
}
return returnvalue::OK;
}
default:
return CommandMessageIF::UNKNOWN_COMMAND;
}
CommandMessage reply;
if (result != returnvalue::OK) {
if (result == WRONG_HK_PACKET_TYPE) {
printWarningOrError(sif::OutputTypes::OUT_WARNING, "handleHousekeepingMessage",
WRONG_HK_PACKET_TYPE);
}
HousekeepingMessage::setHkRequestFailureReply(&reply, sid, result);
} else {
HousekeepingMessage::setHkRequestSuccessReply(&reply, sid);
}
hkQueue->sendMessage(hkDestinationId, &reply);
return result;
}
ReturnValue_t LocalDataPoolManager::printPoolEntry(lp_id_t localPoolId) {
auto poolIter = localPoolMap.find(localPoolId);
if (poolIter == localPoolMap.end()) {
printWarningOrError(sif::OutputTypes::OUT_WARNING, "printPoolEntry",
localpool::POOL_ENTRY_NOT_FOUND);
return localpool::POOL_ENTRY_NOT_FOUND;
}
poolIter->second->print();
return returnvalue::OK;
}
MutexIF* LocalDataPoolManager::getMutexHandle() { return mutex; }
HasLocalDataPoolIF* LocalDataPoolManager::getOwner() { return owner; }
ReturnValue_t LocalDataPoolManager::generateHousekeepingPacket(sid_t sid,
LocalPoolDataSetBase* dataSet,
bool forDownlink,
MessageQueueId_t destination) {
if (dataSet == nullptr) {
/* Configuration error. */
printWarningOrError(sif::OutputTypes::OUT_WARNING, "generateHousekeepingPacket",
DATASET_NOT_FOUND);
return DATASET_NOT_FOUND;
}
store_address_t storeId;
HousekeepingPacketDownlink hkPacket(sid, dataSet);
size_t serializedSize = 0;
ReturnValue_t result =
serializeHkPacketIntoStore(hkPacket, storeId, forDownlink, &serializedSize);
if (result != returnvalue::OK or serializedSize == 0) {
return result;
}
/* Now we set a HK message and send it the HK packet destination. */
CommandMessage hkMessage;
HousekeepingMessage::setHkReportReply(&hkMessage, sid, storeId);
if (hkQueue == nullptr) {
/* Error, no queue available to send packet with. */
printWarningOrError(sif::OutputTypes::OUT_WARNING, "generateHousekeepingPacket",
QUEUE_OR_DESTINATION_INVALID);
return QUEUE_OR_DESTINATION_INVALID;
}
if (destination == MessageQueueIF::NO_QUEUE) {
if (hkDestinationId == MessageQueueIF::NO_QUEUE) {
/* Error, all destinations invalid */
printWarningOrError(sif::OutputTypes::OUT_WARNING, "generateHousekeepingPacket",
QUEUE_OR_DESTINATION_INVALID);
return QUEUE_OR_DESTINATION_INVALID;
}
destination = hkDestinationId;
}
return hkQueue->sendMessage(destination, &hkMessage);
}
ReturnValue_t LocalDataPoolManager::serializeHkPacketIntoStore(HousekeepingPacketDownlink& hkPacket,
store_address_t& storeId,
bool forDownlink,
size_t* serializedSize) {
uint8_t* dataPtr = nullptr;
const size_t maxSize = hkPacket.getSerializedSize();
ReturnValue_t result = ipcStore->getFreeElement(&storeId, maxSize, &dataPtr);
if (result != returnvalue::OK) {
return result;
}
if (forDownlink) {
return hkPacket.serialize(&dataPtr, serializedSize, maxSize, SerializeIF::Endianness::BIG);
}
return hkPacket.serialize(&dataPtr, serializedSize, maxSize, SerializeIF::Endianness::MACHINE);
}
void LocalDataPoolManager::performPeriodicHkGeneration(HkReceiver& receiver) {
sid_t sid = receiver.dataId.sid;
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
if (dataSet == nullptr) {
printWarningOrError(sif::OutputTypes::OUT_WARNING, "performPeriodicHkGeneration",
DATASET_NOT_FOUND);
return;
}
if (not LocalPoolDataSetAttorney::getReportingEnabled(*dataSet)) {
return;
}
PeriodicHousekeepingHelper* periodicHelper =
LocalPoolDataSetAttorney::getPeriodicHelper(*dataSet);
if (periodicHelper == nullptr) {
/* Configuration error */
return;
}
if (not periodicHelper->checkOpNecessary()) {
return;
}
ReturnValue_t result = generateHousekeepingPacket(sid, dataSet, true);
if (result != returnvalue::OK) {
/* Configuration error */
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "LocalDataPoolManager::performPeriodicHkOperation: HK generation failed."
<< std::endl;
#else
sif::printWarning("LocalDataPoolManager::performPeriodicHkOperation: HK generation failed.\n");
#endif
}
}
ReturnValue_t LocalDataPoolManager::togglePeriodicGeneration(sid_t sid, bool enable) {
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
if (dataSet == nullptr) {
printWarningOrError(sif::OutputTypes::OUT_WARNING, "togglePeriodicGeneration",
DATASET_NOT_FOUND);
return DATASET_NOT_FOUND;
}
if ((LocalPoolDataSetAttorney::getReportingEnabled(*dataSet) and enable) or
(not LocalPoolDataSetAttorney::getReportingEnabled(*dataSet) and not enable)) {
return returnvalue::OK;
}
LocalPoolDataSetAttorney::setReportingEnabled(*dataSet, enable);
return returnvalue::OK;
}
ReturnValue_t LocalDataPoolManager::changeCollectionInterval(sid_t sid,
float newCollectionInterval) {
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
if (dataSet == nullptr) {
printWarningOrError(sif::OutputTypes::OUT_WARNING, "changeCollectionInterval",
DATASET_NOT_FOUND);
return DATASET_NOT_FOUND;
}
PeriodicHousekeepingHelper* periodicHelper =
LocalPoolDataSetAttorney::getPeriodicHelper(*dataSet);
if (periodicHelper == nullptr) {
/* Configuration error, set might not have a corresponding pool manager */
return PERIODIC_HELPER_INVALID;
}
periodicHelper->changeCollectionInterval(newCollectionInterval);
return returnvalue::OK;
}
ReturnValue_t LocalDataPoolManager::generateSetStructurePacket(sid_t sid) {
/* Get and check dataset first. */
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
if (dataSet == nullptr) {
printWarningOrError(sif::OutputTypes::OUT_WARNING, "performPeriodicHkGeneration",
DATASET_NOT_FOUND);
return DATASET_NOT_FOUND;
}
bool valid = dataSet->isValid();
bool reportingEnabled = LocalPoolDataSetAttorney::getReportingEnabled(*dataSet);
float collectionInterval =
LocalPoolDataSetAttorney::getPeriodicHelper(*dataSet)->getCollectionIntervalInSeconds();
// Generate set packet which can be serialized.
HousekeepingSetPacket setPacket(sid, reportingEnabled, valid, collectionInterval, dataSet);
size_t expectedSize = setPacket.getSerializedSize();
uint8_t* storePtr = nullptr;
store_address_t storeId;
ReturnValue_t result = ipcStore->getFreeElement(&storeId, expectedSize, &storePtr);
if (result != returnvalue::OK) {
printWarningOrError(sif::OutputTypes::OUT_ERROR, "generateSetStructurePacket",
returnvalue::FAILED, "Could not get free element from IPC store.");
return result;
}
// Serialize set packet into store.
size_t size = 0;
result = setPacket.serialize(&storePtr, &size, expectedSize, SerializeIF::Endianness::BIG);
if (result != returnvalue::OK) {
ipcStore->deleteData(storeId);
return result;
}
if (expectedSize != size) {
printWarningOrError(sif::OutputTypes::OUT_WARNING, "generateSetStructurePacket",
returnvalue::FAILED, "Expected size is not equal to serialized size");
}
// Send structure reporting reply.
CommandMessage reply;
HousekeepingMessage::setHkStuctureReportReply(&reply, sid, storeId);
result = hkQueue->reply(&reply);
if (result != returnvalue::OK) {
ipcStore->deleteData(storeId);
}
return result;
}
void LocalDataPoolManager::clearReceiversList() {
/* Clear the vector completely and releases allocated memory. */
HkReceivers().swap(hkReceivers);
/* Also clear the reset helper if it exists */
HkUpdateResetList().swap(hkUpdateResetList);
}
MutexIF* LocalDataPoolManager::getLocalPoolMutex() { return this->mutex; }
object_id_t LocalDataPoolManager::getCreatorObjectId() const { return owner->getObjectId(); }
void LocalDataPoolManager::printWarningOrError(sif::OutputTypes outputType,
const char* functionName, ReturnValue_t error,
const char* errorPrint) {
#if FSFW_VERBOSE_LEVEL >= 1
if (errorPrint == nullptr) {
if (error == DATASET_NOT_FOUND) {
errorPrint = "Dataset not found";
} else if (error == POOLOBJECT_NOT_FOUND) {
errorPrint = "Pool Object not found";
} else if (error == WRONG_HK_PACKET_TYPE) {
errorPrint = "Wrong Packet Type";
} else if (error == returnvalue::FAILED) {
if (outputType == sif::OutputTypes::OUT_WARNING) {
errorPrint = "Generic Warning";
} else {
errorPrint = "Generic error";
}
} else if (error == QUEUE_OR_DESTINATION_INVALID) {
errorPrint = "Queue or destination not set";
} else if (error == localpool::POOL_ENTRY_TYPE_CONFLICT) {
errorPrint = "Pool entry type conflict";
} else if (error == localpool::POOL_ENTRY_NOT_FOUND) {
errorPrint = "Pool entry not found";
} else {
errorPrint = "Unknown error";
}
}
object_id_t objectId = 0xffffffff;
if (owner != nullptr) {
objectId = owner->getObjectId();
}
if (outputType == sif::OutputTypes::OUT_WARNING) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "LocalDataPoolManager::" << functionName << ": Object ID 0x" << std::setw(8)
<< std::setfill('0') << std::hex << objectId << " | " << errorPrint << std::dec
<< std::setfill(' ') << std::endl;
#else
sif::printWarning("LocalDataPoolManager::%s: Object ID 0x%08x | %s\n", functionName, objectId,
errorPrint);
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
} else if (outputType == sif::OutputTypes::OUT_ERROR) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "LocalDataPoolManager::" << functionName << ": Object ID 0x" << std::setw(8)
<< std::setfill('0') << std::hex << objectId << " | " << errorPrint << std::dec
<< std::setfill(' ') << std::endl;
#else
sif::printError("LocalDataPoolManager::%s: Object ID 0x%08x | %s\n", functionName, objectId,
errorPrint);
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
}
#endif /* #if FSFW_VERBOSE_LEVEL >= 1 */
}
LocalDataPoolManager* LocalDataPoolManager::getPoolManagerHandle() { return this; }
void LocalDataPoolManager::setHkDestinationId(MessageQueueId_t hkDestId) {
hkDestinationId = hkDestId;
}
@@ -1,380 +0,0 @@
#ifndef FSFW_DATAPOOLLOCAL_LOCALDATAPOOLMANAGER_H_
#define FSFW_DATAPOOLLOCAL_LOCALDATAPOOLMANAGER_H_
#include <map>
#include <vector>
#include "AccessLocalPoolF.h"
#include "ProvidesDataPoolSubscriptionIF.h"
#include "fsfw/datapool/DataSetIF.h"
#include "fsfw/datapool/PoolEntry.h"
#include "fsfw/housekeeping/AcceptsHkPacketsIF.h"
#include "fsfw/housekeeping/HousekeepingMessage.h"
#include "fsfw/housekeeping/HousekeepingPacketDownlink.h"
#include "fsfw/housekeeping/PeriodicHousekeepingHelper.h"
#include "fsfw/ipc/CommandMessage.h"
#include "fsfw/ipc/MessageQueueIF.h"
#include "fsfw/ipc/MutexGuard.h"
#include "fsfw/ipc/MutexIF.h"
#include "fsfw/objectmanager/SystemObjectIF.h"
#include "fsfw/serviceinterface/ServiceInterface.h"
namespace Factory {
void setStaticFrameworkObjectIds();
}
class LocalPoolDataSetBase;
class HousekeepingSnapshot;
class HasLocalDataPoolIF;
class LocalDataPool;
/**
* @brief This class is the managing instance for the local data pool.
* @details
* The actual data pool structure is a member of this class. Any class which
* has a local data pool shall have this manager class as a member and implement
* the HasLocalDataPoolIF.
*
* The manager offers some adaption points and functions which can be used
* by the owning class to simplify data handling significantly.
*
* Please ensure that both initialize and initializeAfterTaskCreation are
* called at some point by the owning class in the respective functions of the
* same name!
*
* Users of the data pool use the helper classes LocalDataSet,
* LocalPoolVariable and LocalPoolVector to access pool entries in
* a thread-safe and efficient way.
*
* The local data pools employ a blackboard logic: Only the most recent
* value is stored. The helper classes offer a read() and commit() interface
* through the PoolVariableIF which is used to read and update values.
* Each pool entry has a valid state too.
* @author R. Mueller
*/
class LocalDataPoolManager : public ProvidesDataPoolSubscriptionIF, public AccessPoolManagerIF {
friend void(Factory::setStaticFrameworkObjectIds)();
//! Some classes using the pool manager directly need to access class internals of the
//! manager. The attorney provides granular control of access to these internals.
friend class LocalDpManagerAttorney;
public:
static constexpr uint8_t INTERFACE_ID = CLASS_ID::HOUSEKEEPING_MANAGER;
static constexpr ReturnValue_t QUEUE_OR_DESTINATION_INVALID = MAKE_RETURN_CODE(0);
static constexpr ReturnValue_t WRONG_HK_PACKET_TYPE = MAKE_RETURN_CODE(1);
static constexpr ReturnValue_t REPORTING_STATUS_UNCHANGED = MAKE_RETURN_CODE(2);
static constexpr ReturnValue_t PERIODIC_HELPER_INVALID = MAKE_RETURN_CODE(3);
static constexpr ReturnValue_t POOLOBJECT_NOT_FOUND = MAKE_RETURN_CODE(4);
static constexpr ReturnValue_t DATASET_NOT_FOUND = MAKE_RETURN_CODE(5);
/**
* This constructor is used by a class which wants to implement
* a personal local data pool. The queueToUse can be supplied if it
* is already known.
*
* initialize() has to be called in any case before using the object!
* @param owner
* @param queueToUse
* @param appendValidityBuffer Specify whether a buffer containing the
* validity state is generated when serializing or deserializing packets.
*/
LocalDataPoolManager(HasLocalDataPoolIF* owner, MessageQueueIF* queueToUse,
bool appendValidityBuffer = true);
~LocalDataPoolManager() override;
void setHkDestinationId(MessageQueueId_t hkDestId);
/**
* Assigns the queue to use. Make sure to call this in the #initialize
* function of the owner.
* @param queueToUse
* @param nonDiagInvlFactor See #setNonDiagnosticIntervalFactor doc
* @return
*/
ReturnValue_t initialize(MessageQueueIF* queueToUse);
/**
* Initializes the map by calling the map initialization function and
* setting the periodic factor for non-diagnostic packets.
* Don't forget to call this in the #initializeAfterTaskCreation call of
* the owner, otherwise the map will be invalid!
* @param nonDiagInvlFactor
* @return
*/
ReturnValue_t initializeAfterTaskCreation();
/**
* @brief This should be called in the periodic handler of the owner.
* @details
* This in generally called in the #performOperation function of the owner.
* It performs all the periodic functionalities of the data pool manager,
* for example generating periodic HK packets.
* Marked virtual as an adaption point for custom data pool managers.
* @return
*/
virtual ReturnValue_t performHkOperation();
/**
* @brief Subscribe for a notification message which will be sent
* if a dataset has changed.
* @details
* This subscription mechanism will generally be used internally by
* other software components.
* @param setId Set ID of the set to receive update messages from.
* @param destinationObject
* @param targetQueueId
* @param generateSnapshot If this is set to true, a copy of the current
* data with a timestamp will be generated and sent via message.
* Otherwise, only an notification message is sent.
* @return
*/
ReturnValue_t subscribeForSetUpdateMessage(uint32_t setId, object_id_t destinationObject,
MessageQueueId_t targetQueueId,
bool generateSnapshot) override;
/**
* @brief Subscribe for an notification message which will be sent if a
* pool variable has changed.
* @details
* This subscription mechanism will generally be used internally by
* other software components.
* @param localPoolId Pool ID of the pool variable
* @param destinationObject
* @param targetQueueId
* @param generateSnapshot If this is set to true, a copy of the current
* data with a timestamp will be generated and sent via message.
* Otherwise, only an notification message is sent.
* @return
*/
ReturnValue_t subscribeForVariableUpdateMessage(lp_id_t localPoolId,
object_id_t destinationObject,
MessageQueueId_t targetQueueId,
bool generateSnapshot) override;
/**
* @brief The manager is also able to handle housekeeping messages.
* @details
* This most commonly is used to handle messages for the housekeeping
* interface, but the manager is also able to handle update notifications
* and calls a special function which can be overriden by a child class
* to handle data set or pool variable updates. This is relevant
* for classes like controllers which have their own local datapool
* but pull their data from other local datapools.
* @param message
* @return
*/
virtual ReturnValue_t handleHousekeepingMessage(CommandMessage* message);
/**
* Generate a housekeeping packet with a given SID.
* @param sid
* @return
*/
ReturnValue_t generateHousekeepingPacket(sid_t sid, LocalPoolDataSetBase* dataSet,
bool forDownlink,
MessageQueueId_t destination = MessageQueueIF::NO_QUEUE);
ReturnValue_t changeCollectionInterval(sid_t sid, float newCollectionInterval);
HasLocalDataPoolIF* getOwner();
ReturnValue_t printPoolEntry(lp_id_t localPoolId);
/**
* Different types of housekeeping reporting are possible.
* 1. PERIODIC:
* HK packets are generated in fixed intervals and sent to
* destination. Fromat will be raw.
* 2. UPDATE_NOTIFICATION:
* Notification will be sent out if HK data has changed.
* 3. UPDATE_SNAPSHOT:
* HK packets are only generated if explicitely requested.
* Propably not necessary, just use multiple local data sets or
* shared datasets.
*/
enum class ReportingType : uint8_t {
//! Periodic generation of HK packets.
PERIODIC,
//! Housekeeping packet will be generated if values have changed.
UPDATE_HK,
//! Update notification will be sent out as message.
UPDATE_NOTIFICATION,
//! Notification will be sent out as message and a snapshot of the
//! current data will be generated.
UPDATE_SNAPSHOT,
};
/** Different data types are possible in the HK receiver map. For example, updates can be
requested for full datasets or for single pool variables. Periodic reporting is only possible
for data sets. */
enum class DataType : uint8_t { LOCAL_POOL_VARIABLE, DATA_SET };
/* Copying forbidden */
LocalDataPoolManager(const LocalDataPoolManager&) = delete;
LocalDataPoolManager operator=(const LocalDataPoolManager&) = delete;
/**
* This function can be used to clear the receivers list. This is
* intended for test functions and not for regular operations, because
* the insertion operations allocate dynamically.
*/
void clearReceiversList();
[[nodiscard]] object_id_t getCreatorObjectId() const;
/**
* Get the pointer to the mutex. Can be used to lock the data pool
* externally. Use with care and don't forget to unlock locked mutexes!
* For now, only friend classes can accss this function.
* @return
*/
MutexIF* getMutexHandle();
LocalDataPoolManager* getPoolManagerHandle() override;
ReturnValue_t subscribeForRegularPeriodicPacket(subdp::RegularHkPeriodicParams params) override;
ReturnValue_t subscribeForDiagPeriodicPacket(subdp::DiagnosticsHkPeriodicParams params) override;
ReturnValue_t subscribeForRegularUpdatePacket(subdp::RegularHkUpdateParams params) override;
ReturnValue_t subscribeForDiagUpdatePacket(subdp::DiagnosticsHkUpdateParams params) override;
protected:
ReturnValue_t subscribeForPeriodicPacket(subdp::ParamsBase& params);
ReturnValue_t subscribeForUpdatePacket(subdp::ParamsBase& params);
/** Core data structure for the actual pool data */
localpool::DataPool localPoolMap;
/** Every housekeeping data manager has a mutex to protect access
to it's data pool. */
MutexIF* mutex = nullptr;
/** The class which actually owns the manager (and its datapool). */
HasLocalDataPoolIF* owner = nullptr;
uint8_t nonDiagnosticIntervalFactor = 0;
/** Default receiver for periodic HK packets */
static object_id_t defaultHkDestination;
MessageQueueId_t hkDestinationId = MessageQueueIF::NO_QUEUE;
union DataId {
DataId() : sid() {};
sid_t sid;
lp_id_t localPoolId;
};
/** The data pool manager will keep an internal map of HK receivers. */
struct HkReceiver {
/** Object ID of receiver */
object_id_t objectId = objects::NO_OBJECT;
DataType dataType = DataType::DATA_SET;
DataId dataId;
ReportingType reportingType = ReportingType::PERIODIC;
MessageQueueId_t destinationQueue = MessageQueueIF::NO_QUEUE;
};
/** This vector will contain the list of HK receivers. */
using HkReceivers = std::vector<struct HkReceiver>;
HkReceivers hkReceivers;
struct HkUpdateResetHelper {
DataType dataType = DataType::DATA_SET;
DataId dataId;
uint8_t updateCounter;
uint8_t currentUpdateCounter;
};
using HkUpdateResetList = std::vector<struct HkUpdateResetHelper>;
/** This list is used to manage creating multiple update packets and only resetting
the update flag if all of them were created. */
HkUpdateResetList hkUpdateResetList = HkUpdateResetList();
/** This is the map holding the actual data. Should only be initialized
* once ! */
bool mapInitialized = false;
/** This specifies whether a validity buffer is appended at the end
* of generated housekeeping packets. */
bool appendValidityBuffer = true;
/**
* @brief Queue used for communication, for example commands.
* Is also used to send messages. Can be set either in the constructor
* or in the initialize() function.
*/
MessageQueueIF* hkQueue = nullptr;
/** Global IPC store is used to store all packets. */
StorageManagerIF* ipcStore = nullptr;
/**
* Read a variable by supplying its local pool ID and assign the pool
* entry to the supplied PoolEntry pointer. The type of the pool entry
* is deduced automatically. This call is not thread-safe!
* For now, only classes designated by the LocalDpManagerAttorney may use this function.
* @tparam T Type of the pool entry
* @param localPoolId Pool ID of the variable to read
* @param poolVar [out] Corresponding pool entry will be assigned to the
* supplied pointer.
* @return
*/
template <class T>
ReturnValue_t fetchPoolEntry(lp_id_t localPoolId, PoolEntry<T>** poolEntry);
/**
* This function is used to fill the local data pool map with pool
* entries. It should only be called once by the pool owner.
* @param localDataPoolMap
* @return
*/
ReturnValue_t initializeHousekeepingPoolEntriesOnce();
MutexIF* getLocalPoolMutex() override;
ReturnValue_t serializeHkPacketIntoStore(HousekeepingPacketDownlink& hkPacket,
store_address_t& storeId, bool forDownlink,
size_t* serializedSize);
void performPeriodicHkGeneration(HkReceiver& hkReceiver);
ReturnValue_t togglePeriodicGeneration(sid_t sid, bool enable);
ReturnValue_t generateSetStructurePacket(sid_t sid);
void handleHkUpdateResetListInsertion(DataType dataType, DataId dataId);
void handleChangeResetLogic(DataType type, DataId dataId, MarkChangedIF* toReset);
void resetHkUpdateResetHelper();
ReturnValue_t handleHkUpdate(HkReceiver& hkReceiver, ReturnValue_t& status);
ReturnValue_t handleNotificationUpdate(HkReceiver& hkReceiver, ReturnValue_t& status);
ReturnValue_t handleNotificationSnapshot(HkReceiver& hkReceiver, ReturnValue_t& status);
ReturnValue_t addUpdateToStore(HousekeepingSnapshot& updatePacket, store_address_t& storeId);
void printWarningOrError(sif::OutputTypes outputType, const char* functionName,
ReturnValue_t errorCode = returnvalue::FAILED,
const char* errorPrint = nullptr);
};
template <class T>
inline ReturnValue_t LocalDataPoolManager::fetchPoolEntry(lp_id_t localPoolId,
PoolEntry<T>** poolEntry) {
if (poolEntry == nullptr) {
return returnvalue::FAILED;
}
auto poolIter = localPoolMap.find(localPoolId);
if (poolIter == localPoolMap.end()) {
printWarningOrError(sif::OutputTypes::OUT_WARNING, "fetchPoolEntry",
localpool::POOL_ENTRY_NOT_FOUND);
return localpool::POOL_ENTRY_NOT_FOUND;
}
*poolEntry = dynamic_cast<PoolEntry<T>*>(poolIter->second);
if (*poolEntry == nullptr) {
printWarningOrError(sif::OutputTypes::OUT_WARNING, "fetchPoolEntry",
localpool::POOL_ENTRY_TYPE_CONFLICT);
return localpool::POOL_ENTRY_TYPE_CONFLICT;
}
return returnvalue::OK;
}
#endif /* FSFW_DATAPOOLLOCAL_LOCALDATAPOOLMANAGER_H_ */
-21
View File
@@ -1,21 +0,0 @@
#include "fsfw/datapoollocal/LocalDataSet.h"
#include <cmath>
#include <cstring>
#include "fsfw/datapoollocal/LocalDataPoolManager.h"
#include "fsfw/serialize/SerializeAdapter.h"
LocalDataSet::LocalDataSet(HasLocalDataPoolIF *hkOwner, uint32_t setId,
const size_t maxNumberOfVariables)
: LocalPoolDataSetBase(hkOwner, setId, nullptr, maxNumberOfVariables),
poolVarList(maxNumberOfVariables) {
this->setContainer(poolVarList.data());
}
LocalDataSet::LocalDataSet(sid_t sid, const size_t maxNumberOfVariables)
: LocalPoolDataSetBase(sid, nullptr, maxNumberOfVariables), poolVarList(maxNumberOfVariables) {
this->setContainer(poolVarList.data());
}
LocalDataSet::~LocalDataSet() {}
@@ -1,291 +0,0 @@
#include <cmath>
#include <cstring>
#include "fsfw/datapoollocal.h"
#include "fsfw/datapoollocal/LocalDataPoolManager.h"
#include "fsfw/globalfunctions/bitutility.h"
#include "fsfw/housekeeping/PeriodicHousekeepingHelper.h"
#include "fsfw/objectmanager/ObjectManager.h"
#include "fsfw/serialize/SerializeAdapter.h"
#include "fsfw/serviceinterface/ServiceInterface.h"
#include "internal/HasLocalDpIFUserAttorney.h"
LocalPoolDataSetBase::LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner, uint32_t setId,
PoolVariableIF **registeredVariablesArray,
const size_t maxNumberOfVariables, bool periodicHandling)
: PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
if (hkOwner == nullptr) {
// Configuration error.
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "LocalPoolDataSetBase::LocalPoolDataSetBase: Owner "
<< "invalid!" << std::endl;
#else
sif::printError(
"LocalPoolDataSetBase::LocalPoolDataSetBase: Owner "
"invalid!\n\r");
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
return;
}
AccessPoolManagerIF *accessor = HasLocalDpIFUserAttorney::getAccessorHandle(hkOwner);
if (accessor != nullptr) {
poolManager = accessor->getPoolManagerHandle();
mutexIfSingleDataCreator = accessor->getLocalPoolMutex();
}
this->sid.objectId = hkOwner->getObjectId();
this->sid.ownerSetId = setId;
/* Data creators get a periodic helper for periodic HK data generation. */
if (periodicHandling) {
periodicHelper = new PeriodicHousekeepingHelper(this);
}
}
LocalPoolDataSetBase::LocalPoolDataSetBase(sid_t sid, PoolVariableIF **registeredVariablesArray,
const size_t maxNumberOfVariables)
: PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
HasLocalDataPoolIF *hkOwner = ObjectManager::instance()->get<HasLocalDataPoolIF>(sid.objectId);
if (hkOwner != nullptr) {
AccessPoolManagerIF *accessor = HasLocalDpIFUserAttorney::getAccessorHandle(hkOwner);
if (accessor != nullptr) {
mutexIfSingleDataCreator = accessor->getLocalPoolMutex();
poolManager = accessor->getPoolManagerHandle();
}
}
this->sid = sid;
}
LocalPoolDataSetBase::LocalPoolDataSetBase(PoolVariableIF **registeredVariablesArray,
const size_t maxNumberOfVariables,
bool protectEveryReadCommitCall)
: PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
this->setReadCommitProtectionBehaviour(protectEveryReadCommitCall);
}
LocalPoolDataSetBase::~LocalPoolDataSetBase() {
/* We only delete objects which were created in the class constructor */
if (periodicHelper != nullptr) {
delete periodicHelper;
}
/* In case set was read but not comitted, we commit all variables with an invalid state */
if (state == States::STATE_SET_WAS_READ) {
for (uint16_t count = 0; count < fillCount; count++) {
if (registeredVariables[count] != nullptr) {
registeredVariables[count]->setValid(false);
registeredVariables[count]->commit(MutexIF::TimeoutType::WAITING, 20);
}
}
}
}
ReturnValue_t LocalPoolDataSetBase::lockDataPool(MutexIF::TimeoutType timeoutType,
uint32_t timeoutMs) {
if (mutexIfSingleDataCreator != nullptr) {
return mutexIfSingleDataCreator->lockMutex(timeoutType, timeoutMs);
}
return returnvalue::OK;
}
ReturnValue_t LocalPoolDataSetBase::serializeWithValidityBuffer(
uint8_t **buffer, size_t *size, size_t maxSize,
SerializeIF::Endianness streamEndianness) const {
ReturnValue_t result = returnvalue::OK;
const uint8_t validityMaskSize = std::ceil(static_cast<float>(fillCount) / 8.0);
uint8_t *validityPtr = nullptr;
#if defined(_MSC_VER) || defined(__clang__)
// Use a std::vector here because MSVC will (rightly) not create a fixed size array
// with a non constant size specifier. The Apple compiler (LLVM) will not accept
// the initialization of a variable sized array
std::vector<uint8_t> validityMask(validityMaskSize, 0);
validityPtr = validityMask.data();
#else
uint8_t validityMask[validityMaskSize] = {};
validityPtr = validityMask;
#endif
uint8_t validBufferIndex = 0;
uint8_t validBufferIndexBit = 0;
for (uint16_t count = 0; count < fillCount; count++) {
if (registeredVariables[count]->isValid()) {
/* Set bit at correct position */
bitutil::set(validityPtr + validBufferIndex, validBufferIndexBit);
}
if (validBufferIndexBit == 7) {
validBufferIndex++;
validBufferIndexBit = 0;
} else {
validBufferIndexBit++;
}
result = registeredVariables[count]->serialize(buffer, size, maxSize, streamEndianness);
if (result != returnvalue::OK) {
return result;
}
}
if (*size + validityMaskSize > maxSize) {
return SerializeIF::BUFFER_TOO_SHORT;
}
// copy validity buffer to end
std::memcpy(*buffer, validityPtr, validityMaskSize);
*size += validityMaskSize;
return result;
}
ReturnValue_t LocalPoolDataSetBase::deSerializeWithValidityBuffer(
const uint8_t **buffer, size_t *size, SerializeIF::Endianness streamEndianness) {
ReturnValue_t result = returnvalue::FAILED;
for (uint16_t count = 0; count < fillCount; count++) {
result = registeredVariables[count]->deSerialize(buffer, size, streamEndianness);
if (result != returnvalue::OK) {
return result;
}
}
if (*size < std::ceil(static_cast<float>(fillCount) / 8.0)) {
return SerializeIF::STREAM_TOO_SHORT;
}
uint8_t validBufferIndex = 0;
uint8_t validBufferIndexBit = 0;
for (uint16_t count = 0; count < fillCount; count++) {
// set validity buffer here.
bool nextVarValid = false;
bitutil::get(*buffer + validBufferIndex, validBufferIndexBit, nextVarValid);
registeredVariables[count]->setValid(nextVarValid);
if (validBufferIndexBit == 7) {
validBufferIndex++;
validBufferIndexBit = 0;
} else {
validBufferIndexBit++;
}
}
return result;
}
ReturnValue_t LocalPoolDataSetBase::unlockDataPool() {
if (mutexIfSingleDataCreator != nullptr) {
return mutexIfSingleDataCreator->unlockMutex();
}
return returnvalue::OK;
}
ReturnValue_t LocalPoolDataSetBase::serializeLocalPoolIds(uint8_t **buffer, size_t *size,
size_t maxSize,
SerializeIF::Endianness streamEndianness,
bool serializeFillCount) const {
/* Serialize fill count as uint8_t */
uint8_t fillCount = this->fillCount;
if (serializeFillCount) {
SerializeAdapter::serialize(&fillCount, buffer, size, maxSize, streamEndianness);
}
for (uint16_t count = 0; count < fillCount; count++) {
lp_id_t currentPoolId = registeredVariables[count]->getDataPoolId();
auto result =
SerializeAdapter::serialize(&currentPoolId, buffer, size, maxSize, streamEndianness);
if (result != returnvalue::OK) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "LocalPoolDataSetBase::serializeLocalPoolIds: "
<< "Serialization error!" << std::endl;
#else
sif::printWarning(
"LocalPoolDataSetBase::serializeLocalPoolIds: "
"Serialization error!\n\r");
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
return result;
}
}
return returnvalue::OK;
}
uint8_t LocalPoolDataSetBase::getLocalPoolIdsSerializedSize(bool serializeFillCount) const {
if (serializeFillCount) {
return fillCount * sizeof(lp_id_t) + sizeof(uint8_t);
} else {
return fillCount * sizeof(lp_id_t);
}
}
size_t LocalPoolDataSetBase::getSerializedSize() const {
if (withValidityBuffer) {
uint8_t validityMaskSize = std::ceil(static_cast<float>(fillCount) / 8.0);
return validityMaskSize + PoolDataSetBase::getSerializedSize();
} else {
return PoolDataSetBase::getSerializedSize();
}
}
void LocalPoolDataSetBase::setValidityBufferGeneration(bool withValidityBuffer) {
this->withValidityBuffer = withValidityBuffer;
}
ReturnValue_t LocalPoolDataSetBase::deSerialize(const uint8_t **buffer, size_t *size,
SerializeIF::Endianness streamEndianness) {
if (withValidityBuffer) {
return this->deSerializeWithValidityBuffer(buffer, size, streamEndianness);
} else {
return PoolDataSetBase::deSerialize(buffer, size, streamEndianness);
}
}
ReturnValue_t LocalPoolDataSetBase::serialize(uint8_t **buffer, size_t *size, size_t maxSize,
SerializeIF::Endianness streamEndianness) const {
if (withValidityBuffer) {
return this->serializeWithValidityBuffer(buffer, size, maxSize, streamEndianness);
} else {
return PoolDataSetBase::serialize(buffer, size, maxSize, streamEndianness);
}
}
void LocalPoolDataSetBase::setReportingEnabled(bool reportingEnabled) {
this->reportingEnabled = reportingEnabled;
}
bool LocalPoolDataSetBase::getReportingEnabled() const { return reportingEnabled; }
void LocalPoolDataSetBase::initializePeriodicHelper(float collectionInterval,
dur_millis_t minimumPeriodicInterval) {
periodicHelper->initialize(collectionInterval, minimumPeriodicInterval);
}
void LocalPoolDataSetBase::setChanged(bool changed) { this->changed = changed; }
bool LocalPoolDataSetBase::hasChanged() const { return changed; }
sid_t LocalPoolDataSetBase::getSid() const { return sid; }
bool LocalPoolDataSetBase::isValid() const { return this->valid; }
void LocalPoolDataSetBase::setValidity(bool valid, bool setEntriesRecursively) {
if (setEntriesRecursively) {
for (size_t idx = 0; idx < this->getFillCount(); idx++) {
registeredVariables[idx]->setValid(valid);
}
}
this->valid = valid;
}
object_id_t LocalPoolDataSetBase::getCreatorObjectId() {
if (poolManager != nullptr) {
return poolManager->getCreatorObjectId();
}
return objects::NO_OBJECT;
}
void LocalPoolDataSetBase::setAllVariablesReadOnly() {
for (size_t idx = 0; idx < this->getFillCount(); idx++) {
registeredVariables[idx]->setReadWriteMode(pool_rwm_t::VAR_READ);
}
}
float LocalPoolDataSetBase::getCollectionInterval() const {
if (periodicHelper != nullptr) {
return periodicHelper->getCollectionIntervalInSeconds();
} else {
return 0.0;
}
}
void LocalPoolDataSetBase::printSet() { return; }
@@ -1,68 +0,0 @@
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLOBJECTBASE_H_
#define FSFW_DATAPOOLLOCAL_LOCALPOOLOBJECTBASE_H_
#include "MarkChangedIF.h"
#include "fsfw/datapool/PoolVariableIF.h"
#include "fsfw/objectmanager/SystemObjectIF.h"
#include "fsfw/returnvalues/returnvalue.h"
#include "localPoolDefinitions.h"
class LocalDataPoolManager;
class DataSetIF;
class HasLocalDataPoolIF;
/**
* @brief This class serves as a non-template base for pool objects like pool variables
* or pool vectors.
*/
class LocalPoolObjectBase : public PoolVariableIF, public MarkChangedIF {
public:
LocalPoolObjectBase(lp_id_t poolId, HasLocalDataPoolIF* hkOwner, DataSetIF* dataSet,
pool_rwm_t setReadWriteMode);
LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId, DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
void setReadWriteMode(pool_rwm_t newReadWriteMode) override;
pool_rwm_t getReadWriteMode() const override;
bool isValid() const override;
void setValid(bool valid) override;
void setChanged(bool changed) override;
bool hasChanged() const override;
lp_id_t getDataPoolId() const override;
void setDataPoolId(lp_id_t poolId);
protected:
/**
* @brief To access the correct data pool entry on read and commit calls,
* the data pool id is stored.
*/
uint32_t localPoolId = PoolVariableIF::NO_PARAMETER;
/**
* @brief The valid information as it was stored in the data pool
* is copied to this attribute.
*/
bool valid = false;
/**
* @brief A local pool variable can be marked as changed.
*/
bool changed = false;
/**
* @brief The information whether the class is read-write or
* read-only is stored here.
*/
ReadWriteMode_t readWriteMode = pool_rwm_t::VAR_READ_WRITE;
//! @brief Pointer to the class which manages the HK pool.
LocalDataPoolManager* hkManager = nullptr;
void reportReadCommitError(const char* variableType, ReturnValue_t error, bool read,
object_id_t objectId, lp_id_t lpId);
};
#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLOBJECTBASE_H_ */
-197
View File
@@ -1,197 +0,0 @@
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_H_
#define FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_H_
#include "../datapool/DataSetIF.h"
#include "../datapool/PoolVariableIF.h"
#include "../objectmanager/ObjectManagerIF.h"
#include "../serialize/SerializeAdapter.h"
#include "../serviceinterface/ServiceInterface.h"
#include "AccessLocalPoolF.h"
#include "HasLocalDataPoolIF.h"
#include "LocalDataPoolManager.h"
#include "LocalPoolObjectBase.h"
#include "internal/LocalDpManagerAttorney.h"
/**
* @brief Local Pool Variable class which is used to access the local pools.
* @details
* This class is not stored in the map. Instead, it is used to access
* the pool entries by using a pointer to the map storing the pool
* entries. It can also be used to organize these pool entries into data sets.
*
* @tparam T The template parameter sets the type of the variable. Currently,
* all plain data types are supported, but in principle any type is possible.
* @ingroup data_pool
*/
template <typename T>
class LocalPoolVariable : public LocalPoolObjectBase {
public:
//! Default ctor is forbidden.
LocalPoolVariable() = delete;
/**
* This constructor is used by the data creators to have pool variable
* instances which can also be stored in datasets.
*
* It does not fetch the current value from the data pool, which
* has to be done by calling the read() operation.
* Datasets can be used to access multiple local pool entries in an
* efficient way. A pointer to a dataset can be passed to register
* the pool variable in that dataset directly.
* @param poolId ID of the local pool entry.
* @param hkOwner Pointer of the owner. This will generally be the calling
* class itself which passes "this".
* @param dataSet The data set in which the variable shall register itself.
* If nullptr, the variable is not registered.
* @param setReadWriteMode Specify the read-write mode of the pool variable.
*/
LocalPoolVariable(HasLocalDataPoolIF* hkOwner, lp_id_t poolId, DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
/**
* This constructor is used by data users like controllers to have
* access to the local pool variables of data creators by supplying
* the respective creator object ID.
*
* It does not fetch the current value from the data pool, which
* has to be done by calling the read() operation.
* Datasets can be used to access multiple local pool entries in an
* efficient way. A pointer to a dataset can be passed to register
* the pool variable in that dataset directly.
* @param poolId ID of the local pool entry.
* @param hkOwner object ID of the pool owner.
* @param dataSet The data set in which the variable shall register itself.
* If nullptr, the variable is not registered.
* @param setReadWriteMode Specify the read-write mode of the pool variable.
*
*/
LocalPoolVariable(object_id_t poolOwner, lp_id_t poolId, DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
/**
* Variation which takes the global unique identifier of a pool variable.
* @param globalPoolId
* @param dataSet
* @param setReadWriteMode
*/
LocalPoolVariable(gp_id_t globalPoolId, DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
virtual ~LocalPoolVariable() {};
/**
* @brief This is the local copy of the data pool entry.
* @details The user can work on this attribute
* just like he would on a simple local variable.
*/
T value = 0;
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
SerializeIF::Endianness streamEndianness) const override;
size_t getSerializedSize() const override;
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
SerializeIF::Endianness streamEndianness) override;
/**
* @brief This is a call to read the array's values
* from the global data pool.
* @details
* When executed, this operation tries to fetch the pool entry with matching
* data pool id from the data pool and copies all array values and the valid
* information to its local attributes.
* In case of a failure (wrong type, size or pool id not found), the
* variable is set to zero and invalid.
* The read call is protected with a lock.
* It is recommended to use DataSets to read and commit multiple variables
* at once to avoid the overhead of unnecessary lock und unlock operations.
*
*/
ReturnValue_t read(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20) override;
/**
* @brief The commit call copies the array values back to the data pool.
* @details
* It checks type and size, as well as if the variable is writable. If so,
* the value is copied and the local valid flag is written back as well.
* The read call is protected with a lock.
* It is recommended to use DataSets to read and commit multiple variables
* at once to avoid the overhead of unnecessary lock und unlock operations.
*/
ReturnValue_t commit(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20) override;
/**
* @brief This commit function can be used to set the pool variable valid
* as well.
* @param setValid
* @param timeoutType
* @param timeoutMs
* @return
*/
ReturnValue_t commit(bool setValid,
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20);
LocalPoolVariable<T>& operator=(const T& newValue);
LocalPoolVariable<T>& operator=(const LocalPoolVariable<T>& newPoolVariable);
//! Explicit type conversion operator. Allows casting the class to
//! its template type to perform operations on value.
explicit operator T() const;
bool operator==(const LocalPoolVariable<T>& other) const;
bool operator==(const T& other) const;
bool operator!=(const LocalPoolVariable<T>& other) const;
bool operator!=(const T& other) const;
bool operator<(const LocalPoolVariable<T>& other) const;
bool operator<(const T& other) const;
bool operator>(const LocalPoolVariable<T>& other) const;
bool operator>(const T& other) const;
protected:
/**
* @brief Like #read, but without a lock protection of the global pool.
* @details
* The operation does NOT provide any mutual exclusive protection by itself.
* This can be used if the lock is handled externally to avoid the overhead
* of consecutive lock und unlock operations.
* Declared protected to discourage free public usage.
*/
ReturnValue_t readWithoutLock() override;
/**
* @brief Like #commit, but without a lock protection of the global pool.
* @details
* The operation does NOT provide any mutual exclusive protection by itself.
* This can be used if the lock is handled externally to avoid the overhead
* of consecutive lock und unlock operations.
* Declared protected to discourage free public usage.
*/
ReturnValue_t commitWithoutLock() override;
#if FSFW_CPP_OSTREAM_ENABLED == 1
// std::ostream is the type for object std::cout
template <typename U>
friend std::ostream& operator<<(std::ostream& out, const LocalPoolVariable<U>& var);
#endif
};
#include "LocalPoolVariable.tpp"
template <class T>
using lp_var_t = LocalPoolVariable<T>;
using lp_bool_t = LocalPoolVariable<uint8_t>;
using lp_uint8_t = LocalPoolVariable<uint8_t>;
using lp_uint16_t = LocalPoolVariable<uint16_t>;
using lp_uint32_t = LocalPoolVariable<uint32_t>;
using lp_uint64_t = LocalPoolVariable<uint64_t>;
using lp_int8_t = LocalPoolVariable<int8_t>;
using lp_int16_t = LocalPoolVariable<int16_t>;
using lp_int32_t = LocalPoolVariable<int32_t>;
using lp_int64_t = LocalPoolVariable<int64_t>;
using lp_float_t = LocalPoolVariable<float>;
using lp_double_t = LocalPoolVariable<double>;
#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_H_ */
@@ -1,193 +0,0 @@
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_TPP_
#define FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_TPP_
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_H_
#error Include LocalPoolVariable.h before LocalPoolVariable.tpp!
#endif
template <typename T>
inline LocalPoolVariable<T>::LocalPoolVariable(HasLocalDataPoolIF* hkOwner, lp_id_t poolId,
DataSetIF* dataSet, pool_rwm_t setReadWriteMode)
: LocalPoolObjectBase(poolId, hkOwner, dataSet, setReadWriteMode) {}
template <typename T>
inline LocalPoolVariable<T>::LocalPoolVariable(object_id_t poolOwner, lp_id_t poolId,
DataSetIF* dataSet, pool_rwm_t setReadWriteMode)
: LocalPoolObjectBase(poolOwner, poolId, dataSet, setReadWriteMode) {}
template <typename T>
inline LocalPoolVariable<T>::LocalPoolVariable(gp_id_t globalPoolId, DataSetIF* dataSet,
pool_rwm_t setReadWriteMode)
: LocalPoolObjectBase(globalPoolId.objectId, globalPoolId.localPoolId, dataSet,
setReadWriteMode) {}
template <typename T>
inline ReturnValue_t LocalPoolVariable<T>::read(MutexIF::TimeoutType timeoutType,
uint32_t timeoutMs) {
if (hkManager == nullptr) {
return readWithoutLock();
}
MutexIF* mutex = LocalDpManagerAttorney::getMutexHandle(*hkManager);
ReturnValue_t result = mutex->lockMutex(timeoutType, timeoutMs);
if (result != returnvalue::OK) {
return result;
}
result = readWithoutLock();
mutex->unlockMutex();
return result;
}
template <typename T>
inline ReturnValue_t LocalPoolVariable<T>::readWithoutLock() {
if (readWriteMode == pool_rwm_t::VAR_WRITE) {
object_id_t targetObjectId = hkManager->getCreatorObjectId();
reportReadCommitError("LocalPoolVector", PoolVariableIF::INVALID_READ_WRITE_MODE, true,
targetObjectId, localPoolId);
return PoolVariableIF::INVALID_READ_WRITE_MODE;
}
PoolEntry<T>* poolEntry = nullptr;
ReturnValue_t result =
LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId, &poolEntry);
if (result != returnvalue::OK) {
object_id_t ownerObjectId = hkManager->getCreatorObjectId();
reportReadCommitError("LocalPoolVariable", result, false, ownerObjectId, localPoolId);
return result;
}
this->value = *(poolEntry->getDataPtr());
this->valid = poolEntry->getValid();
return returnvalue::OK;
}
template <typename T>
inline ReturnValue_t LocalPoolVariable<T>::commit(bool setValid, MutexIF::TimeoutType timeoutType,
uint32_t timeoutMs) {
this->setValid(setValid);
return commit(timeoutType, timeoutMs);
}
template <typename T>
inline ReturnValue_t LocalPoolVariable<T>::commit(MutexIF::TimeoutType timeoutType,
uint32_t timeoutMs) {
if (hkManager == nullptr) {
return commitWithoutLock();
}
MutexIF* mutex = LocalDpManagerAttorney::getMutexHandle(*hkManager);
ReturnValue_t result = mutex->lockMutex(timeoutType, timeoutMs);
if (result != returnvalue::OK) {
return result;
}
result = commitWithoutLock();
mutex->unlockMutex();
return result;
}
template <typename T>
inline ReturnValue_t LocalPoolVariable<T>::commitWithoutLock() {
if (readWriteMode == pool_rwm_t::VAR_READ) {
object_id_t targetObjectId = hkManager->getCreatorObjectId();
reportReadCommitError("LocalPoolVector", PoolVariableIF::INVALID_READ_WRITE_MODE, false,
targetObjectId, localPoolId);
return PoolVariableIF::INVALID_READ_WRITE_MODE;
}
PoolEntry<T>* poolEntry = nullptr;
ReturnValue_t result =
LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId, &poolEntry);
if (result != returnvalue::OK) {
object_id_t ownerObjectId = hkManager->getCreatorObjectId();
reportReadCommitError("LocalPoolVariable", result, false, ownerObjectId, localPoolId);
return result;
}
*(poolEntry->getDataPtr()) = this->value;
poolEntry->setValid(this->valid);
return returnvalue::OK;
}
template <typename T>
inline ReturnValue_t LocalPoolVariable<T>::serialize(
uint8_t** buffer, size_t* size, const size_t max_size,
SerializeIF::Endianness streamEndianness) const {
return SerializeAdapter::serialize(&value, buffer, size, max_size, streamEndianness);
}
template <typename T>
inline size_t LocalPoolVariable<T>::getSerializedSize() const {
return SerializeAdapter::getSerializedSize(&value);
}
template <typename T>
inline ReturnValue_t LocalPoolVariable<T>::deSerialize(const uint8_t** buffer, size_t* size,
SerializeIF::Endianness streamEndianness) {
return SerializeAdapter::deSerialize(&value, buffer, size, streamEndianness);
}
#if FSFW_CPP_OSTREAM_ENABLED == 1
template <typename T>
inline std::ostream& operator<<(std::ostream& out, const LocalPoolVariable<T>& var) {
out << var.value;
return out;
}
#endif
template <typename T>
inline LocalPoolVariable<T>::operator T() const {
return value;
}
template <typename T>
inline LocalPoolVariable<T>& LocalPoolVariable<T>::operator=(const T& newValue) {
value = newValue;
return *this;
}
template <typename T>
inline LocalPoolVariable<T>& LocalPoolVariable<T>::operator=(
const LocalPoolVariable<T>& newPoolVariable) {
value = newPoolVariable.value;
return *this;
}
template <typename T>
inline bool LocalPoolVariable<T>::operator==(const LocalPoolVariable<T>& other) const {
return this->value == other.value;
}
template <typename T>
inline bool LocalPoolVariable<T>::operator==(const T& other) const {
return this->value == other;
}
template <typename T>
inline bool LocalPoolVariable<T>::operator!=(const LocalPoolVariable<T>& other) const {
return not(*this == other);
}
template <typename T>
inline bool LocalPoolVariable<T>::operator!=(const T& other) const {
return not(*this == other);
}
template <typename T>
inline bool LocalPoolVariable<T>::operator<(const LocalPoolVariable<T>& other) const {
return this->value < other.value;
}
template <typename T>
inline bool LocalPoolVariable<T>::operator<(const T& other) const {
return this->value < other;
}
template <typename T>
inline bool LocalPoolVariable<T>::operator>(const LocalPoolVariable<T>& other) const {
return not(*this < other);
}
template <typename T>
inline bool LocalPoolVariable<T>::operator>(const T& other) const {
return not(*this < other);
}
#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_TPP_ */
-177
View File
@@ -1,177 +0,0 @@
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_TPP_
#define FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_TPP_
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_
#error Include LocalPoolVector.h before LocalPoolVector.tpp!
#endif
template <typename T, uint16_t vectorSize>
inline LocalPoolVector<T, vectorSize>::LocalPoolVector(HasLocalDataPoolIF* hkOwner, lp_id_t poolId,
DataSetIF* dataSet,
pool_rwm_t setReadWriteMode)
: LocalPoolObjectBase(poolId, hkOwner, dataSet, setReadWriteMode) {}
template <typename T, uint16_t vectorSize>
inline LocalPoolVector<T, vectorSize>::LocalPoolVector(object_id_t poolOwner, lp_id_t poolId,
DataSetIF* dataSet,
pool_rwm_t setReadWriteMode)
: LocalPoolObjectBase(poolOwner, poolId, dataSet, setReadWriteMode) {}
template <typename T, uint16_t vectorSize>
inline LocalPoolVector<T, vectorSize>::LocalPoolVector(gp_id_t globalPoolId, DataSetIF* dataSet,
pool_rwm_t setReadWriteMode)
: LocalPoolObjectBase(globalPoolId.objectId, globalPoolId.localPoolId, dataSet,
setReadWriteMode) {}
template <typename T, uint16_t vectorSize>
inline ReturnValue_t LocalPoolVector<T, vectorSize>::read(MutexIF::TimeoutType timeoutType,
uint32_t timeoutMs) {
MutexGuard(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs);
return readWithoutLock();
}
template <typename T, uint16_t vectorSize>
inline ReturnValue_t LocalPoolVector<T, vectorSize>::readWithoutLock() {
if (readWriteMode == pool_rwm_t::VAR_WRITE) {
object_id_t targetObjectId = hkManager->getCreatorObjectId();
reportReadCommitError("LocalPoolVector", PoolVariableIF::INVALID_READ_WRITE_MODE, true,
targetObjectId, localPoolId);
return PoolVariableIF::INVALID_READ_WRITE_MODE;
}
PoolEntry<T>* poolEntry = nullptr;
ReturnValue_t result =
LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId, &poolEntry);
memset(this->value, 0, vectorSize * sizeof(T));
if (result != returnvalue::OK) {
object_id_t targetObjectId = hkManager->getCreatorObjectId();
reportReadCommitError("LocalPoolVector", result, true, targetObjectId, localPoolId);
return result;
}
std::memcpy(this->value, poolEntry->getDataPtr(), poolEntry->getByteSize());
this->valid = poolEntry->getValid();
return returnvalue::OK;
}
template <typename T, uint16_t vectorSize>
inline ReturnValue_t LocalPoolVector<T, vectorSize>::commit(bool valid,
MutexIF::TimeoutType timeoutType,
uint32_t timeoutMs) {
this->setValid(valid);
return commit(timeoutType, timeoutMs);
}
template <typename T, uint16_t vectorSize>
inline ReturnValue_t LocalPoolVector<T, vectorSize>::commit(MutexIF::TimeoutType timeoutType,
uint32_t timeoutMs) {
MutexGuard(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs);
return commitWithoutLock();
}
template <typename T, uint16_t vectorSize>
inline ReturnValue_t LocalPoolVector<T, vectorSize>::commitWithoutLock() {
if (readWriteMode == pool_rwm_t::VAR_READ) {
object_id_t targetObjectId = hkManager->getCreatorObjectId();
reportReadCommitError("LocalPoolVector", PoolVariableIF::INVALID_READ_WRITE_MODE, false,
targetObjectId, localPoolId);
return PoolVariableIF::INVALID_READ_WRITE_MODE;
}
PoolEntry<T>* poolEntry = nullptr;
ReturnValue_t result =
LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId, &poolEntry);
if (result != returnvalue::OK) {
object_id_t targetObjectId = hkManager->getCreatorObjectId();
reportReadCommitError("LocalPoolVector", result, false, targetObjectId, localPoolId);
return result;
}
std::memcpy(poolEntry->getDataPtr(), this->value, poolEntry->getByteSize());
poolEntry->setValid(this->valid);
return returnvalue::OK;
}
template <typename T, uint16_t vectorSize>
inline T& LocalPoolVector<T, vectorSize>::operator[](size_t i) {
if (i < vectorSize) {
return value[i];
}
// If this happens, I have to set some value. I consider this
// a configuration error, but I wont exit here.
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "LocalPoolVector: Invalid index. Setting or returning"
" last value!"
<< std::endl;
#else
sif::printWarning(
"LocalPoolVector: Invalid index. Setting or returning"
" last value!\n");
#endif
return value[vectorSize - 1];
}
template <typename T, uint16_t vectorSize>
inline const T& LocalPoolVector<T, vectorSize>::operator[](size_t i) const {
if (i < vectorSize) {
return value[i];
}
// If this happens, I have to set some value. I consider this
// a configuration error, but I wont exit here.
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "LocalPoolVector: Invalid index. Setting or returning"
" last value!"
<< std::endl;
#else
sif::printWarning(
"LocalPoolVector: Invalid index. Setting or returning"
" last value!\n");
#endif
return value[vectorSize - 1];
}
template <typename T, uint16_t vectorSize>
inline ReturnValue_t LocalPoolVector<T, vectorSize>::serialize(
uint8_t** buffer, size_t* size, size_t maxSize,
SerializeIF::Endianness streamEndianness) const {
ReturnValue_t result = returnvalue::FAILED;
for (uint16_t i = 0; i < vectorSize; i++) {
result = SerializeAdapter::serialize(&(value[i]), buffer, size, maxSize, streamEndianness);
if (result != returnvalue::OK) {
break;
}
}
return result;
}
template <typename T, uint16_t vectorSize>
inline size_t LocalPoolVector<T, vectorSize>::getSerializedSize() const {
return vectorSize * SerializeAdapter::getSerializedSize(value);
}
template <typename T, uint16_t vectorSize>
inline ReturnValue_t LocalPoolVector<T, vectorSize>::deSerialize(
const uint8_t** buffer, size_t* size, SerializeIF::Endianness streamEndianness) {
ReturnValue_t result = returnvalue::FAILED;
for (uint16_t i = 0; i < vectorSize; i++) {
result = SerializeAdapter::deSerialize(&(value[i]), buffer, size, streamEndianness);
if (result != returnvalue::OK) {
break;
}
}
return result;
}
#if FSFW_CPP_OSTREAM_ENABLED == 1
template <typename T, uint16_t vectorSize>
inline std::ostream& operator<<(std::ostream& out, const LocalPoolVector<T, vectorSize>& var) {
out << "Vector: [";
for (int i = 0; i < vectorSize; i++) {
out << var.value[i];
if (i < vectorSize - 1) {
out << ", ";
}
}
out << "]";
return out;
}
#endif
#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_TPP_ */
@@ -1,152 +0,0 @@
#ifndef FSFW_DATAPOOLLOCAL_PROVIDESDATAPOOLSUBSCRIPTION_H_
#define FSFW_DATAPOOLLOCAL_PROVIDESDATAPOOLSUBSCRIPTION_H_
#include "fsfw/housekeeping/AcceptsHkPacketsIF.h"
#include "fsfw/ipc/MessageQueueIF.h"
#include "fsfw/ipc/messageQueueDefinitions.h"
#include "fsfw/returnvalues/returnvalue.h"
#include "localPoolDefinitions.h"
namespace subdp {
struct ParamsBase {
ParamsBase(sid_t sid, bool enableReporting, float collectionInterval, bool diagnostics)
: sid(sid),
enableReporting(enableReporting),
collectionInterval(collectionInterval),
diagnostics(diagnostics) {}
[[nodiscard]] bool isDiagnostics() const { return diagnostics; }
sid_t sid;
bool enableReporting;
float collectionInterval;
MessageQueueId_t receiver = MessageQueueIF::NO_QUEUE;
protected:
bool diagnostics;
};
struct RegularHkPeriodicParams : public ParamsBase {
RegularHkPeriodicParams(sid_t sid, bool enableReporting, float collectionInterval)
: ParamsBase(sid, enableReporting, collectionInterval, false) {}
};
struct DiagnosticsHkPeriodicParams : public ParamsBase {
DiagnosticsHkPeriodicParams(sid_t sid, bool enableReporting, float collectionInterval)
: ParamsBase(sid, enableReporting, collectionInterval, true) {}
};
struct RegularHkUpdateParams : public ParamsBase {
RegularHkUpdateParams(sid_t sid, bool enableReporting)
: ParamsBase(sid, enableReporting, 0.0, false) {}
};
struct DiagnosticsHkUpdateParams : public ParamsBase {
DiagnosticsHkUpdateParams(sid_t sid, bool enableReporting)
: ParamsBase(sid, enableReporting, 0.0, true) {}
};
} // namespace subdp
class ProvidesDataPoolSubscriptionIF {
public:
virtual ~ProvidesDataPoolSubscriptionIF() = default;
/**
* @brief Subscribe for the generation of periodic packets. Used for regular HK packets
* @details
* This subscription mechanism will generally be used by the data creator
* to generate housekeeping packets which are downlinked directly.
* @return
*/
virtual ReturnValue_t subscribeForRegularPeriodicPacket(
subdp::RegularHkPeriodicParams params) = 0;
/**
* @brief Subscribe for the generation of periodic packets. Used for diagnostic packets
* @details
* This subscription mechanism will generally be used by the data creator
* to generate housekeeping packets which are downlinked directly.
* @return
*/
virtual ReturnValue_t subscribeForDiagPeriodicPacket(
subdp::DiagnosticsHkPeriodicParams params) = 0;
[[deprecated(
"Please use the new API which takes all arguments as one wrapper "
"struct")]] virtual ReturnValue_t
subscribeForPeriodicPacket(sid_t sid, bool enableReporting, float collectionInterval,
bool isDiagnostics,
object_id_t packetDestination = objects::NO_OBJECT) {
if (isDiagnostics) {
subdp::DiagnosticsHkPeriodicParams params(sid, enableReporting, collectionInterval);
return subscribeForDiagPeriodicPacket(params);
} else {
subdp::RegularHkPeriodicParams params(sid, enableReporting, collectionInterval);
return subscribeForRegularPeriodicPacket(params);
}
}
/**
* @brief Subscribe for the generation of packets if the dataset
* is marked as changed.
* @details
* This subscription mechanism will generally be used by the data creator.
* @param sid
* @param isDiagnostics
* @param packetDestination
* @return
*/
virtual ReturnValue_t subscribeForRegularUpdatePacket(subdp::RegularHkUpdateParams params) = 0;
virtual ReturnValue_t subscribeForDiagUpdatePacket(subdp::DiagnosticsHkUpdateParams params) = 0;
[[deprecated(
"Please use the new API which takes all arguments as one wrapper "
"struct")]] virtual ReturnValue_t
subscribeForUpdatePacket(sid_t sid, bool reportingEnabled, bool isDiagnostics,
object_id_t packetDestination = objects::NO_OBJECT) {
if (isDiagnostics) {
subdp::DiagnosticsHkUpdateParams params(sid, reportingEnabled);
return subscribeForDiagUpdatePacket(params);
} else {
subdp::RegularHkUpdateParams params(sid, reportingEnabled);
return subscribeForRegularUpdatePacket(params);
}
}
/**
* @brief Subscribe for a notification message which will be sent
* if a dataset has changed.
* @details
* This subscription mechanism will generally be used internally by
* other software components.
* @param setId Set ID of the set to receive update messages from.
* @param destinationObject Object ID of the receiver.
* @param targetQueueId Receiver queue ID
* @param generateSnapshot If this is set to true, a copy of the current data with a
* timestamp will be generated and sent via message.
* Otherwise, only an notification message is sent.
* @return
*/
virtual ReturnValue_t subscribeForSetUpdateMessage(uint32_t setId, object_id_t destinationObject,
MessageQueueId_t targetQueueId,
bool generateSnapshot) = 0;
/**
* @brief Subscribe for an notification message which will be sent if a
* pool variable has changed.
* @details
* This subscription mechanism will generally be used internally by
* other software components.
* @param localPoolId Pool ID of the pool variable
* @param destinationObject Object ID of the receiver
* @param targetQueueId Receiver queue ID
* @param generateSnapshot If this is set to true, a copy of the current data with a
* timestamp will be generated and sent via message. Otherwise,
* only an notification message is sent.
* @return
*/
virtual ReturnValue_t subscribeForVariableUpdateMessage(lp_id_t localPoolId,
object_id_t destinationObject,
MessageQueueId_t targetQueueId,
bool generateSnapshot) = 0;
};
#endif /* FSFW_DATAPOOLLOCAL_PROVIDESDATAPOOLSUBSCRIPTION_H_ */
@@ -1,37 +0,0 @@
#ifndef FSFW_DATAPOOLLOCAL_SHAREDLOCALDATASET_H_
#define FSFW_DATAPOOLLOCAL_SHAREDLOCALDATASET_H_
#include <vector>
#include "../datapool/SharedDataSetIF.h"
#include "../objectmanager/SystemObject.h"
#include "LocalPoolDataSetBase.h"
/**
* This local dataset variation can be used if the dataset is used concurrently across
* multiple threads. It provides a lock in addition to all other functionalities provided
* by the LocalPoolDataSetBase class.
*
* The user is completely responsible for lockingand unlocking the dataset when using the
* shared dataset.
*/
class SharedLocalDataSet : public SystemObject,
public LocalPoolDataSetBase,
public SharedDataSetIF {
public:
SharedLocalDataSet(object_id_t objectId, HasLocalDataPoolIF* owner, uint32_t setId,
const size_t maxSize);
SharedLocalDataSet(object_id_t objectId, sid_t sid, const size_t maxSize);
virtual ~SharedLocalDataSet();
ReturnValue_t lockDataset(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
dur_millis_t mutexTimeout = 20) override;
ReturnValue_t unlockDataset() override;
private:
MutexIF* datasetLock = nullptr;
std::vector<PoolVariableIF*> poolVarVector;
};
#endif /* FSFW_DATAPOOLLOCAL_SHAREDLOCALDATASET_H_ */
@@ -1,2 +0,0 @@
target_sources(${LIB_FSFW_NAME} PRIVATE HasLocalDpIFUserAttorney.cpp
HasLocalDpIFManagerAttorney.cpp)
@@ -1,19 +0,0 @@
#include "HasLocalDpIFManagerAttorney.h"
#include "fsfw/datapoollocal/HasLocalDataPoolIF.h"
#include "fsfw/datapoollocal/LocalPoolDataSetBase.h"
#include "fsfw/datapoollocal/LocalPoolObjectBase.h"
LocalPoolDataSetBase* HasLocalDpIFManagerAttorney::getDataSetHandle(HasLocalDataPoolIF* clientIF,
sid_t sid) {
return clientIF->getDataSetHandle(sid);
}
LocalPoolObjectBase* HasLocalDpIFManagerAttorney::getPoolObjectHandle(HasLocalDataPoolIF* clientIF,
lp_id_t localPoolId) {
return clientIF->getPoolObjectHandle(localPoolId);
}
object_id_t HasLocalDpIFManagerAttorney::getObjectId(HasLocalDataPoolIF* clientIF) {
return clientIF->getObjectId();
}
@@ -1,21 +0,0 @@
#ifndef FSFW_DATAPOOLLOCAL_HASLOCALDPIFMANAGERATTORNEY_H_
#define FSFW_DATAPOOLLOCAL_HASLOCALDPIFMANAGERATTORNEY_H_
#include "fsfw/datapoollocal/localPoolDefinitions.h"
class HasLocalDataPoolIF;
class LocalPoolDataSetBase;
class LocalPoolObjectBase;
class HasLocalDpIFManagerAttorney {
static LocalPoolDataSetBase* getDataSetHandle(HasLocalDataPoolIF* clientIF, sid_t sid);
static LocalPoolObjectBase* getPoolObjectHandle(HasLocalDataPoolIF* clientIF,
lp_id_t localPoolId);
static object_id_t getObjectId(HasLocalDataPoolIF* clientIF);
friend class LocalDataPoolManager;
};
#endif /* FSFW_DATAPOOLLOCAL_HASLOCALDPIFMANAGERATTORNEY_H_ */
@@ -1,8 +0,0 @@
#include "HasLocalDpIFUserAttorney.h"
#include "fsfw/datapoollocal/AccessLocalPoolF.h"
#include "fsfw/datapoollocal/HasLocalDataPoolIF.h"
AccessPoolManagerIF* HasLocalDpIFUserAttorney::getAccessorHandle(HasLocalDataPoolIF* clientIF) {
return clientIF->getAccessorHandle();
}
@@ -1,26 +0,0 @@
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETATTORNEY_H_
#define FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETATTORNEY_H_
#include "fsfw/datapoollocal/LocalPoolDataSetBase.h"
class LocalPoolDataSetAttorney {
private:
static void initializePeriodicHelper(LocalPoolDataSetBase& set, float collectionInterval,
uint32_t minimumPeriodicIntervalMs) {
set.initializePeriodicHelper(collectionInterval, minimumPeriodicIntervalMs);
}
static void setReportingEnabled(LocalPoolDataSetBase& set, bool enabled) {
set.setReportingEnabled(enabled);
}
static bool getReportingEnabled(LocalPoolDataSetBase& set) { return set.getReportingEnabled(); }
static PeriodicHousekeepingHelper* getPeriodicHelper(LocalPoolDataSetBase& set) {
return set.periodicHelper;
}
friend class LocalDataPoolManager;
};
#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETATTORNEY_H_ */
+25 -9
View File
@@ -71,7 +71,8 @@ bool AssemblyBase::handleChildrenChangedHealth() {
if (iter == childrenMap.end()) {
return false;
}
HealthState healthState = healthHelper.healthTable->getHealth(iter->first);
HealthState healthState =
healthHelper.healthTable->getHealth(convertToDeviceObjectId(iter->first));
if (healthState == HasHealthIF::NEEDS_RECOVERY) {
triggerEvent(TRYING_RECOVERY, iter->first, 0);
recoveryState = RECOVERY_STARTED;
@@ -91,10 +92,14 @@ bool AssemblyBase::handleChildrenChangedHealth() {
void AssemblyBase::handleChildrenTransition() {
if (commandsOutstanding <= 0) {
switch (internalState) {
case STATE_NEED_SECOND_STEP:
case STATE_NEED_SECOND_STEP: {
internalState = STATE_SECOND_STEP;
commandChildren(targetMode, targetSubmode);
ReturnValue_t result = commandChildren(targetMode, targetSubmode);
if (result == NEED_SECOND_STEP) {
internalState = STATE_NEED_SECOND_STEP;
}
return;
}
case STATE_OVERWRITE_HEALTH: {
internalState = STATE_SINGLE_STEP;
ReturnValue_t result = commandChildren(mode, submode);
@@ -170,7 +175,7 @@ ReturnValue_t AssemblyBase::checkChildrenStateOff() {
ReturnValue_t AssemblyBase::checkChildOff(uint32_t objectId) {
ChildInfo childInfo = childrenMap.find(objectId)->second;
if (healthHelper.healthTable->isCommandable(objectId)) {
if (healthHelper.healthTable->isCommandable(convertToDeviceObjectId(objectId))) {
if (childInfo.submode != SUBMODE_NONE) {
return returnvalue::FAILED;
} else {
@@ -227,7 +232,7 @@ bool AssemblyBase::checkAndHandleRecovery() {
case RECOVERY_STARTED:
// The recovery was already start in #handleChildrenChangedHealth and we just need
// to wait for an off time period.
// TODO: make time period configurable
// The timeout can be defined by #setRecoveryWaitTimer
recoveryState = RECOVERY_WAIT;
recoveryOffTimer.resetTimer();
return true;
@@ -235,14 +240,14 @@ bool AssemblyBase::checkAndHandleRecovery() {
if (recoveryOffTimer.isBusy()) {
return true;
}
triggerEvent(RECOVERY_STEP, 0);
triggerEvent(RECOVERY_WAITING, recoveringDevice->first);
sendHealthCommand(recoveringDevice->second.commandQueue, HEALTHY);
internalState = STATE_NONE;
recoveryState = RECOVERY_ONGOING;
// Don't check state!
return true;
case RECOVERY_ONGOING:
triggerEvent(RECOVERY_STEP, 1);
triggerEvent(RECOVERY_RESTARTING, recoveringDevice->first);
recoveryState = RECOVERY_ONGOING_2;
recoveringDevice->second.healthChanged = false;
// Device should be healthy again, so restart a transition.
@@ -250,7 +255,7 @@ bool AssemblyBase::checkAndHandleRecovery() {
doStartTransition(targetMode, targetSubmode);
return true;
case RECOVERY_ONGOING_2:
triggerEvent(RECOVERY_DONE);
triggerEvent(RECOVERY_DONE, recoveringDevice->first);
// Now we're through, but not sure if it was successful.
recoveryState = RECOVERY_IDLE;
return false;
@@ -264,7 +269,14 @@ void AssemblyBase::overwriteDeviceHealth(object_id_t objectId, HasHealthIF::Heal
triggerEvent(OVERWRITING_HEALTH, objectId, oldHealth);
internalState = STATE_OVERWRITE_HEALTH;
modeHelper.setForced(true);
sendHealthCommand(childrenMap[objectId].commandQueue, EXTERNAL_CONTROL);
if (childrenMap.find(objectId) != childrenMap.end()) {
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) {
@@ -274,3 +286,7 @@ void AssemblyBase::triggerModeHelperEvents(Mode_t mode, Submode_t submode) {
triggerEvent(CHANGING_MODE, mode, submode);
}
}
void AssemblyBase::setRecoveryWaitTimer(uint32_t timeoutMS) {
recoveryOffTimer.setTimeout(timeoutMS);
}
+2
View File
@@ -206,6 +206,8 @@ class AssemblyBase : public SubsystemBase {
void overwriteDeviceHealth(object_id_t objectId, HasHealthIF::HealthState oldHealth);
void triggerModeHelperEvents(Mode_t mode, Submode_t submode);
void setRecoveryWaitTimer(uint32_t timeoutMS);
};
#endif /* FSFW_DEVICEHANDLERS_ASSEMBLYBASE_H_ */
+22 -39
View File
@@ -1,11 +1,10 @@
#include "DeviceHandlerBase.h"
#include "fsfw/datapool/PoolReadGuard.h"
#include "fsfw/datapoollocal/LocalPoolVariable.h"
#include "fsfw/datapool/PoolVariable.h"
#include "fsfw/devicehandlers/AcceptsDeviceResponsesIF.h"
#include "fsfw/devicehandlers/DeviceTmReportingWrapper.h"
#include "fsfw/globalfunctions/CRC.h"
#include "fsfw/housekeeping/HousekeepingMessage.h"
#include "fsfw/ipc/MessageQueueMessage.h"
#include "fsfw/ipc/QueueFactory.h"
#include "fsfw/objectmanager/ObjectManager.h"
@@ -28,11 +27,12 @@ DeviceHandlerBase::DeviceHandlerBase(object_id_t setObjectId, object_id_t device
storedRawData(StorageManagerIF::INVALID_ADDRESS),
deviceCommunicationId(deviceCommunication),
comCookie(comCookie),
sharedPool(DeviceHandlerBase::getObjectId()),
healthHelper(this, setObjectId),
modeHelper(this),
parameterHelper(this),
actionHelper(this, nullptr),
poolManager(this, nullptr),
hkHelper(this, nullptr),
childTransitionFailure(returnvalue::OK),
fdirInstance(fdirInstance),
defaultFDIRUsed(fdirInstance == nullptr),
@@ -111,7 +111,7 @@ ReturnValue_t DeviceHandlerBase::performOperation(uint8_t counter) {
doGetRead();
/* This will be performed after datasets have been updated by the
custom device implementation. */
poolManager.performHkOperation();
hkHelper.performHkOperation();
break;
default:
break;
@@ -152,8 +152,7 @@ ReturnValue_t DeviceHandlerBase::initialize() {
}
if (rawDataReceiverId != objects::NO_OBJECT) {
AcceptsDeviceResponsesIF* rawReceiver =
ObjectManager::instance()->get<AcceptsDeviceResponsesIF>(rawDataReceiverId);
auto* rawReceiver = ObjectManager::instance()->get<AcceptsDeviceResponsesIF>(rawDataReceiverId);
if (rawReceiver == nullptr) {
printWarningOrError(sif::OutputTypes::OUT_ERROR, "initialize",
@@ -214,7 +213,7 @@ ReturnValue_t DeviceHandlerBase::initialize() {
return result;
}
result = poolManager.initialize(commandQueue);
result = hkHelper.initialize(commandQueue);
if (result != returnvalue::OK) {
return result;
}
@@ -226,7 +225,7 @@ ReturnValue_t DeviceHandlerBase::initialize() {
// Set temperature target state to NON_OP.
if (pg.getReadResult() == returnvalue::OK) {
thermalSet->heaterRequest.value = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL;
thermalSet->heaterRequest.setValid(true);
// thermalSet->heaterRequest.setValid(true);
}
}
@@ -288,7 +287,7 @@ void DeviceHandlerBase::readCommandQueue() {
return;
}
result = poolManager.handleHousekeepingMessage(&command);
result = hkHelper.handleHousekeepingMessage(&command);
if (result == returnvalue::OK) {
return;
}
@@ -413,7 +412,7 @@ ReturnValue_t DeviceHandlerBase::isModeCombinationValid(Mode_t mode, Submode_t s
}
ReturnValue_t DeviceHandlerBase::insertInCommandAndReplyMap(
DeviceCommandId_t deviceCommand, uint16_t maxDelayCycles, LocalPoolDataSetBase* replyDataSet,
DeviceCommandId_t deviceCommand, uint16_t maxDelayCycles, dp::SharedSetBase* replyDataSet,
size_t replyLen, bool periodic, bool hasDifferentReplyId, DeviceCommandId_t replyId,
Countdown* countdown) {
// No need to check, as we may try to insert multiple times.
@@ -428,7 +427,7 @@ ReturnValue_t DeviceHandlerBase::insertInCommandAndReplyMap(
ReturnValue_t DeviceHandlerBase::insertInReplyMap(DeviceCommandId_t replyId,
uint16_t maxDelayCycles,
LocalPoolDataSetBase* dataSet, size_t replyLen,
dp::SharedSetBase* dataSet, size_t replyLen,
bool periodic, Countdown* countdown) {
DeviceReplyInfo info;
info.maxDelayCycles = maxDelayCycles;
@@ -529,7 +528,7 @@ ReturnValue_t DeviceHandlerBase::updatePeriodicReply(bool enable, DeviceCommandI
}
ReturnValue_t DeviceHandlerBase::setReplyDataset(DeviceCommandId_t replyId,
LocalPoolDataSetBase* dataSet) {
dp::SharedSetBase* dataSet) {
auto replyIter = deviceReplyMap.find(replyId);
if (replyIter == deviceReplyMap.end()) {
return returnvalue::FAILED;
@@ -593,7 +592,7 @@ void DeviceHandlerBase::setMode(Mode_t newMode, uint8_t newSubmode) {
if (thermalSet->heaterRequest.value != ThermalComponentIF::STATE_REQUEST_IGNORE) {
thermalSet->heaterRequest.value = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL;
}
thermalSet->heaterRequest.setValid(true);
// thermalSet->heaterRequest.setValid(true);
}
}
/* TODO: This will probably be done by the LocalDataPoolManager now */
@@ -1269,7 +1268,7 @@ ReturnValue_t DeviceHandlerBase::letChildHandleMessage(CommandMessage* message)
void DeviceHandlerBase::handleDeviceTm(const uint8_t* rawData, size_t rawDataLen,
DeviceCommandId_t replyId, bool forceDirectTm) {
SerialBufferAdapter bufferWrapper(rawData, rawDataLen);
SerialBufferAdapter<uint32_t> bufferWrapper(rawData, rawDataLen);
handleDeviceTm(bufferWrapper, replyId, forceDirectTm);
}
@@ -1468,8 +1467,9 @@ Submode_t DeviceHandlerBase::getInitialSubmode() { return SUBMODE_NONE; }
void DeviceHandlerBase::performOperationHook() {}
/*
ReturnValue_t DeviceHandlerBase::initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
LocalDataPoolManager& poolManager) {
PeriodicHkGenerationHelper& poolManager) {
if (thermalStateCfg.has_value()) {
localDataPoolMap.emplace(thermalStateCfg.value().thermalStatePoolId,
new PoolEntry<DeviceHandlerIF::dh_thermal_state_t>());
@@ -1478,6 +1478,7 @@ ReturnValue_t DeviceHandlerBase::initializeLocalDataPool(localpool::DataPool& lo
}
return returnvalue::OK;
}
*/
ReturnValue_t DeviceHandlerBase::initializeAfterTaskCreation() {
// In this function, the task handle should be valid if the task
@@ -1485,7 +1486,7 @@ ReturnValue_t DeviceHandlerBase::initializeAfterTaskCreation() {
if (executingTask != nullptr) {
pstIntervalMs = executingTask->getPeriodMs();
}
this->poolManager.initializeAfterTaskCreation();
// this->poolManager.initializeAfterTaskCreation();
if (thermalStateCfg.has_value()) {
ThermalStateCfg& cfg = thermalStateCfg.value();
@@ -1497,20 +1498,11 @@ ReturnValue_t DeviceHandlerBase::initializeAfterTaskCreation() {
return returnvalue::OK;
}
LocalPoolDataSetBase* DeviceHandlerBase::getDataSetHandle(sid_t sid) {
auto iter = deviceReplyMap.find(sid.ownerSetId);
if (iter != deviceReplyMap.end()) {
return iter->second.dataSet;
} else {
return nullptr;
}
}
object_id_t DeviceHandlerBase::getObjectId() const { return SystemObject::getObjectId(); }
void DeviceHandlerBase::setStartUpImmediately() { this->setStartupImmediately = true; }
dur_millis_t DeviceHandlerBase::getPeriodicOperationFrequency() const { return pstIntervalMs; }
// dur_millis_t DeviceHandlerBase::getPeriodicOperationFrequency() const { return pstIntervalMs; }
DeviceCommandId_t DeviceHandlerBase::getPendingCommand() const {
if (cookieInfo.pendingCommand != deviceCommandMap.end()) {
@@ -1519,17 +1511,6 @@ DeviceCommandId_t DeviceHandlerBase::getPendingCommand() const {
return DeviceHandlerIF::NO_COMMAND_ID;
}
void DeviceHandlerBase::setNormalDatapoolEntriesInvalid() {
for (const auto& reply : deviceReplyMap) {
if (reply.second.dataSet != nullptr) {
PoolReadGuard pg(reply.second.dataSet);
if (pg.getReadResult() == returnvalue::OK) {
reply.second.dataSet->setValidity(false, true);
}
}
}
}
void DeviceHandlerBase::printWarningOrError(sif::OutputTypes errorType, const char* functionName,
ReturnValue_t errorCode, const char* errorPrint) {
if (errorPrint == nullptr) {
@@ -1570,8 +1551,6 @@ void DeviceHandlerBase::printWarningOrError(sif::OutputTypes errorType, const ch
}
}
LocalDataPoolManager* DeviceHandlerBase::getHkManagerHandle() { return &poolManager; }
MessageQueueId_t DeviceHandlerBase::getCommanderQueueId(DeviceCommandId_t replyId) const {
auto commandIter = deviceCommandMap.find(replyId);
if (commandIter == deviceCommandMap.end()) {
@@ -1628,3 +1607,7 @@ ReturnValue_t DeviceHandlerBase::finishAction(bool success, DeviceCommandId_t ac
actionHelper.finish(success, commandIter->second.sendReplyTo, action, result);
return returnvalue::OK;
}
void DeviceHandlerBase::setNormalDatapoolEntriesInvalid() { return; }
hk::PeriodicHelper& DeviceHandlerBase::getHkHelper() { return hkHelper; }
+19 -37
View File
@@ -1,5 +1,6 @@
#ifndef FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_
#define FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_
#pragma once
#include <fsfw/housekeeping/PeriodicHkHelper.h>
#include <map>
#include <optional>
@@ -10,9 +11,6 @@
#include "DeviceHandlerThermalSet.h"
#include "fsfw/action/ActionHelper.h"
#include "fsfw/action/HasActionsIF.h"
#include "fsfw/datapool/PoolVariableIF.h"
#include "fsfw/datapoollocal/HasLocalDataPoolIF.h"
#include "fsfw/datapoollocal/LocalDataPoolManager.h"
#include "fsfw/health/HealthHelper.h"
#include "fsfw/ipc/MessageQueueIF.h"
#include "fsfw/modes/HasModesIF.h"
@@ -20,7 +18,6 @@
#include "fsfw/parameters/ParameterHelper.h"
#include "fsfw/power/PowerSwitchIF.h"
#include "fsfw/returnvalues/returnvalue.h"
#include "fsfw/serviceinterface/ServiceInterface.h"
#include "fsfw/serviceinterface/serviceInterfaceDefintions.h"
#include "fsfw/subsystem/ModeTreeConnectionIF.h"
#include "fsfw/tasks/ExecutableObjectIF.h"
@@ -88,7 +85,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
public ModeTreeChildIF,
public ModeTreeConnectionIF,
public ReceivesParameterMessagesIF,
public HasLocalDataPoolIF {
public hk::GeneratesPeriodicHkIF {
friend void(Factory::setStaticFrameworkObjectIds)();
public:
@@ -96,7 +93,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
* The constructor passes the objectId to the SystemObject().
*
* @param setObjectId the ObjectId to pass to the SystemObject() Constructor
* @param deviceCommuncation Communcation Interface object which is used
* @param deviceCommunication Communication Interface object which is used
* to implement communication functions
* @param comCookie This object will be passed to the communication inter-
* face and can contain user-defined information about the communication.
@@ -109,6 +106,8 @@ class DeviceHandlerBase : public DeviceHandlerIF,
void setCustomFdir(FailureIsolationBase *fdir);
void setPowerSwitcher(PowerSwitchIF *switcher);
hk::PeriodicHelper &getHkHelper();
/**
* extending the modes of DeviceHandler IF for internal state machine
*/
@@ -230,7 +229,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
ReturnValue_t initializeAfterTaskCreation() override;
/** Destructor. */
virtual ~DeviceHandlerBase();
~DeviceHandlerBase() override;
/**
* Implementation of ExecutableObjectIF function
@@ -238,7 +237,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
* @param task_ Pointer to the taskIF of this task
*/
virtual void setTaskIF(PeriodicTaskIF *task_) override;
virtual MessageQueueId_t getCommandQueue(void) const override;
virtual MessageQueueId_t getCommandQueue() const override;
/** Explicit interface implementation of getObjectId */
virtual object_id_t getObjectId() const override;
@@ -506,7 +505,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
* - @c returnvalue::FAILED else.
*/
ReturnValue_t insertInCommandAndReplyMap(DeviceCommandId_t deviceCommand, uint16_t maxDelayCycles,
LocalPoolDataSetBase *replyDataSet = nullptr,
dp::SharedSetBase *replyDataSet = nullptr,
size_t replyLen = 0, bool periodic = false,
bool hasDifferentReplyId = false,
DeviceCommandId_t replyId = 0,
@@ -527,7 +526,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
* - @c returnvalue::FAILED else.
*/
ReturnValue_t insertInReplyMap(DeviceCommandId_t deviceCommand, uint16_t maxDelayCycles,
LocalPoolDataSetBase *dataSet = nullptr, size_t replyLen = 0,
dp::SharedSetBase *dataSet = nullptr, size_t replyLen = 0,
bool periodic = false, Countdown *countdown = nullptr);
/**
@@ -580,7 +579,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
* @details
* Used by the local data pool manager.
*/
ReturnValue_t setReplyDataset(DeviceCommandId_t replyId, LocalPoolDataSetBase *dataset);
ReturnValue_t setReplyDataset(DeviceCommandId_t replyId, dp::SharedSetBase *dataset);
/**
* Get the time needed to transit from modeFrom to modeTo.
@@ -607,8 +606,8 @@ class DeviceHandlerBase : public DeviceHandlerIF,
* @param localDataPoolMap
* @return
*/
virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
LocalDataPoolManager &poolManager) override;
// virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
// PeriodicHkGenerationHelper &poolManager) override;
/**
* @brief Set all datapool variables that are update periodically in
* normal mode invalid
@@ -619,15 +618,6 @@ class DeviceHandlerBase : public DeviceHandlerIF,
* method optionally.
*/
virtual void setNormalDatapoolEntriesInvalid();
/**
* @brief Get the dataset handle for a given SID.
* @details
* The default implementation will use the deviceCommandMap to look for the corresponding
* dataset handle. The user can override this function if this is not desired.
* @param sid
* @return
*/
virtual LocalPoolDataSetBase *getDataSetHandle(sid_t sid) override;
/* HasModesIF overrides */
virtual void startTransition(Mode_t mode, Submode_t submode) override;
@@ -797,6 +787,8 @@ class DeviceHandlerBase : public DeviceHandlerIF,
/** Cookie used for communication */
CookieIF *comCookie;
dp::SharedPool sharedPool;
/* Health helper for HasHealthIF */
HealthHelper healthHelper;
/* Mode helper for HasModesIF */
@@ -806,7 +798,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
/* Action helper for HasActionsIF */
ActionHelper actionHelper;
/* Housekeeping Manager */
LocalDataPoolManager poolManager;
hk::PeriodicHelper hkHelper;
/**
* @brief Information about commands
@@ -854,7 +846,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
//! The dataset used to access housekeeping data related to the
//! respective device reply. Will point to a dataset held by
//! the child handler (if one is specified)
LocalPoolDataSetBase *dataSet = nullptr;
dp::SharedSetBase *dataSet = nullptr;
//! The command that expects this reply.
DeviceCommandMap::iterator command;
//! Instead of using delayCycles to specify the maximum time to wait for the device reply, it
@@ -1006,12 +998,6 @@ class DeviceHandlerBase : public DeviceHandlerIF,
*/
virtual void doOnActivity();
/**
* Required for HasLocalDataPoolIF, return a handle to the local pool manager.
* @return
*/
LocalDataPoolManager *getHkManagerHandle() override;
const HasHealthIF *getOptHealthIF() const override;
const HasModesIF &getModeIF() const override;
@@ -1403,8 +1389,6 @@ class DeviceHandlerBase : public DeviceHandlerIF,
ReturnValue_t handleDeviceHandlerMessage(CommandMessage *message);
virtual dur_millis_t getPeriodicOperationFrequency() const override;
void parseReply(const uint8_t *receivedData, size_t receivedDataLen);
void handleTransitionToOnMode(Mode_t commandedMode, Submode_t commandedSubmode);
@@ -1424,6 +1408,4 @@ class DeviceHandlerBase : public DeviceHandlerIF,
* @brief Disables all commands and replies when device is set to MODE_OFF
*/
void disableCommandsAndReplies();
};
#endif /* FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ */
};
+6 -6
View File
@@ -2,11 +2,11 @@
#define FSFW_DEVICEHANDLERS_DEVICEHANDLERIF_H_
#include "../action/HasActionsIF.h"
#include "../datapoollocal/localPoolDefinitions.h"
#include "../events/Event.h"
#include "../ipc/MessageQueueSenderIF.h"
#include "../modes/HasModesIF.h"
#include "DeviceHandlerMessage.h"
#include "fsfw/datapool/definitions.h"
/**
* This is used to uniquely identify commands that are sent to a device
@@ -120,10 +120,10 @@ class DeviceHandlerIF {
NOTHING //!< Do nothing.
};
static constexpr uint32_t DEFAULT_THERMAL_SET_ID = sid_t::INVALID_SET_ID - 1;
static constexpr uint32_t DEFAULT_THERMAL_SET_ID = dp::sid_t::INVALID_SET_ID - 1;
static constexpr lp_id_t DEFAULT_THERMAL_STATE_POOL_ID = localpool::INVALID_LPID - 2;
static constexpr lp_id_t DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID = localpool::INVALID_LPID - 1;
static constexpr dp::id_t DEFAULT_THERMAL_STATE_POOL_ID = dp::INVALID_LPID - 2;
static constexpr dp::id_t DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID = dp::INVALID_LPID - 1;
/**
* Default Destructor
@@ -138,8 +138,8 @@ class DeviceHandlerIF {
};
struct ThermalStateCfg {
lp_id_t thermalStatePoolId = DeviceHandlerIF::DEFAULT_THERMAL_STATE_POOL_ID;
lp_id_t thermalRequestPoolId = DeviceHandlerIF::DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID;
dp::id_t thermalStatePoolId = DeviceHandlerIF::DEFAULT_THERMAL_STATE_POOL_ID;
dp::id_t thermalRequestPoolId = DeviceHandlerIF::DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID;
uint32_t thermalSetId = DeviceHandlerIF::DEFAULT_THERMAL_SET_ID;
};
@@ -1,27 +1,29 @@
#ifndef FSFW_DEVICEHANDLERS_DEVICEHANDLERTHERMALSET_H_
#define FSFW_DEVICEHANDLERS_DEVICEHANDLERTHERMALSET_H_
#include "../datapoollocal/LocalPoolVariable.h"
#include "../datapoollocal/StaticLocalDataSet.h"
#include "DeviceHandlerIF.h"
#include <fsfw/datapool/StaticSharedSet.h>
#include <fsfw/housekeeping/GeneratesPeriodicHkIF.h>
class DeviceHandlerThermalSet : public StaticLocalDataSet<2> {
#include "DeviceHandlerIF.h"
#include "fsfw/datapool/PoolVariable.h"
class DeviceHandlerThermalSet : public dp::StaticSharedSet<2> {
public:
DeviceHandlerThermalSet(HasLocalDataPoolIF* hkOwner, ThermalStateCfg cfg)
DeviceHandlerThermalSet(hk::GeneratesPeriodicHkIF* hkOwner, ThermalStateCfg cfg)
: DeviceHandlerThermalSet(hkOwner->getObjectId(), cfg) {}
DeviceHandlerThermalSet(object_id_t deviceHandler, ThermalStateCfg cfg)
: StaticLocalDataSet(sid_t(deviceHandler, cfg.thermalSetId)),
: StaticSharedSet(dp::structure_id_t(deviceHandler, cfg.thermalSetId), true),
thermalStatePoolId(cfg.thermalStatePoolId),
heaterRequestPoolId(cfg.thermalRequestPoolId) {}
const lp_id_t thermalStatePoolId;
const lp_id_t heaterRequestPoolId;
const dp::id_t thermalStatePoolId;
const dp::id_t heaterRequestPoolId;
lp_var_t<DeviceHandlerIF::dh_thermal_state_t> thermalState =
lp_var_t<DeviceHandlerIF::dh_thermal_state_t>(sid.objectId, thermalStatePoolId, this);
lp_var_t<DeviceHandlerIF::dh_heater_request_t> heaterRequest =
lp_var_t<DeviceHandlerIF::dh_heater_request_t>(sid.objectId, heaterRequestPoolId, this);
dp::var_t<DeviceHandlerIF::dh_thermal_state_t> thermalState =
dp::var_t<DeviceHandlerIF::dh_thermal_state_t>(sid.objectId, thermalStatePoolId, this);
dp::var_t<DeviceHandlerIF::dh_heater_request_t> heaterRequest =
dp::var_t<DeviceHandlerIF::dh_heater_request_t>(sid.objectId, heaterRequestPoolId, this);
};
#endif /* FSFW_DEVICEHANDLERS_DEVICEHANDLERTHERMALSET_H_ */
@@ -8,12 +8,12 @@ FreshDeviceHandlerBase::FreshDeviceHandlerBase(DhbConfig config)
: SystemObject(config.objectId),
actionHelper(this, nullptr),
modeHelper(this),
healthHelper(this, getObjectId()),
healthHelper(this, FreshDeviceHandlerBase::getObjectId()),
paramHelper(this),
poolManager(this, nullptr),
hkHelper(this, nullptr),
fdirInstance(config.fdirInstance),
defaultFdirParent(config.defaultFdirParent) {
auto mqArgs = MqArgs(config.objectId, static_cast<void*>(this));
auto mqArgs = MqArgs(config.objectId, this);
messageQueue = QueueFactory::instance()->createMessageQueue(
config.msgQueueDepth, MessageQueueMessage::MAX_MESSAGE_SIZE, &mqArgs);
}
@@ -33,7 +33,7 @@ ReturnValue_t FreshDeviceHandlerBase::performOperation(uint8_t opCode) {
handleQueue();
fdirInstance->checkForFailures();
performDeviceOperation(opCode);
poolManager.performHkOperation();
hkHelper.performHkOperation();
return returnvalue::OK;
}
@@ -43,7 +43,6 @@ ReturnValue_t FreshDeviceHandlerBase::performDeviceOperationPreQueueHandling(uin
void FreshDeviceHandlerBase::startTransition(Mode_t mode_, Submode_t submode_) {
triggerEvent(CHANGING_MODE, mode_, submode_);
// Complete mode transition immediately by default.
setMode(mode_, submode_);
}
@@ -97,7 +96,7 @@ ReturnValue_t FreshDeviceHandlerBase::handleQueue() {
continue;
}
result = poolManager.handleHousekeepingMessage(&command);
result = hkHelper.handleHousekeepingMessage(&command);
if (result == returnvalue::OK) {
continue;
}
@@ -127,17 +126,6 @@ ReturnValue_t FreshDeviceHandlerBase::connectModeTreeParent(HasModeTreeChildrenI
// Executable Overrides.
void FreshDeviceHandlerBase::setTaskIF(PeriodicTaskIF* task_) { executingTask = task_; }
// Pool Manager overrides.
LocalDataPoolManager* FreshDeviceHandlerBase::getHkManagerHandle() { return &poolManager; }
[[nodiscard]] uint32_t FreshDeviceHandlerBase::getPeriodicOperationFrequency() const {
return this->executingTask->getPeriodMs();
}
ReturnValue_t FreshDeviceHandlerBase::initializeAfterTaskCreation() {
return poolManager.initializeAfterTaskCreation();
}
ReturnValue_t FreshDeviceHandlerBase::setHealth(HasHealthIF::HealthState health) {
// Assembly should handle commanding to OFF.
healthHelper.setHealth(health);
@@ -174,7 +162,7 @@ ReturnValue_t FreshDeviceHandlerBase::initialize() {
return result;
}
result = poolManager.initialize(messageQueue);
result = hkHelper.initialize(messageQueue);
if (result != returnvalue::OK) {
return result;
}
@@ -201,3 +189,5 @@ ReturnValue_t FreshDeviceHandlerBase::getParameter(uint8_t domainId, uint8_t uni
}
return INVALID_DOMAIN_ID;
}
datapool::SharedPool* FreshDeviceHandlerBase::getOptionalSharedPool() { return nullptr; }
@@ -1,12 +1,13 @@
#pragma once
#include <fsfw/housekeeping/PeriodicHkHelper.h>
#include "fsfw/action.h"
#include "fsfw/datapoollocal/HasLocalDataPoolIF.h"
#include "fsfw/datapoollocal/LocalDataPoolManager.h"
#include "fsfw/devicehandlers/DeviceHandlerIF.h"
#include "fsfw/fdir/FailureIsolationBase.h"
#include "fsfw/health/HasHealthIF.h"
#include "fsfw/health/HealthHelper.h"
#include "fsfw/housekeeping/GeneratesPeriodicHkIF.h"
#include "fsfw/modes/HasModesIF.h"
#include "fsfw/objectmanager.h"
#include "fsfw/parameters/ParameterHelper.h"
@@ -61,7 +62,7 @@ class FreshDeviceHandlerBase : public SystemObject,
public ModeTreeConnectionIF,
public HasActionsIF,
public ReceivesParameterMessagesIF,
public HasLocalDataPoolIF {
public hk::GeneratesPeriodicHkIF {
public:
explicit FreshDeviceHandlerBase(DhbConfig config);
~FreshDeviceHandlerBase() override;
@@ -96,16 +97,12 @@ class FreshDeviceHandlerBase : public SystemObject,
ReturnValue_t connectModeTreeParent(HasModeTreeChildrenIF& parent) override;
ModeTreeChildIF& getModeTreeChildIF() override;
[[nodiscard]] uint32_t getPeriodicOperationFrequency() const override;
protected:
// Pool Manager overrides.
LocalDataPoolManager* getHkManagerHandle() override;
ActionHelper actionHelper;
ModeHelper modeHelper;
HealthHelper healthHelper;
ParameterHelper paramHelper;
LocalDataPoolManager poolManager;
hk::PeriodicHelper hkHelper;
bool hasCustomFdir = false;
FailureIsolationBase* fdirInstance;
@@ -151,6 +148,24 @@ class FreshDeviceHandlerBase : public SystemObject,
// System Object overrides.
ReturnValue_t initialize() override;
/**
* This function is implemented to serialize a housekeeping packet when a HK message to
* generate the packet is received, or periodic generation is necessary. The user should serialize
* the HK set into the provided buffer, which will have the size specified in the set
* specification.
*/
ReturnValue_t serializeHkDataset(dp::sid_t structureId, uint8_t* buf,
size_t maxSize) override = 0;
/**
* This function is implemented by the user to specify the available housekeeping sets.
*/
ReturnValue_t specifyHkDatasets(std::vector<hk::SetSpecification>& setList) override = 0;
/*
* If this device handler has an optional pool, return it. Otherwise, nullptr can be returned.
*/
datapool::SharedPool* getOptionalSharedPool() override = 0;
/**
* Implemented by child class. Handle all command messages which are
* not health, mode, action or housekeeping messages.
@@ -159,18 +174,7 @@ class FreshDeviceHandlerBase : public SystemObject,
*/
virtual ReturnValue_t handleCommandMessage(CommandMessage* message) = 0;
// HK manager abstract functions.
LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override = 0;
ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
LocalDataPoolManager& poolManager) override = 0;
/**
* HasModesIF implementation to check the validity of a mode command.
* @param mode
* @param submode
* @param msToReachTheMode
* @return
*/
// Mode abstract functions
ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode,
uint32_t* msToReachTheMode) override = 0;
// Health Overrides.
@@ -202,7 +206,6 @@ class FreshDeviceHandlerBase : public SystemObject,
* @return
*/
virtual ReturnValue_t performOperation(uint8_t opCode) override;
ReturnValue_t initializeAfterTaskCreation() override;
/**
* This calls the FDIR instance event trigger function.
+5 -4
View File
@@ -14,8 +14,6 @@ enum Severity : EventSeverity_t { INFO = 1, LOW = 2, MEDIUM = 3, HIGH = 4 };
} // namespace severity
#define MAKE_EVENT(id, severity) (((severity) << 16) + (SUBSYSTEM_ID * 100) + (id))
typedef uint32_t Event;
namespace event {
@@ -24,11 +22,14 @@ constexpr EventId_t getEventId(Event event) { return (event & 0xFFFF); }
constexpr EventSeverity_t getSeverity(Event event) { return ((event >> 16) & 0xFF); }
constexpr Event makeEvent(uint8_t subsystemId, UniqueEventId_t uniqueEventId,
EventSeverity_t eventSeverity) {
template <uint8_t subsystemId, UniqueEventId_t uniqueEventId, EventSeverity_t eventSeverity>
constexpr Event makeEvent() {
static_assert(uniqueEventId < 100, "The unique event ID must be smaller than 100!");
return (eventSeverity << 16) + (subsystemId * 100) + uniqueEventId;
}
} // namespace event
#define MAKE_EVENT(id, severity) event::makeEvent<SUBSYSTEM_ID, id, severity>();
#endif /* EVENTOBJECT_EVENT_H_ */
+1
View File
@@ -2,6 +2,7 @@ target_sources(
${LIB_FSFW_NAME}
PRIVATE arrayprinter.cpp
AsciiConverter.cpp
CobsEncoder.cpp
CRC.cpp
DleEncoder.cpp
DleParser.cpp
+78
View File
@@ -0,0 +1,78 @@
#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
@@ -0,0 +1,113 @@
#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_ */
+35 -25
View File
@@ -56,26 +56,33 @@ void arrayprinter::printHex(const uint8_t *data, size_t size, size_t maxCharPerL
std::cout << std::dec << std::setfill(' ');
std::cout << "]" << std::endl;
#else
// General format: 0x01, 0x02, 0x03 so it is number of chars times 6
// plus line break plus small safety margin.
char printBuffer[(size + 1) * 7 + 1] = {};
#if FSFW_DISABLE_PRINTOUT == 0
// Emitted in fixed size chunks. This used to size one buffer from the input - a variable length
// array of (size + 1) * 7 + 1 bytes on the stack - which is unusable for the sizes this is
// actually called with: dumping a 12 KB receive buffer asks for 84 KB of stack, and overflowed
// an 8 KB task on the iOBC as soon as a frame parse error made it dump one. The output is
// unchanged, it is just flushed as it is built.
constexpr size_t CHUNK_LEN = 128;
// An entry appends at most two hex digits, a separator and a line break.
constexpr size_t MAX_ENTRY_LEN = 4;
char printBuffer[CHUNK_LEN] = {};
size_t currentPos = 0;
printf("hex [");
for (size_t i = 0; i < size; i++) {
// To avoid buffer overflows.
if (sizeof(printBuffer) - currentPos <= 7) {
break;
if (currentPos + MAX_ENTRY_LEN >= CHUNK_LEN) {
printf("%s", printBuffer);
printBuffer[0] = '\0';
currentPos = 0;
}
currentPos += snprintf(printBuffer + currentPos, 6, "%02x", data[i]);
currentPos += snprintf(printBuffer + currentPos, CHUNK_LEN - currentPos, "%02x", data[i]);
if (i < size - 1) {
currentPos += sprintf(printBuffer + currentPos, ",");
currentPos += snprintf(printBuffer + currentPos, CHUNK_LEN - currentPos, ",");
if ((i + 1) % maxCharPerLine == 0) {
currentPos += sprintf(printBuffer + currentPos, "\n");
currentPos += snprintf(printBuffer + currentPos, CHUNK_LEN - currentPos, "\n");
}
}
}
#if FSFW_DISABLE_PRINTOUT == 0
printf("hex [%s]\n", printBuffer);
printf("%s]\n", printBuffer);
#endif /* FSFW_DISABLE_PRINTOUT == 0 */
#endif
}
@@ -98,27 +105,30 @@ void arrayprinter::printDec(const uint8_t *data, size_t size, size_t maxCharPerL
}
std::cout << "]" << std::endl;
#else
// General format: 32,243,-12 so it is number of chars times 4
// plus line break plus small safety margin.
uint16_t expectedLines = ceil((double)size / maxCharPerLine);
char printBuffer[size * 4 + 1 + expectedLines] = {};
#if FSFW_DISABLE_PRINTOUT == 0
// Chunked for the same reason as printHex above: the buffer used to be a variable length array
// sized from the input.
constexpr size_t CHUNK_LEN = 128;
// An entry appends at most three digits, a separator and a line break.
constexpr size_t MAX_ENTRY_LEN = 5;
char printBuffer[CHUNK_LEN] = {};
size_t currentPos = 0;
printf("dec [");
for (size_t i = 0; i < size; i++) {
// To avoid buffer overflows.
if (sizeof(printBuffer) - currentPos <= 4) {
break;
if (currentPos + MAX_ENTRY_LEN >= CHUNK_LEN) {
printf("%s", printBuffer);
printBuffer[0] = '\0';
currentPos = 0;
}
currentPos += snprintf(printBuffer + currentPos, 4, "%d", data[i]);
currentPos += snprintf(printBuffer + currentPos, CHUNK_LEN - currentPos, "%d", data[i]);
if (i < size - 1) {
currentPos += sprintf(printBuffer + currentPos, ",");
currentPos += snprintf(printBuffer + currentPos, CHUNK_LEN - currentPos, ",");
if ((i + 1) % maxCharPerLine == 0) {
currentPos += sprintf(printBuffer + currentPos, "\n");
currentPos += snprintf(printBuffer + currentPos, CHUNK_LEN - currentPos, "\n");
}
}
}
#if FSFW_DISABLE_PRINTOUT == 0
printf("dec [%s]\n", printBuffer);
printf("%s]\n", printBuffer);
#endif /* FSFW_DISABLE_PRINTOUT == 0 */
#endif
}
+10 -4
View File
@@ -27,13 +27,19 @@ class HasHealthIF {
static const Event CHILD_PROBLEMS = MAKE_EVENT(8, severity::LOW);
//! Assembly overwrites health information of children to keep satellite alive.
static const Event OVERWRITING_HEALTH = MAKE_EVENT(9, severity::LOW);
//! Someone starts a recovery of a component (typically power-cycle). No parameters.
//! Someone starts a recovery of a component (typically power-cycle).
//! P1: Object Id of the recovering device.
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.
//! P1: Object Id of the recovering device.
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 MessageQueueId_t getCommandQueue() const = 0;
+4 -2
View File
@@ -17,11 +17,11 @@ void HealthTable::setMutexTimeout(MutexIF::TimeoutType timeoutType, uint32_t tim
HealthTable::~HealthTable() { MutexFactory::instance()->deleteMutex(mutex); }
ReturnValue_t HealthTable::registerObject(object_id_t object,
HasHealthIF::HealthState initilialState) {
HasHealthIF::HealthState initialState) {
if (healthMap.count(object) != 0) {
return returnvalue::FAILED;
}
healthMap.emplace(object, initilialState);
healthMap.emplace(object, initialState);
return returnvalue::OK;
}
@@ -112,3 +112,5 @@ ReturnValue_t HealthTable::iterate(HealthEntry* value, bool reset) {
mapIterator++;
return result;
}
MutexIF* HealthTable::getMutex() { return mutex; }
+3 -1
View File
@@ -18,7 +18,7 @@ class HealthTable : public HealthTableIF, public SystemObject {
/** HealthTableIF overrides */
virtual ReturnValue_t registerObject(
object_id_t object, HasHealthIF::HealthState initilialState = HasHealthIF::HEALTHY) override;
object_id_t object, HasHealthIF::HealthState initialState = HasHealthIF::HEALTHY) override;
ReturnValue_t removeObject(object_id_t object) override;
virtual size_t getPrintSize() override;
virtual void printAll(uint8_t* pointer, size_t maxSize) override;
@@ -28,6 +28,8 @@ class HealthTable : public HealthTableIF, public SystemObject {
virtual void setHealth(object_id_t object, HasHealthIF::HealthState newState) override;
virtual HasHealthIF::HealthState getHealth(object_id_t) override;
MutexIF* getMutex();
protected:
using HealthMap = std::map<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 ReturnValue_t registerObject(
object_id_t object, HasHealthIF::HealthState initilialState = HasHealthIF::HEALTHY) = 0;
object_id_t object, HasHealthIF::HealthState initialState = HasHealthIF::HEALTHY) = 0;
virtual ReturnValue_t removeObject(object_id_t objectId) = 0;
Loaded 100 of 274 files, more files were not shown because too many files have changed in this diff. Show more