diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..ada61da
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,8 @@
+---
+BasedOnStyle: Google
+IndentWidth: 2
+---
+Language: Cpp
+ColumnLimit: 100
+ReflowComments: true
+---
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..6dfa6b9
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,6 @@
+/build*
+generators
+misc
+tmtc
+doc
+
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..eb6a49f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,28 @@
+/build*
+/cmake-build*
+
+# Eclipse
+.settings
+.metadata
+.project
+.cproject
+!misc/eclipse/**/.cproject
+!misc/eclipse/**/.project
+
+#vscode
+/.vscode
+
+# IntelliJ
+/.idea/*
+
+# Python
+__pycache__
+
+# CLion
+!/.idea/cmake.xml
+
+generators/*.db
+
+# Clangd LSP
+/compile_commands.json
+/.cache
diff --git a/.gitmodules b/.gitmodules
index 4b96622..e1ea03a 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,24 @@
+[submodule "arduino"]
+ path = arduino
+ url = https://egit.irs.uni-stuttgart.de/eive/eive_arduino_interface.git
[submodule "fsfw"]
path = fsfw
- url = https://egit.irs.uni-stuttgart.de/fsfw/fsfw.git
+ url = https://egit.irs.uni-stuttgart.de/eive/fsfw.git
+[submodule "tmtc"]
+ path = tmtc
+ url = https://egit.irs.uni-stuttgart.de/eive/eive-tmtc.git
+[submodule "thirdparty/lwgps"]
+ path = thirdparty/lwgps
+ url = https://github.com/rmspacefish/lwgps.git
+[submodule "thirdparty/json"]
+ path = thirdparty/json
+ url = https://github.com/nlohmann/json.git
+[submodule "thirdparty/rapidcsv"]
+ path = thirdparty/rapidcsv
+ url = https://github.com/d99kris/rapidcsv.git
+[submodule "thirdparty/gomspace-sw"]
+ path = thirdparty/gomspace-sw
+ url = https://egit.irs.uni-stuttgart.de/eive/gomspace-sw.git
+[submodule "thirdparty/sagittactl"]
+ path = thirdparty/sagittactl
+ url = https://egit.irs.uni-stuttgart.de/eive/sagittactl.git
diff --git a/.idea/cmake.xml b/.idea/cmake.xml
new file mode 100644
index 0000000..eff02c3
--- /dev/null
+++ b/.idea/cmake.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/Q7S FM.run.xml b/.run/Q7S FM.run.xml
new file mode 100644
index 0000000..ea4ae08
--- /dev/null
+++ b/.run/Q7S FM.run.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..f7a863b
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,2362 @@
+Change Log
+=======
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](http://keepachangelog.com/).
+
+The [milestone](https://egit.irs.uni-stuttgart.de/eive/eive-obsw/milestones)
+list yields a list of all related PRs for each release.
+
+Starting at v2.0.0, this project will adhere to semantic versioning and the the following changes
+will consitute of a breaking change warranting a new major release:
+
+- The TMTC interface changes in any shape of form.
+- The behaviour of the OBSW changes in a major shape or form relevant for operations
+
+# [unreleased]
+
+# [v8.2.1] 2025-02-07
+
+Patch release with EM changes only.
+
+## Changed
+
+- BPX battery is not added anymore for EM build, dummy is used by default now.
+
+## Fixed
+
+- Small fix for `q7s-cp.py` script: Add `-O` argument.
+
+# [v8.2.0] 2024-06-26
+
+## Changed
+
+- STR quaternions are now used by the `MEKF` by default
+- Changed nominal `SUS Assembly` side to `B Side`.
+- Changed source for state machine of detumbling to SUS and MGM only.
+- Changed `FusedRotRateData` dataset to always display rotation rate from SUS and MGM.
+- Solution from `QUEST` will be set to invalid if sun vector and magnetic field vector are too close
+ to each other.
+- Changed collection intervals of dataset collection
+ - `GPS Controller`: `GPS Set` to 60s
+ - `MTQ Handler`: `HK with Torque`, `HK without Torque` to 60s
+ - `RW Handler`: `Status Set` to 30s
+ - `STR Handler`: `Solution Set` to 30s
+ - `ACS Controller`: `MGM Sensor`, `MGM Processed`, `SUS Sensor`, `SUS Processed`, `GYR Sensor`,
+ `GPS Processed` to 60s
+ - `ACS Controller` at or below `IDLE`: `CTRL Values`, `ACT Commands`, `Attitude Estimation`,
+ `Fused Rotation Rate` to 30s
+ - `ACS Controller` above `IDLE`: `CTRL Values`, `ACT Commands`, `Attitude Estimation`,
+ `Fused Rotation Rate` to 10s
+
+## Fixed
+
+- Added null termination for PLOC MPSoC image taking command which could possibly lead to
+ default target filenames.
+
+# [v8.1.1] 2024-06-05
+
+## Added
+
+- PLOC SUPV MPSoC update re-try logic for the `WRITE_MEMORY` command. These packets form > 98%
+ of all packets required for a software update, but the update mechanism is not tolerant against
+ occasional glitches on the RS485 communication to the PLOC SUPV. A simple re-try mechanism which
+ tries to re-attempt packet handling up to three times for those packets is introduced.
+
+# [v8.1.0] 2024-05-29
+
+## Fixed
+
+- Small fix for transition failure handling of the MPSoC when the `START_MPSOC` action command
+ to the supervisor fails.
+- Fixed inits of arrays within the `MEKF` not being zeros.
+- Important bugfix for PLOC SUPV: The SUPV previously was able to steal packets from the special
+ communication helper, for example during software updates.
+- Corrected sigma of STR for `MEKF`.
+
+## Added
+
+- Added new command to cancel the PLOC SUPV special communication helper.
+
+# [v8.0.0] 2024-05-13
+
+- `eive-tmtc` v7.0.0
+
+## Fixed
+
+- Fixed calculation for target rotation rate during pointing modes.
+- Possible fix for MPSoC file read algorithm.
+
+## Changed
+
+- Reworked MPSoC handler to be compatible to new MPSoC software image and use
+ new device handler base class. This should increase the reliability of the communication
+ significantly.
+- MPSoC device modes IDLE, SNAPSHOT and REPLAY are now modelled using submodes.
+- Commanding a submode the device is already in will not result in a completion failure
+ anymore.
+
+## Added
+
+- Added `VERIFY_BOOT` command for MPSoC.
+- New command for MPSoC to store camera image in chunks.
+
+# [v7.8.1] 2024-04-11
+
+## Fixed
+
+- Reverted fix for wrong order in quaternion multiplication for computation of the error quaternion.
+
+# [v7.8.0] 2024-04-10
+
+## Changed
+
+- Reverted lower OP limit of `PLOC` to -10°C.
+- All pointing laws are now allowed to use the `MEKF` per default.
+- Changed limits in `PWR Controller`.
+- PUS time service: Now dumps the time before and after relative timeshift or setting absolute time
+- The `GPS Controller` does not set itself to `OFF` anymore, if it has not detected a valid fix for
+ some time. Instead it attempts to reset both GNSS devices once.
+- The maximum time to reach a fix is shortened from 30min to 15min.
+- The time the reset pin of the GNSS devices is pulled is prolonged from 5ms to 10s.
+- A `GPS FIX HAS CHANGED` is now only triggered if no fix change has been detected within the past
+ 2min. This means, this event might be thrown with a 2min delay. It is instantly thrown, if the mode
+ of the controller is changed. As arguments it now displays the new fix and the numer of fix changes
+ missed.
+- The number of satellites seen and used is reset to 0, in case they are set to invalid.
+- Altitude, latitude and longitude messages are not checked anymore, in case the mode message was
+ already invalid.
+
+## Added
+
+- PUS timeservice relative timeshift.
+
+## Fixed
+
+- Fixed wrong order in quaternion multiplication for computation of the error quaternion.
+- Re-worked some FDIR logic in the FSFW. The former logic prevented events with a severity
+ higher than INFO if the device was in EXTERNAL CONTROL. The new logic will allow to trigger
+ events but still inhibit FDIR reactions if the device is in EXTERNAL CONTROL.
+
+# [v7.7.4] 2024-03-21
+
+## Changed
+
+- Rotational rate limit for the GS target pointing is now seperated from controller limit. It
+ is also reduced to 0.75°/s now.
+
+## Fixed
+
+- Fixed wrong sign in calculation of total current within the `PWR Controller`.
+
+# [v7.7.3] 2024-03-18
+
+- Bumped `eive-fsfw`
+
+## Added
+
+- Added parameter to disable STR input for MEKF.
+
+## Changed
+
+- If a primary heater is set to `EXTERNAL CONTROL` and `ON`, the `TCS Controller` will no
+ try to control the temperature of that object.
+- Set lower OP limit of `PLOC` to -5°C.
+
+## Fixed
+
+- Added prevention of sign jump for target quaternion of GS pointing, which would reduce the
+ performance of the controller.
+- Heaters set to `EXTERNAL CONTROL` no longer can be switched off by the `TCS Controller`, if
+ they violate the maximum burn duration of the controller.
+
+# [v7.7.2] 2024-03-06
+
+## Fixed
+
+- Camera and E-band antenna now point towards the target instead of away from the target for the
+ pointing target mode.
+
+# [v7.7.1] 2024-03-06
+
+- Bumped `eive-tmtc` to v6.1.1
+- Bumped `eive-fsfw`
+
+## Added
+
+- The `CoreController` now sets the leap seconds on initalization. They are stored in a persistent
+ file. If the file does yet not exist, it will be created. The leap seconds can be updated using an
+ action command. This will also update the file.
+
+## Fixed
+
+- Fixed wrong dimension of a matrix within the `MEKF`, which would lead to a seg fault, if the
+ star tracker was available.
+- Fixed case in which control values within the `AcsController` could become NaN.
+
+# [v7.7.0] 2024-02-29
+
+- Bumped `eive-tmtc` to v6.1.0
+- Bumped `eive-fsfw`
+
+## Fixed
+
+- PLOC SUPV sets: Added missing `PoolReadGuard` instantiations when reading boot status report
+ and latchup status report.
+- PLOC SUPV latchup report could not be handled previously.
+- Bugfix in PLOC SUPV latchup report parsing.
+- Bugfix in PLOC MPSoC HK set: Set and variables were not set valid.
+- The `PTG_CTRL_NO_ATTITUDE_INFORMATION` will now actually trigger a fallback into safe mode
+ and is triggered by the `AcsController` now.
+- Fixed a corner case, in which an invalid speed command could be sent to the `RwHandler`.
+- Fixed calculation of desaturation torque for faulty RWs.
+- Fixed bugs within the `MEKF` and simplified the code.
+
+## Changed
+
+- `FusedRotationRate` now only uses rotation rate from QUEST and STR in higher modes
+- QUEST and STR rates are now allowed per default
+- Changed PTG Strat priorities to favor STR before MEKF.
+- Increased message queue depth and maximum number of handled messages per cycle for
+ `PusServiceBase` based classes (especially PUS scheduler).
+- `MathOperations` functions were moved to their appropriate classes within the `eive-fsfw`
+- Changed pointing strategy for target groundstation mode to prevent blinding of the STR. This
+ also limits the rotation for the reference target quaternion to prevent spikes in required
+ rotation rates.
+- Updated QUEST and Sun Vector Params to new values.
+- Removed the satellites's angular momentum from desaturation calculation.
+- Bumped internal `sagittactl` library to v11.11.
+
+## Added
+
+- Updated STR handler to unlock and allow using the secondary firmware slot.
+- STR handling for new BlobStats TM set.
+- Added new action command to update the standard deviations within the `MEKF` from the
+ `AcsParameters`.
+
+# [v7.6.1] 2024-02-05
+
+## Changed
+
+- Guidance now uses the coordinate functions from the FSFW.
+- Idle should now point the STR away from the earth
+
+## Fixed
+
+- Fixed bugs in `Guidance::comparePtg` and corrected overloading
+- Detumbling State Machine is now robust to commanded mode changes.
+
+# [v7.6.0] 2024-01-30
+
+- Bumped `eive-tmtc` to v5.13.0
+- Bumped `eive-fsfw`
+
+## Added
+
+- Added new parameter for MPSoC which allows to skip SUPV commanding.
+
+## Changed
+
+- Increased allowed mode transition time for PLOC SUPV.
+- Detumbling can now be triggered from all modes of the `AcsController`. In case the
+ current mode is a higher pointing mode, the STR will be set to faulty, to trigger a
+ transition to safe first. Then, in a second step, a transition to detumble is triggered.
+
+## Fixed
+
+- If the PCDU handler fails reading data from the IPC store, it will
+ not try to do a deserialization anymore.
+- All action commands sent by the PLOC SUPV to itself will have no sender now.
+- RW speed commands get reset to 0 RPM, if the `AcsController` has changed its mode
+ to Safe
+- Antistiction for RWs will set commanded speed to 0 RPM, if a wheel is detected as not
+ working
+- Removed parameter to disable antistiction, as deactivating it would result in the
+ `AcsController` being allowed sending invalid speed commands to the RW Handler, which
+ would then trigger FDIR and turning off the functioning device
+- `RwHandler` returnvalues would use the `INTERFACE_ID` of the `DeviceHandlerBase`
+- The `AcsController` will reset its stored guidance values on mode change and lost
+ orientation.
+- The nullspace controller will only be used if all RWs are available.
+- Calculation of required rotation rate in pointing modes has been fixed to actual
+ calculation of rotation rate from two quaternions.
+- Fixed alignment matrix and pseudo inverses of RWs, to match the wrong definition of
+ positive rotation.
+
+# [v7.5.5] 2024-01-22
+
+## Fixed
+
+- Calculation of error quaternion was done with inverse of the required target quaternion.
+
+# [v7.5.4] 2024-01-16
+
+## Fixed
+
+- Pointing strategy now actually uses fused rotation rate source instead of its valid flag.
+- All datasets now get updated during pointing mode, even if the strategy is a fault one.
+
+# [v7.5.3] 2023-12-19
+
+## Fixed
+
+- Set STR quaternions to invalid in device handler if the solution is not trustworthy.
+
+# [v7.5.2] 2023-12-14
+
+## Fixed
+
+- Fixed faulty scaling within the QUEST algorithm.
+
+# [v7.5.1] 2023-12-13
+
+- `eive-tmtc` v5.12.1
+
+## Changed
+
+- Increased the maximum number of scheduled telecommands from 500 to 4000. Merry Christmas!
+
+## Fixed
+
+- Faulty mapping of input values for QUEST algorithm.
+- Fixed validity check for QUEST algorithm.
+
+# [v7.5.0] 2023-12-06
+
+- `eive-tmtc` v5.12.0
+
+## Changed
+
+- ACS-Board default side changed to B-Side
+- The TLE uploaded now gets stored in a file on the filesystem. It will always be stored on
+ the current active SD Card. After a reboot, the TLE will be read from the filesystem.
+ A filesystem change via `prefSD` on bootup, can lead to the TLE not being read, even
+ though it is there.
+- Added action cmd to read the currently stored TLE.
+- Both the `AcsController` and the `PwrController` now use the monotonic clock to calculate
+ the time difference.
+- `ACS Controller` now has the function `performAttitudeControl` which is called prior to passing
+ on to the relevant mode functions. It handles all telemetry relevant functions, which were
+ always called, regardless of the mode.
+
+## Added
+
+- Higher ACS modes can now be entered without a running `MEKF`. Higher modes will collect their
+ quaternion and rotational rate depending on the available sources.
+- `QUEST` attitude estimation was added to the `AcsController`.
+- The fused rotational rate can now be estimated from `QUEST` and the `STR`.
+
+# [v7.4.1] 2023-12-06
+
+## Fixed
+
+- Schedule SCEX again. Scheduling was removed accidentaly when Payload Task was converted to a PST.
+- SCEX transition was previously 0 seconds.. which did not lead to bugs? In any case it is 5
+ seconds now.
+
+# [v7.4.0] 2023-11-30
+
+- `eive-tmtc` v5.11.0
+
+## Changed
+
+- Rewrote the PLOC Supervisor Handler, which is now based on a new device handler base class.
+ Added ADC and Logging Counters telemetry set support.
+
+## Fixed
+
+- Increase allowed time for PTME writers to finish partial transfers. A duration of 200 ms was
+ not sufficient for cases where 3 writers write concurrently.
+- Fixed state issue for PTME writer object where the writer was not reset properly after a timeout
+ of a partial transfer. This was a major bug blocking the whole VC if it occured.
+- STR config path was previously hardcoded to `/mnt/sd0/startracker/flight-config.json`.
+ A new abstraction was introduces which now uses the active SD card to build the correct
+ config path when initializing the star tracker.
+
+## Added
+
+- PL PCDU: Add command to enable and disable channel order checks.
+- Added new PUS 15 subservice `DELETE_BY_TIME_RANGE` which allows to also specify a deletion
+ start time when deleting packets from the persistent TM store.
+- Introduced a new `RELOAD_JSON_CFG_FILE` command for the STR to reload the JSON configuration
+ data based on the current output of the config file path getter function. A reboot of the
+ device is still necessary to load the configuration to the STR.
+
+# [v7.3.0] 2023-11-07
+
+## Changed
+
+- Changed PDEC addresses depending on which firmware version is used. It is suspected that
+ the previous addresses were invalid and not properly covered by the Linux memory protection.
+ The OBSW will use the old addresses for older FW versions.
+- Reverted some STR ComIF behaviour back to an older software version.
+
+## Added
+
+- Always add PLOC MPSoC and PLOC SUPV components for the EM as well.
+
+# [v7.2.0] 2023-10-27
+
+- `eive-tmtc` v5.10.1
+
+## Added
+
+- STR: Added new TM sets: Blob, Blobs, MatchedCentroids, Contrast, AutoBlob, Centroid, Centroids
+- STR: Added new mechanism where the secondary TM which is polled can now be a set instead of
+ being temperature only. An API is exposed which allows to add a data set to that set of secondary
+ telemetry, reset it back to temperature only, and read the whole set. This allows more debugging
+ capability.
+- CFDP source handler, which allows file downlink using the standardized
+ CFDP interface.
+- Proper back pressure handling for the CFDP handler, where the `LiveTmTask` is able to throttle
+ the CFDP handler.
+- Added CFDP fault handler events.
+- The EIVE system will command the payload OFF explicitely again when receiving the
+ `power::POWER_LEVEL_CRITICAL` event.
+
+## Fixed
+
+- If the PTME is driven in a way where it fills faster than it can be emptied, the interface
+ can become full during the process of a regular packet write. The interface of the PAPB VC
+ was adapted to be stateful now. Packet generation is started with a `write` call while
+ write transfers are advanced and completed with the `advanceWrite` call if they can not be
+ completed immediately.
+- CFDP Space Packets SSC is now generated properly, was always 0 before.
+- Host build fixes
+- PL Enable set of the power controller is now set to invalid properly if the power controller
+ is not in normal mode.
+- MPSoC debug mode.
+- Possible bugfix for PL PCDU parameter JSON handling which might not have been initialized
+ properly from the JSON file.
+
+## Changed
+
+- Swapped RTD 9 (PLOC HPA Sensor) and RTD 11 (PLOC MPA Sensor) chip select definitions. It is
+ strongly suspected the cables for those devices were swapped during integration. This is probably
+ the easiest way to fix the issue without the need to tweak ground or other OBSW or controller
+ code.
+- Added a 3 second delay in the EIVE system between commanding all PL components except the SUPV,
+ and the SUPV itself OFF when the power level becomes low or critical.
+- SUS FDIR should now trigger less events. The finish event is now only triggered once the
+ SUS has been working properly for a minute again. It will then display the number of periods
+ during which the SUS was not working as well as the maximum amount of invalid messages.
+- Updated battery internal resistance to new value
+
+# [v7.1.0] 2023-10-11
+
+- Bumped `eive-tmtc` to v5.8.0.
+- Activate Xiphos WDT with a timeout period of 80 seconds using the `libxiphos` API. The WDT
+ calls are done by the new `XiphosWdtHandler` object.
+
+# [v7.0.0] 2023-10-11
+
+- Bumped `eive-tmtc` to v5.7.1.
+- Bumped `eive-fsfw`
+
+## Added
+
+- EPS Subsystem has been added to EIVE System Tree
+- Power Controller for calculating the State of Charge and FDIR regarding low SoC has been
+ introduced.
+
+## Changed
+
+- Changed internals for MPSoC boot process to make the code more understandable and some
+ parameters better configurable. This should not affect the behaviour of the OBSW, but might
+ make it more reliable and fix some corner cases.
+
+## Fixed
+
+- Missing `nullptr` checks for PLOC Supervisor handler, which could lead to crashes.
+- SCEX bugfix for normal and transition commanding.
+
+# [v6.6.0] 2023-09-18
+
+## Changed
+
+- Changed the memory initialized for the PDEC Config Memory and the PDEC RAM by using `mmap`
+ directly and ignoring UIO. This makes the OBSW compatible to a device tree update, where those
+ memory segments are marked reserved and are thus not properly accessible through the UIO API
+ anymore. This change should be downwards compatible to older device trees.
+
+# [v6.5.1] 2023-09-12
+
+- Bumped `eive-tmtc` to v5.5.0.
+
+# [v6.5.0] 2023-09-12
+
+## Changed
+
+- Relaxed SUS FDIR. The devices have shown to be glitchy in orbit, but still seem to deliver
+ sensible raw values most of the time. Some further testing is necessary, but some changes in the
+ code should cause the SUS devices to remain healthy for now.
+- The primary and the secondary temperature sensors for the PLOC mission boards are exchanged.
+- ACS parameters for the SUSMGM (FLP) safe mode have been adjusted. This safe mode is now the
+ default one.
+- MGM3100 Startup Configuration: Ignore bit 1 of the CMM reply, which is sometimes set to
+ 1 in the reply for some reason.
+
+# [v6.4.1] 2023-08-21
+
+## Fixed
+
+- `PDEC_CONFIG_CORRUPTED` event now actually contains the readback instead of the expected
+ config
+- Magnetic field vector was not calculated if only MGM4 was available, but still written to
+ the dataset. This would result in a NaN vector. Allowance for usage of MGM4 is now checked
+ before entering calculation.
+
+# [v6.4.0] 2023-08-16
+
+- `eive-tmtc`: v5.4.3
+
+## Fixed
+
+- The handling function of the GPS data is only called once per GPS read. This should remove
+ the fake fix-has-changed events.
+- Fix for PLOC SUPV HK set parsing.
+- The timestamp for the `POSSIBLE_FILE_CORRUPTION` event will be generated properly now.
+
+## Changed
+
+- PDEC FDIR rework: A full PDEC reboot will now only be performed after a regular PDEC reset has
+ failed 10 times. The mechanism will reset after no PDEC reset has happended for 2 minutes.
+ The PDEC reset will be performed when counting 4 dirty frame events 10 seconds after the count
+ was incremented initially.
+- GPS Fix has changed event is no longer triggered for the EM
+- MGM and SUS rates now will only be calculated, if 2 valid consecutive datapoints are available.
+ The stored value of the last timestep will now be reset, if no actual value is available.
+
+## Added
+
+- The PLOC SUPV HK set is requested and downlinked periodically if the SUPV is on now.
+- SGP4 Propagator is now used for propagating the position of EIVE. It will only work once
+ a TLE has been uploaded with the newly added action command for the ACS Controller. In
+ return the actual GPS data will be ignored once SPG4 is running. However, by setting the
+ according parameter, the ACS Controller can be directed to ignore the SGP4 solution.
+- Skyview dataset for more GPS TM has been added
+- `PDEC_CONFIG_CORRUPTED` event which is triggered when the PDEC configuration does not match the
+ expected configuration. P1 will contain the readback of the first word and P2 will contain the
+ readback of the second word.
+- The MGM and SUS vectors being too close together does not prevent the usage of the safe
+ mode controller anymore.
+- Parameter to disable usage of MGM4, which is part of the MTQ and therefore cannot be
+ disabled without disabling the MTQ itself.
+
+# [v6.3.0] 2023-08-03
+
+## Fixed
+
+- Small SCEX fix: The temperatur check option was not passed
+ on for commands with a user data size larger than 1.
+- SCEX: Properly check whether filesystem is usable for filesystem checks.
+- ACS Controller strategy is now actually written to the dataset for detumbling.
+- During detumble the fused rotation rate is now calculated.
+- Detumbling is now exited when its exit value is undercut and not its entry value.
+- Rotation rate of last cycle is now stored in all cases for the fused rotational rate
+ calculation.
+- Fused rotation rate estimation during eclipse can be disabled. This will also prevent
+ detumbling during eclipse, as no relevant rotational rate is available for now.
+- `EiveSystem`: Add a small delay between triggering an event for FDIR reboots and sending the
+ command to the core controller.
+- PL PDU: Fixed bounds checking logic. Bound checks will only be performed for modules which are
+ enabled.
+
+## Changed
+
+- SCEX: Only perform filesystem checks when not in OFF mode.
+- The `EiveSystem` now only sends reboot commands targetting the same image.
+- Added 200 ms delay between switching HPA/MPA/TX/X8 and DRO GPIO pin OFF.
+- PL PCDU ADC set is now automatically enabled for `NORMAL` mode transitions. It is automatically
+ disabled for `OFF` mode transitions.
+
+## Added
+
+- PL PCDU for EM build.
+- SCEX: Add warning event if filesystem is unusable.
+
+# [v6.2.0] 2023-07-26
+
+- `eive-tmtc`: v5.3.1
+
+## Changed
+
+- STR missed reply handling is now moved to DHB rather than the COM interface. The COM IF will
+ still trigger an event if a reply is taking too long, and FDIR should still work via reply
+ timeouts.
+- Re-ordered some functions of the core controller in the initialize function.
+- Rad sensor is now only polled every 30 minutes instead of every device cycle to reduce wear of
+ the RADFET electronics.
+- The SD cards will still be switched OFF on a reboot, but this is done in a non-blocking manner
+ now with a timeout of 10 seconds where the reboot will be performed in any case.
+- ACS Controller now includes the safe mode from FLP, which will calculate its rotational rate
+ from SUS and MGM measurements. To accommodate these changes, low-pass filters for SUS
+ measurements and rates as well as MGM measurements and rates are included. Usage of the new
+ controller as well as settings of the low-pass filters can be handled via parameter commands.
+- Simplify and fix the chip and copy protection functions in the core controller. This mechanism
+ now is always performed for the target chip and target copy in the reboot handlers.
+- Improvement in FSFW: HK generation is now countdown based.
+
+## Added
+
+- 5 ms delay after pulling RADFET enable pin high before starting
+ the ADC conversion.
+- Set STR time in configuration sequence to firmware mode.
+- The STR `AutoThreshold` parameters are now set from the configuration JSON file at STR
+ startup.
+- The STR handler can now handle the COM error reply and triggers an low severity event accordingly.
+- Add SCEX handler for EM.
+- Radiation sensor handler dummy for the EM.
+- Added event for SD card information in core controller initialize function. This event will also
+ be triggered after the SD state machine has run, so the event will generally be triggered twice
+ at system boot, and once after commanding SD card switches.
+
+## Fixed
+
+- General bugs in the SD card state machine. This might fix some other known bugs for certain
+ combinations of switching ON and OFF SD cards and also makes the whole state machine a lot more
+ robust against hanging up.
+- SUS dummy handler went to `MODE_NORMAL` for ON commands.
+- PL PCDU dummy went to `MODE_NORMAL` for ON commands.
+
+# [v6.1.0] 2023-07-13
+
+- `eive-tmtc`: v5.2.0
+
+## Changed
+
+- TCS: Remove OBC IF board thermal module, which is exactly identical to OBC module and therefore
+ obsolete.
+- Swapped PL and PS I2C, BPX battery and MGT are connected to PS I2C now for firmware versions
+ equal or above v4. However, this software version is compatible to both v3 and v4 of the firmware.
+- The firmware version variables are global statics inititalized early during the program
+ runtime now. This makes it possible to check the firmware version earlier.
+- The TCS controller will now always command heaters OFF when being blind for thermal
+ components (no sensors available), irrespective of current switch state.
+- Make OBSW compatible to prospective FW version v5.0.0, where the Q7 I2C devices were
+ moved to a PL I2C block and the TMP sensor devices were moved to the PS I2C0.
+- Made `Xadc` code a little bit more robust against errors.
+
+## Fixed
+
+- TMP1075: Set dataset invalid on shutdown explicitely
+- Small fixes for TMP1075 FDIR: Use strange and missed reply counters.
+- TCS controller: Last heater (S-band heater) was skipped for transition completion
+ checks.
+- TCS controller: A helper member to track the elapsed heater control cycles was not reset
+ properly, which could lead to switch transitions being completed immediately. This can
+ lead to weird bugs like heaters being commanded ON twice and can potentially lead to
+ other bugs.
+- TMP1075: Devices did not go to OFF mode when being set faulty.
+- Update PL PCDU 1 in TCS mode tree on the EM.
+- TMP1075: Possibly ignored health commands.
+- Bugfix in FSFW where certain packet types could only be sent with source data fields with a
+ maximum size of 255 bytes.
+- TCS CTRL: Limit number of heater handler messages sent in case there are not sensors available
+ anymore.
+- Fix to allow adding real STR device for EM.
+
+# Added
+
+- Two events for heaters being commanded ON and OFF by the TCS controller
+- Upper limit for burn time of TCS heaters. Currently set to 1 hour for each heater.
+ This mechanism will only track the burn time for heaters which were commanded by the
+ TCS controller.
+- TCS controller is now observable by introducing a new HK dataset which exposes some internal
+ fields related to TCS control.
+
+# [v6.0.0] 2023-07-02
+
+- `q7s-package` version v3.2.0
+- Important bugfixes for PTME. See `q7s-package` CHANGELOG.
+
+## Changed
+
+- Added back PTME busy bit polling. This is necessary due to changes to the AXI APB interface
+ to the PTME core.
+
+## Fixed
+
+- For the live channel (VC0), telemetry was still only dumped if the transmitter is active.
+ Please note that this fix will lead to crashes for FW versions below v3.2.
+ However, it might not be an issue for the oldest firmware on the satellite (v2.5.1).
+
+# [v5.2.0] 2023-07-02
+
+## Fixed
+
+- The firmware information event was not triggered even when possible because of an ordering
+ bug in the initializer function.
+- Empty dumps (no TM in time range) will now correctly be completed immediately
+
+## Changed
+
+- PTME was always reset on submode changes. The reset will now only be performed if the actual data
+ rate changes.
+- Add back ACS board code for the EM. Now that the radiation sensor was removed, the image switching
+ issue has disappeared and adding back the ACS board is worth it for the GPS timekeeping.
+
+# [v5.1.0] 2023-06-28
+
+- `eive-tmtc` version v5.1.0
+
+## Changed
+
+- Persistent TM store dumps are now performed in chronological order.
+- Increase Syrlinks RX HK rate to 5.0 seconds during a pass.
+- Various robustness improvements for the heater handler. The heater handler will now only
+ process the command queue if it is not busy with switch commanding which reduces the amount
+ of possible bugs.
+- The heater handler is only able to process messages stricly sequentially now but is scheduled
+ twice in a 0.5 second slot so something like a consecutive heater ON or OFF command can still
+ be handled relatively quickly.
+
+## Added
+
+- Sequence counters for PUS and CFDP packets are now stored persistently across graceful reboots.
+- The PUS packet message type counter will now be incremented properly for each PUS service.
+- Internal error reporter set is now enabled by default and generated every 120 seconds.
+
+# [v5.0.0] 2023-06-26
+
+v3.3.1 and all following version will now be moved to v5.0.0 with the additional changes listed
+here. This was done because the firmware update (v4.0.0) is not working right now and it is not
+known when and how it will be fixed. Because of that, all updates to make the SW work with the new
+firmware, which are limited to a few files will be moved to a dev branch so regular development
+compatible to the old firmware can continue.
+
+TLDR: This version is compatible to the old firmware and some changes which only work with the new
+firmware have been reverted.
+
+## Changed
+
+- Added `sync` syscall in graceful shutdown handler
+- Graceful shutdown is now performed by the reboot watchdog
+- There is now a separate file for the total reboot counter. The reboot watchdog has its own local
+ counters to determine whether a reboot is necessary.
+
+# [v4.0.1] 2023-06-24
+
+## Fixed
+
+- `PusLiveDemux` packet demultiplexing bugfix where the demultiplexing did not work when there was
+ only one destination available.
+
+# [v4.0.0] 2023-06-22
+
+- `eive-tmtc` version v5.0.0
+- `q7s-package` version v3.1.1
+
+## Fixed
+
+- Important bugfixes for PTME. See `q7s-package` CHANGELOG.
+- TCS fixes: Set temperature values to invalid value for MAX31865 RTD handler, SUS handler
+ and STR handler. Also set dataset to invakid for RTD handler.
+- Fixed H parameter in SUS converter from 1 mm to 2.5 mm.
+
+## Changed
+
+- Removed PTME busy/ready signals. Those were not used anyway because register reads are used now.
+- APB bus access busy checking is not done anymore as this is performed by the bus itself now.
+- Core controller will now announce version and image information event in addition to reboot
+ event in the `inititalize` function.
+- Core controller will now try to request and announce the firmware version in addition to the
+ OBSW version as well.
+- Added core controller action to read reboot mechansm information
+- GNSS reset pin will now only be asserted for 5 ms instead of 100 ms.
+
+## Added
+
+- Added PL I2C reset pin. It is not used/connected for now but could be used for FDIR procedures to
+ restore the PL I2C.
+- Core controller now announces firmware version as well when requesting a version info event
+
+# [v3.3.1] 2023-06-22
+
+## Fixed
+
+- `PusLiveDemux` packet demultiplexing bugfix where the demultiplexing did not work when there was
+ only one destination available.
+
+## Fixed
+
+- Fixed H parameter in SUS converter from 1 mm to 2.5 mm.
+
+# [v3.3.0] 2023-06-21
+
+Like v3.2.0 but without the custom FM changes related to VC0.
+
+# [v3.2.0] 2023-06-21
+
+## Fixed
+
+- Fix sun vector calculation
+- SUS total vector was not reset to being a zero vector during eclipse due to a wrong memcpy
+ length.
+
+## Changed
+
+- Reverted all changes related to VC0 handling to avoid FM bug possibly related to FPGA bug.
+
+# [v3.1.1] 2023-06-14
+
+## Fixed
+
+- TMP1075 bugfix where negative temperatures could not be measured because of a two's-complement
+ conversion bug.
+
+# [v3.1.0] 2023-06-14
+
+- `eive-tmtc` version v4.1.0
+
+## Fixed
+
+- TCS heater switch enumeration naming was old/wrong and was not updated in sync with the object ID
+ update. This lead to the TCS controller commanding the wrong heaters.
+
+## Changed
+
+- Increase number of allowed parallel HK commands to 16
+
+## Added
+
+- Added `CONFIG_SET`, `MAN_HEATER_ON` and `MAN_HEATER_OFF` support for the BPX battery handler
+
+# [v3.0.0] 2023-06-11
+
+- `eive-tmtc` version v4.0.0
+
+## Changed
+
+- Adapt EM configuration to include all GomSpace PCDU devices except the ACU. For the ACU
+ (which broke), a dummy will still be used.
+- Event Manager queue depth is configurable now.
+- Do not construct and schedule broken TMP1075 device anymore.
+- Do not track payload modes in system mode tables.
+- ACS modes derived from system modes.
+- The CMake build generator will now search for the cross-compiler binaries in the environmental
+ variable named `CROSS_COMPILE_BIN_PATH` first when setting up the build system. This prevents
+ CMake from selecting wrong cross-compilers if multiple cross-compilers with the same name are used
+ on the same system.
+- Add ACS board for EM by default now.
+- Add support for MPSoC HK packet.
+- Add support for MPSoC Flash Directory Content Report.
+- Dynamically enable and disable HK packets for MPSoC on `ON` and `OFF` commands.
+- Add support for MPSoC Flash Directory Content Report.
+- Larger allowed path and file sizes for STR and PLOC MPSoC modules.
+- More robust MPSoC flash read and write command data handling.
+- Increase frequency of payload handlers from 0.8 seconds to 0.5 seconds.
+- Disable missed deadlines per default. Not useful in orbit, and triggers all the time on the EM
+ build after a number of subsequent runs, without any apparent reason (deadlines are not actually
+ missed, thread usage displayed is nominal)
+- TM store dumpes will not be cancelled anymore if the transmitter is off. The dump can be cancelled
+ with an OFF command, and the PTME is perfectly capable of dumping without the transmitter being
+ on.
+- Transmitter state is not taken into account anymore for writing into the PTME. The PTME should
+ be perfectly capable of generating a valid CADU, even when the transmitter is not ON for any
+ reason.
+- OFF mode is ignores in TM store for determining whether a store will be written. The modes will
+ only be used to cancel a transfer.
+- Handling of multiple RWs in the ACS Controller is improved and can be changed by parameter
+ commands.
+- The Directory Listing direct dumper now has a state machine to stagger the directory listing dump.
+ This is required because very large dumps will overload the queue capacities in the framework.
+- The PUS Service 8 now has larger queue sizes to handle more action replies. The PUS Service 1
+ also has a larger queue size to handle a lot of step replies now.
+- Moved PDU `vcc` and `vbat` variable from auxiliary dataset to core dataset.
+- Tweak TCP/IP configuration: Only the TCP server will be included on the EM. For the FM, no
+ TCP/IP servers will be included by default.
+
+## Added
+
+- Add the remaining system modes.
+- PLOC MPSoC flash read command working.
+- BPX battery handler is added for EM by default.
+- ACU dummy HK sets
+- IMTQ HK sets
+- IMTQ dummy now handles power switch
+- Added some new ACS parameters
+- Enabled decimation filter for the ADIS GYRs
+- Enabled second low-pass filter for L3GD20H GYRs
+
+## Fixed
+
+- CFDP low level protocol bugfix. Requires `fsfw` update and `tmtc` update.
+- Compile fix if SCEX is compiled for the EM.
+- Set up Rad Sensor chip select even for EM to avoid SPI bus issues.
+- Correct ADIS Gyroscope type configuration for the EM, where the 16507 type is used instead of the
+ 16505 type.
+- Host build is working again. Added reduced live TM helper which schedules the PUS and CFDP
+ funnel.
+- PLOC Supervisor handler now has a power switcher assigned to make PLOC power switching work
+ without an additional PCDU power switch command.
+- The PLOC Supervisor handler now waits for the replies to the `SET_TIME` command to verify working
+ communication.
+- The PLOC MPSoC now waits 10 cycles before going to on. These wait cycles are necessary because
+ the MPSoC is not ready to process commands without this additional boot time.
+- Fixed correction for `GPS Altitude` in case the sensor data is out of the expected bonds.
+- PLOC MPSoC special communication is now scheduled, which allows flash read and flash write
+ commands to work.
+- Fixed the MPSoC flash write command.
+- Added missing ACS parameter.
+- HK TM store: The HK store dump success event was triggered for cancelled HK dumps.
+- When a PUS parsing error occured while parsing a TM store file, the dump completion procedure
+ was always executed.
+- Some smaller logic fixes in the TM store base class
+- Fixed usage of C `abs` instead of C++ `std::abs`, which results in MTQ commands not being
+ scaled correctly between 1Am² and 0.2Am².
+- TCS Heater Handler: Always trigger mode event if a heater goes `OFF` or `ON`. This event might
+ soon replace the `HEATER_WENT_ON` and `HEATER_WENT_OFF` events.
+- Prevent spam of TCS controller heater unavailability event if all heaters are in external control.
+- TCS heater switch info set contained invalid values because of a faulty `memcpy` in the TCS
+ controller. There is not crash risk but the heater states were invalid.
+- STR datasets were not set to invalid on shutdown.
+- Fixed usage of quaternion valid flag, which does not actually represent the validity of the
+ quaternion.
+- Various fixes for the pointing modes of the `ACS Controller`. All modes should work now as
+ intended.
+- The variance for the ADIS GYRs now represents the used `-3` version and not the `-1` version
+- CFDP funnel did not route packets to live channel VC0
+
+# [v2.0.5] 2023-05-11
+
+- The dual lane assembly transition failed handler started new transitions towards the current mode
+ instead of the target mode. This means that if the dual lane assembly never reached the initial
+ submode (e.g. mode normal and submode dual side), it will transition back to the current mode,
+ which miht be `MODE_OFF`. Furthermore, this can lead to invalid internal states, so the subsequent
+ recovery handling becomes stuck in the custom recovery sequence when swichting power back on.
+- The dual lane custom recovery handling was adapted to always perform proper power switch handling
+ irrespective of current or target modes.
+
+# [v2.0.4] 2023-04-19
+
+## Fixed
+
+- The dual lane assembly datasets were not marked invalid properly on OFF transitions.
+
+# [v2.0.3] 2023-04-17
+
+- eive-tmtc: v3.1.1
+
+## Fixed
+
+- Fixed shadowing within the `SafeCtrl`, which prevented actuator commands to be calculated during
+ eclipse phase.
+- EM build idle mode fixes for RW dummy.
+
+## Added
+
+- Add `MGT_OVERHEATING` event and fallback to system SAFE mode if the MGT is overheating for
+ whatever reason.
+
+## Changed
+
+- Low-pass filters can no longer be executed if no actual data is available.
+
+# [v2.0.2] 2023-04-16
+
+- Bump patch version to 2.
+
+# [v2.0.1] 2023-04-16
+
+- eive-tmtc: v3.1.0
+
+# [v2.0.0] 2023-04-16
+
+This is the version which will fly on the satellite for the initial launch phase.
+
+- q7s-package: v2.5.0
+- eive-tmtc: v3.0.0
+- `wire` library is now on version v10.7 as well.
+
+## Added
+
+- Added `mv`, `cp` and `rm` action helpers for the core controller for common filesystem operations.
+- Extended directory listing helpers. There is now a directory listing helper which dumps the
+ directory listing as an action data reply immediately. For smaller directory listings, this
+ allows a listing without requiring a separate file downlink (which also has not been implemented
+ yet)
+
+## Changed
+
+- The directory listing action commands now allow compressing of either the target output file
+ for the directory listing into file action command, or compression in the helper which dumps
+ the directory listing directly.
+
+# [v1.45.0] 2023-04-14
+
+- q7s-package: v2.5.0
+- eive-tmtc: v3.0.0
+- STR firmware was updated to v10.7. `wire` library still needs to be updated.
+
+## Fixed
+
+- Small fix for `install-obsw-yocto.sh` script
+- Bugfix for STR firmware update procedure where the last remaining
+ bytes were not written properly.
+- Bugfix for STR where an invalid reply was received for special requests
+ like firmware updates.
+- Bugfix for shell command executors in command controller which lead to a crash.
+- Important bugfix for STR solution set. Missing STR mode u8 parameter.
+- Fix for STR image download.
+- Possible fix for STR image upload.
+- Fixed regression where the reply result for ACS board and SUS board devices was set to FAILED
+ even when going to OFF mode. In that case, it has to be set to OK so the device handler can
+ complete the OFF transition.
+
+## Changed
+
+- STR `wire` library updated to v10.3. Submodule renamed to `sagittactl`.
+- Custom FDIR for TMP1075 sensors. The device handlers reject `NEEDS_RECOVERY` health commands
+ anyway, so it does not really make sense to use the default FDIR.
+- Reject `NEEDS_RECOVERY` health commands for the heater health devices.
+- Adapted some queue sizes so that EM startup works without queue errors
+ - Event Manager: 120 -> 160
+ - UDP TMTC Bridge: 50 -> 120
+ - TCP TMTC Bridge: 50 -> 120
+ - Service 5: 120 -> 160, number of events handled in one cycle increased to 80
+- EM: PCDU dummy is not a device handler anymore, but a custom power switcher object. This avoids
+ some issues where the event manager could not send an event message to the PCDU dummy because
+ the FDIR event queue was too small.
+
+## Added
+
+- Add a way for the MAX31865 RTD handlers to recognize faulty/broken/off sensor devices.
+- Add parameter interface for core controller
+- Allow setting the preferred SD card via the new parameter interface of the core controller
+ with domain ID 0 and unque ID 0.
+- Added action commands to reset the PDEC. Also added autonomous reset handling for the PDEC,
+ because there is no way so send TCs with a faulty PDEC.
+- Added `I2C_REBOOT` and `PDEC_REBOOT` events for EIVE system component to ensure ground gets
+ informed.
+
+## ACS
+
+- Commanding from ACS Controller is now enabled.
+- Safe Controller was reverted to FLP Design. This also introduces safe mode strategies.
+ They contain what the controller does and which data it uses. The controller will
+ automatically based on the available data decide on which strategy to use. If a strategy
+ is undesirable (e.g. the MEKF should not be used) this can be handeld via setting parameters.
+
+# [v1.44.1] 2023-04-12
+
+- eive-tmtc: v2.22.1
+
+## Fixed
+
+- Bugfixes and improvements for SDC state machine. Internal state was not updated correctly due
+ to a regression, so commanding the SDC state machine externally lead to confusing results.
+- Heater states array in TCS controller was too small.
+- Fixed a bug in persistent TM store, where the active file was not reset of SD card switches.
+ SD card switch from 0 to 1 and vice-versa works without errors from persistent TM stores now.
+- Add a way for the SUS polling to detect broken or off devices by checking the retrieved
+ temperature for the all-ones value (0x0fff).
+- Better reply result handling for the ACS board devices.
+- ADIS1650X initial timeout handling now performed in device handler.
+- The RW assembly and TCS board assembly now perform proper power switch handling for their
+ recovery handling.
+
+## Changed
+
+- Added additional logic for SDC state machine so that the SD cards are marked unusable when
+ the active SD card is switched or there is a transition from hot redundant to cold redundant mode.
+ This gives other tasks some time to register the SD cards being unusable, and therefore provides
+ a way for them to perform any re-initialization tasks necessary after SD card switches.
+- TCS controller now only has an OFF mode and an ON mode
+- The TCS controller pauses operations related to the TCS board assembly (reading sensors and
+ the primary control loop) while a TCS board recovery is on-going.
+- Allow specifying custom OBSW update filename. This allowed keeping a cleaner file structure
+ where each update has a name including the version
+- The files extracted during an update process are deleted after the update was performed to keep
+ the update directory cleaner.
+
+## Added
+
+- TCS controller: SUBMODE_NO_HEATER_CTRL (1) added for ON mode. If this submode is
+ commanded, all heaters will be switched off and then no further heater
+ commanding will be done.
+- Fixed a bug in persistent TM store, where the active file was not reset of SD card switches.
+ SD card switch from 0 to 1 and vice-versa works without errors from persistent TM stores now.
+
+# [v1.44.0] 2023-04-07
+
+- eive-tmtc: v2.22.0
+
+## Added
+
+- Special I2C recovery handling. If the I2C bus is unavailable for whatever reason, the EIVE
+ system component will power-cycle all I2C devices by first going to the OFF/BOOT mode, then
+ power-cycling the 3V3 stack and rebooting the battery, and finally going back to safe mode.
+ If this does not restore the bus, a full reboot will be performed. This special sequence can
+ be commanded as well.
+
+## Fixed
+
+- RW Assembly: Correctly transition back to off when more than 1 devices is OFF. Also do this
+ when this was due to two devices being marked faulty.
+- RW dummy and STR dummy components: Set/Update modes correctly.
+- RW handlers: Bugfix for TM set retrieval and special request handling in general where the CRC
+ check always failed for special request. Also removed an unnecessary delay for special requests.
+- RW handlers: Polling is now disabled for RWs which are off.
+
+## Changed
+
+- RW shutdown now waits for the speed to be near 0 or for a OFF transition countdown to be expired
+ before going to off.
+
+# [v1.43.2] 2023-04-05
+
+## Changed
+
+- Adapted HK data rates to new table for LEOP SAFE mode.
+- GPS controller HK is now generated periodically as well.
+- Better mode combination checks for assembly components. This includes:
+ - IMTQ assembly
+ - Syrlinks assembly
+ - Dual Lane Assembly
+- RWs are no longer commanded by the ACS Controller during safe mode. Instead the RW speed command
+ is set to 0 as part or the `doShutDown` of the RW handler.
+
+## Fixed
+
+- Dual lane assemblies: Fix handling when health states are overwritten. Also add better handling
+ when some devices are permanent faulty and some are only faulty. In that case, only the faulty
+ devices will be restored.
+- ACS dual lane assembly: Gyro 3 helper mode was assigned to the Gyro 2 mode.
+
+# [v1.43.1] 2023-04-04
+
+## Fixed
+
+- Generic HK handling: Bug where HKs were generated a lot more often than required. This is the case
+ if a device handler `PERFORM_OPERATION` step is performed more than once per PST cycle.
+- Syrlinks now goes to `_MODE_TO_ON` when finishing the `doStartUp` transition.
+
+## Changed
+
+- Doubled GS PST interval instead of scheduling everything twice.
+- Syrlinks now only has one `PERFORM_OPERATION` step, but still has two communication steps.
+- PCDU components only allow setting `NEEDS_RECOVERY`, `HEALTHY` and `EXTERNAL_CONTROL` health
+ states now. TMP sensor components only allow `HEALTHY` , `EXTERNAL_CONTROL`, `FAULTY` and
+ `PERMANENT_FAULTY`.
+- TCS controller now does a sanity check on the temperature values: Values below -80 C or above
+ 160 C are ignored.
+
+# [v1.43.0] 2023-04-04
+
+- q7s-package: v2.4.0
+- eive-tmtc: v2.21.0
+
+## Added
+
+- Version of thermal controller which performs basic control tasks.
+- PCDU handler can now command switch of the 3V3 stack (switch ID 19)
+- Set STR dev to OFF in assembly when it is faulty.
+- STR: Reset data link layer and flush RX for regular commands and before performing special
+ commands to ensure consistent start state
+
+## Fixed
+
+- PTME was not reset after configuration changes.
+- GPS health devices: ACS board assembly not reacts to health changes.
+- STR COM helper: Reset reply size after returning a reply
+
+## Changed
+
+- Poll threshold configuration of the PTME IP core is now configurable via a parameter command
+ and is set to 0b010 (4 polls) instead of 0b001 (1 poll) per default.
+- EIVE system fallback and COM system fallback: Perform general subsystem handling first, then
+ event reception, and finally any new transition handling.
+- IMTQ MGM integration time lowered to 6 ms. This relaxes scheduling requirements a bit.
+- PCDU handler switcher HK set now has additional 3V3 switcher state HK.
+
+# [v1.42.0] 2023-04-01
+
+- eive-tmtc: v2.20.1
+- q7s-package: v2.3.0
+
+## Changed
+
+- SCEX filename updates. Also use T as the file ID / date separator between date and time.
+- COM TM store and dump handling: Introduce modes for all 4 TM VC/store tasks. The OFF mode can be
+ used to disable ongoing dumps or to prevent writes to the PTME VC. This allows cleaner reset
+ handling of the PTME. All 4 VC/store tasks were attached to the COM mode tree and are commanded
+ as part of the COM sequence as well to ensure consistent state with the CCSDS IP core handler.
+- Added `PTME_LOCKED` boolean lock which is used to lock the PTME so it is not used by the VC tasks
+ anymore. This lock will be controlled by the CCSDS IP core handler and is locked when the PTME
+ needs to be reset. Examples for this are datarate changes.
+- Simulate real PCDU in PCDU dummy by remembering commandes switch change and triggering appropriate
+ events. Switch feedback is still immediate.
+- GomSpace devices are polled with a doubled frequency. This speeds up power switch commanding.
+
+## Fixed
+
+- Bugfix for side lane transitions of the dual lane assemblies, which only worked when the
+ assembly was directly commanded.
+- Syrlinks Handler: Bugfix so transition command is only sent once.
+- SCEX file name bug: Create file name time stamp with `strftime` similarly to how it's done
+ for the persistent TM store.
+
+## Added
+
+- Added GPS0 and GPS1 health device which are used by the ACS board assembly when deciding whether
+ to change to the other side or to go to dual side directly. Setting the health devices to faulty
+ should also trigger a side switch or a switch to dual mode.
+
+# [v1.41.0] 2023-03-28
+
+- eive-tmtc: v2.20.0
+- q7s-package: v2.2.0
+
+## Fixed
+
+- Proper Faulty/External Control handling for the dual lane assemblies.
+- ACS board devices: Go to ON mode instead of going to NORMAL mode directly.
+- SUS device handlers: Go to ON mode on startup instead of NORMAL mode.
+- Tweaks for the delay handling for the persistent TM stores. This allows pushing the full
+ high datarate when dumping telemetry. The most important and interesting fix is that
+ there needs to be a small delay between the polling of the GPIO. Polling the GPIO
+ without any delay consecutively can lead to scheduling issues.
+- Bump FSFW for fix of `ControllerBase` class `startTransition` implementation.
+- Bump FSFW for possible fix of `PowerSwitcherComponent`: Initial mode `MODE_UNDEFINED`.
+
+## Changed
+
+- Enabled periodic hosuekeeping generation for release images.
+- Project structure (linux and mission folder) is subsystem centric now.
+
+# [v1.40.0] 2023-03-24
+
+- eive-tmtc: v2.19.4
+- q7s-packasge: v2.1.0
+- Bumped fsfwgen for bugfix: Event translation can deal with duplicate event names now.
+
+## Fixed
+
+- PAPB busy polling now implemented properly with an upper bound of how often the PAPB is allowed
+ to be busy before returning the BUSY returnvalue. Also propagate and check for that case properly.
+ Ideally, this will never be an issue and the PAPB VC interface should never block for a longer
+ period.
+- The `mekfInvalidCounter` now resets on setting the STR to faulty.
+- Improve the SD lock handling. The file handling does not need to be locked as it
+ is only handled by one thread.
+
+## Added
+
+- The event `MEKF_RECOVERY` will be triggered in case the `MEKF` does manage to recover itself.
+- The persistent TM stores now have low priorities and behave like background threads now. This
+ should prevent them from blocking or slowing down the system even during dumps
+ (at least in theory).
+- STR: Fix weird issues on datalink layer data reception which sometimes occur.
+- Syrlinks FDIR: Fully allow FDIR to do more recoveries. Assembly should take care of preventing
+ the switch to go off.
+- Allow dual lane assembly side switches.
+
+## Changed
+
+- Rework FSFW OSALs to properly support regular scheduling (NICE priorities) and real-time
+ scheduling.
+- STR: Move datalink layer to `StrComHandler` completely. DLL is now completely hidden from
+ device handler.
+- STR: Is now scheduled twice in ACS PST.
+- `StrHelper` renamed to `StrComHandler`, is now a `DeviceHandlerIF` directly and does not wrap
+ a separate UART COM interface anymore.
+- TCS: Local pool variables are members now.
+- Syrlinks: Create dedicated COM helper which uses a ring buffer to parse the Syrlinks datalinklayer
+ and should make communication more reliable even on high CPU loads.
+- Syrlinks: Two communication cycles per PST.
+- Fine-tuning of various task priorities.
+- The CSP router now is scheduled with the `SCHED_RR` policy and the same priority as the PCDU
+ handlers as well.
+- Change project structure to be more subsystem centric for ACS and COM.
+
+# [v1.39.1] 2023-03-22
+
+## Fixed
+
+- Bugfix for STR: Some action commands wrongfully declined.
+- STR: No normal command handling while a special request like an image upload is active.
+- RS485 data line was not enabled when the transmitter was switched on.
+
+# [v1.39.0] 2023-03-21
+
+Requires firmware update for new FPGA design where reset line is routed into the software.
+2 relevant PRs:
+ - https://egit.irs.uni-stuttgart.de/eive/q7s-vivado/pulls/53
+ - https://egit.irs.uni-stuttgart.de/eive/q7s-vivado/pulls/54
+
+eive-tmtc: v2.19.3
+
+## Added
+
+- Added NaN and Inf check for the `MEKF`. If these are detected, the `AcsController` will reset
+ the `MEKF` on its own once. This way, there will not be an event spam and operators will have
+ to look into the reason of wrong outputs. To restore the reset ability, an action command has
+ been added.
+- Contingency handling for non-working I2C bus bug. Reboot the system if the I2C is not working.
+
+## Fixed
+
+- Fixed transition for dual power lane assemblies: When going from dual side submode to single side
+ submode, perform logical commanding first, similarly to when going to OFF mode.
+- GPS time is only set to valid if at least one satellite is in view.
+
+## Changed
+
+- Bugfixes for STR mode transitions: Booting to mode ON with submode FIRMWARE now works properly.
+ Furthermore, the submode in the NORMAL mode now should be 0 instead of some ON mode submode.
+- Updated GYR bias values to newest measurements. This also corrects the ADIS values to always
+ consit of just one digit.
+- The CCSDS IP core handler now exposes a parameter to enable the priority select mode
+ for the PTME core. This mode prioritizes virtual channels with a lower index, so for example
+ the virtual channel (VC0) will have the highest priority, while VC3 will have the lowestg
+ priority. This mode will be enabled by default for now, but can be set via the parameter IF with
+ the unique parameter ID 0. The update of this mode requires a PTME reset. Therefore, it will only
+ be performed when the transmitter is off to avoid weird bugs.
+- Connect and handle reset line for the PTME core in the software now.
+- Safe mode controller failure event now only triggers once per minute.
+
+# [v1.38.0] 2023-03-17
+
+eive-tmtc: v2.19.2
+
+## Fixed
+
+- SA deployment file handling: Use exceptionless API.
+- Fix deadlock in SD card manager constructor: Double lock of preferred SD card lock.
+
+## Added
+
+- Failure of Safe Mode Ctrl will now trigger an event. As this can only be caused by sensors not
+ being in the correct mode, the assemblies should take care that this will never happen and no
+ additional FDIR is needed.
+
+## Changed
+
+- Telemetry relevant datasets for the RWs are now set invalid and partially reset on shotdown.
+- I2C PST now has a polling frequency of 0.4 seconds instead of 0.2 seconds.
+- GS PST now has a polling frequency of 0.5 seconds instead of 1 second.
+- Bump FSFW: merged upstream.
+- Move BPX battery scheduling to ACS PST to avoid clashes with IMTQ scheduling / polling
+
+# [v1.37.2] 2023-03-14
+
+- Changed `PoolManager` bugfix implementation in the FSFW.
+- Some tweaks for IPC and TM store configuration
+
+# [v1.37.1] 2023-03-14
+
+This version works on the EM as well.
+
+eive-tmtc: v2.19.1
+
+## Added
+
+- Added `EXECUTE_SHELL_CMD` action command for `CoreController` to execute arbitrary Linux commands.
+- Added some missing PLOC commands.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/462
+- Add `PcduHandlerDummy` component.
+- Added parameter for timeout until `MEKF_INVALID_MODE_VIOLATION` event is triggered.
+
+## Fixed
+
+- Pointing control of the `AcsController` was still expecting submodes instead of modes.
+- Limitation of RW speeds was done before converting them to the correct unit scale.
+- The Syrlinks task now has a proper name instead of `MAIN_SPI`.
+- Make whole EIVE system initial transition work for the EM. This was also made possible by
+ always scheduling most EIVE components instead of tying the scheduling to preprocessor defines.
+- Store more TCP und UDP packets.
+- Bump fsfw for small tweak in local datapool manager: Re-enable or re-disabling dataset
+ generation will not fail anymore.
+- Bump FSFW to simplify HK helpers: Was previously buggy and did not allow to use
+ minmum sampling frequency. Now both diagnostics and HK can use minimum
+ sampling frequency for requesting HK
+- Bump FSFW to allow the TC/TM/IPC pools to spill to higher pools/pool pages.
+
+## Changed
+
+- Set `OBSW_ADD_TCS_CTRL` to 1. Always add TCS controller now for both EM and FM.
+- generators module: Bump `fsfwgen` dependency to v0.3.1. The returnvalue CSV files are now sorted.
+- generators module: Always generate subsystem ID CSV files now.
+- The COM subsystem is now not commanded by the EIVE system anymore. Instead,
+ a separate RX_ONLY mode command will be sent at OBSW boot. After that,
+ commanding is done autonomously by the COM subsystem internally or by the operator. This prevents
+ the transmitter from going off during fallbacks to the SAFE mode, which might not always be
+ desired.
+- Initialize switch states to a special `SWITCH_STATE_UNKNOWN` (2) variable. Return
+ `PowerSwitchIF::SWITCH_UNKNOWN` in switch state getter if this is the state.
+- Wait 1 second before commanding SAFE mode. This ensures or at least increases the chance that
+ the switch states were initialized.
+- Dual Lane Assemblies: The returnvalues of the dual lane power state machine FSM are not ignored
+ anymore.
+
+# [v1.37.0] 2023-03-11
+
+eive-tmtc: v2.18.1
+
+## Added
+
+- `SensorProcessing` now includes an FDIR for GPS altitude. If the measured GPS altitude is out
+ of bounds of the range defined in the `AcsParameters`, the altitude defaults to an altitude
+ set in the `AcsParameters`.
+- `AcsController` will now never command a RW speed larger than the maximum allowed speed.
+
+## Fixed
+
+- `PAPB_EMPTY_SIGNAL_VC1` GPIO was not set up properly.
+- Fix for heater names: HPA heater (index 7) is now the Syrlinks heater.
+- `AcsParameters` setter were previously all for scalar parameters. Now vector and matrix
+ parameters use their respective setters.
+- Several `AcsController` components had their own implementation of `AcsParameters`. This resulted
+ in those parameters not being updated, while the actual ones were updated. All instances of
+ `AcsParameters` not belonging to `AcsController` are eiter removed or replaced by pointer
+ instances.
+- Instead of updating the `gsTargetModeControllerParameters`, the `targetModeControllerParameters`
+ were updated.
+- Instead of updating the `idleModeControllerParameters`, the `targetModeControllerParameters`
+ were updated.
+- Fixed Idle Mode Controller never calling `ptgLaw` and therefore never calculating control
+ values.
+- Fixed wrong check on wether file used for persistant boolean flag on successful still existed.
+- Scaling of MTQ Cmds now scales the current values to command with the current values and not
+ the values of the last step, which would result in undefined behaviour.
+- Solved naming collision between file used for solar array deployment and confirmation for
+ ACS for solar array deployment.
+- Fixed that scaling of RW torque would result in a zero vector unless the maximum value was exceeded.
+- Bias for the GYR data was substracted within the wrong rf (sensor rf vs body rf).
+
+## Changed
+
+- Refactored TM pipeline to optimize usage of the PTME and communication downlink bandwidth.
+ This was done by moving the dumping of TMs to the VCs into separate threads with permanent loops.
+ These threads are then able to process high TM loads on demand. The PUS TM funnel will route
+ PUS packets to the approrpiate persisten TM stores and then demultiplex the TM to all registered
+ TM destinations as before.
+- Service 5 now handles 40 events per cycle instead of 15
+- Remove periodic SD card check. The file system is not mounted read-only anymore when using an
+ ext4 filesystem
+- The `detumbleCounter` now does not get hard reset anymore, if the critical rate does not get
+ violated anymore. Instead it is incrementally reset.
+- The RW antistiction now only takes the RW speeds in account.
+- ACS CTRL transition to DETUBMLE is now done in CTRL internally. No
+ system level handling necessary anymore.
+- More fixes and improvements for SD card handling. Extend SD card setup in core controller to
+ create full initial state for SD card manager are core controller as early as possible, turn
+ execution of setup file update blocking. This might solve the issue with the SD card manager
+ sometimes blocking for a long time.
+- Request raw MTM measurement twice for IMTQ, might reduce number of times measurement could not
+ be retrieved.
+- Event manager and event service have larger queues now: 45 -> 120 for Service 5, 80 -> 120 for
+ event manager
+- ACS mode changes: The ACS CTRL submodes are now modes. DETUBMLE is now submode of SAFE mode.
+- EIVE system now tracks the mode of the ACS subsyste in SAFE mode.
+
+# [v1.36.0] 2023-03-08
+
+eive-tmtc: v2.17.2
+
+## Added
+
+- Star Tracker Assembly
+- New `REBOOT_COUNTER` and `INDIVIDUAL_BOOT_COUNTS` events. The first contains the total boot count
+ as a u64, the second one contains the individual boot counts as 4 u16. Add new core controller
+ action command `ANNOUNCE_BOOT_COUNTS` with action ID 3 which triggers both events. These events
+ will also be triggered on each reboot.
+
+## Changed
+
+- Persistent TM stores will now create new files on each reboot.
+- Fast ACS subsystem commanding: Command SUS board consecutively with other devices now
+- Star Tracker: Use ground confguration for EM and flight config for FM by default.
+
+## Fixed
+
+- Command TCS controller off first for TCS subsystem transition to off.
+- Health handling for TCS board assembly
+- Mode fallback from IDLE mode to SAFE mode due to ACS errors/events now works properly for
+ the ACS subsystem
+- Bugfix in IDLE transition for system.
+- `std::filesystem` API usages: Avoid exceptions by using variants which return an error code
+ instead of throwing exceptions.
+- GPS fix loss was not reported if mode is unset.
+- Star Tracker: OFF to NORMAL transition now posssible. Requires FSFW bump which sets
+ transition source modes properly for those transitions.
+ FSFW PR: https://egit.irs.uni-stuttgart.de/eive/fsfw/pulls/131
+- Star Tracker JSON initialization is now done during object initization instead of redoing it
+ when building a command. This avoids missed deadlines issues in the ACS PST.
+- Allow arbitrary submodes for dual lane boards to avoid FDIR reactions of subsystem components.
+ Bump FSFW to allow this.
+- PUS 15 was not scheduled
+- Transmitter timeout set to 2 minutes instead of 15 minutes. This will prevent to discharge the
+ battery in case the syrlinks starts transmitting due to detection of unintentional bitlock. This
+ happened e.g. on ground when the uplink to the flying latop was established.
+- ACS system components are now always scheduled (EM specific)
+
+# [v1.35.1] 2023-03-04
+
+## Fixed
+
+- ACS Board Assembly FDIR: Prevent permanent SAFE mode fallbacks by introducing special health
+ handling.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/418/files
+- Watchdog fixes
+- IMTQ timing fixes
+
+## Added
+
+- Add IMTQ assembly
+
+# [v1.35.0] 2023-03-04
+
+eive-tmtc: v2.16.4
+
+## Added
+
+- Improved the OBSW watchdog by adding a watch functionality. The watch functionality is optional
+ and has to be enabled specifically by the application being watched by the watchdog when
+ starting the watchdog. If the watch functionality is enabled and the OBSW has not pinged
+ the watchdog via the FIFO for 2 minutes, the watchdog will restart the OBSW service via systemd.
+ The primary OBSW will only activate the watch functionality if it is the OBSW inside the
+ `/usr/bin` directory. This allows debugging the system by leaving flashed or manually copied
+ debugging images 2 minutes to start the watchdog without the watch functionality.
+
+## Fixed
+
+- Bumped FSFW: `Countdown` and `Stopwatch` use new monotonic clock API now.
+- IMTQ: Various fixes, most notably missing buffer time after starting MGM measurement
+ and corrections for actuator commanding.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/430
+
+# [v1.34.0] 2023-03-03
+
+eive-tmtc: v2.16.3
+
+This might include the fix for the race condition where CPU usage jumped to 200 %. The race
+condition was traced to the `Countdown` class, more specifically to the `getUptime` function where
+the `/proc/uptime` file is read.
+
+## Changed
+
+- The SD card prefix is now set earlier inside the `CoreController` constructor
+- The watchdog handling was moved outside the `CoreController` into the main loop.
+- Moved polling of all SPI parts to the same PST.
+- Allow quicker transition for the EIVE system component by allowing consecutive TCS and ACS
+ component commanding again.
+- Changed a lot of lock guards to use timeouts
+- Queue sizes of TCP/UDP servers increased from 20 to 50
+- Significantly simplified and improved lock guard handling in TCS and ACS board polling
+ tasks.
+
+## Fixed
+
+- IMTQ: Sets were filled with wrong data, e.g. Raw MTM was filled with calibrated MTM measurements.
+- Set RM3100 dataset to valid.
+- Fixed units in calculation of ACS control laws safe and detumble.
+- Bump FSFW for change in Countdown: Use system clock instead of reading uptime from file
+ to prevent possible race condition.
+- GPS: No fix considered a fault now after 30 minutes instead of 5 hours.
+- SUS Assembly FDIR: Prevent permanent SAFE mode fallbacks by introducing special health
+ handling.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/418/files
+
+## Added
+
+- Added Syrlinks Assembly object to allow recovery handling and to fix faulty FDIR behaviour.
+
+# [v1.33.0] 2023-03-01
+
+eive-tmtc: v2.16.2
+
+## Changed
+
+- Move ACS board polling to separate worker thread.
+- Move SUS board polling to separate worker thread.
+
+## Fixed
+
+- Linux GPS handler now checks the individual `*_SET` flags when analysing the `gpsd` struct.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/400
+
+# [v1.32.0] 2023-02-24
+
+eive-tmtc: v2.16.1
+
+## Fixed
+
+- ADIS1650X: Added missing MDL_RANG pool entry for configuration set
+- Bumped FSFW for bugfix in health service: No execution complete for targeted health announce
+ command.
+- Removed matrix determinant calculation as part of the `MEKF`, which would take about
+ 300ms of runtime
+- Resetting the `MEKF` now also actually resets its stored state
+- Bumped FSFW for bugfix in destination handler: Better error handling and able to process
+ destination folder path.
+
+## Changed
+
+- Added basic persistent TM store for PUS telemetry and basic interface to dump and delete
+ telemetry. Implementation is based on a timed rotating files, with the addition that files
+ might be generated more often if the maximum file size of 8192 bytes is exceeded.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/320/files
+- Commented out commanding of actuators as part of the `AcsController`
+- Collection sets of the `AcsController` now get updated before running the actual ACS
+ algorithm
+- `GpsController` now always gets scheduled
+- The `CoreController` now initializes the initial clock from the time file as early as possible
+ (in the constructor) if possible, which should usually be the case.
+
+## Added
+
+- Added basic persistent TM store for PUS telemetry and basic interface to dump and delete
+ telemetry.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/320/files
+- `ExecutableComIfDummy` class to have a dummy for classes like the RTD polling class.
+- Added `AcsController` action command to confirm solar array deployment, which then deletes
+ two files
+- Added `AcsController` action command to reset `MEKF`
+- `GpsCtrlDummy` now initializes the `gpsSet`
+- `RwDummy` now initializes with a non faulty state
+
+
+# [v1.31.1] 2023-02-23
+
+## Fixed
+
+- ADIS1650X configuration set was empty because the local pool variables were not registered.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/402
+- ACS Controller: Correction for size of MEKF dataset and some optimization and fixes
+ for actuator control which lead to a crash.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/403
+
+# [v1.31.0] 2023-02-23
+
+eive-tmtc: v2.16.0
+
+## Fixed
+
+- Usage of floats as iterators and using them to calculate a uint8_t index in `SusConverter`
+- Removed unused variables in the `AcsController`
+- Remove shadowing variables inside ACS assembly classes.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/issues/385
+
+## Changed
+
+COM PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/364
+
+* Moved transmitter timer and handling of carrier and bitlock event from CCSDS handler to COM
+ subsystem
+* Added parameter command to be able to change the transmitter timeout
+* Solves [#362](https://egit.irs.uni-stuttgart.de/eive/eive-obsw/issues/362)
+* Solves [#360](https://egit.irs.uni-stuttgart.de/eive/eive-obsw/issues/360)
+* Solves [#361](https://egit.irs.uni-stuttgart.de/eive/eive-obsw/issues/361)
+* Solves [#386](https://egit.irs.uni-stuttgart.de/eive/eive-obsw/issues/386)
+- All `targetQuat` functions in `Guidance` now return the target quaternion (target
+ in ECI frame), which is passed on to `CtrlValData`.
+- Moved polling sequence table definitions and source code to `mission/core` folder.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/395
+
+## Added
+
+- `MEKF` now returns an unique returnvalue depending on why the function terminates. These
+ returnvalues are used in the `AcsController` to determine on how to procede with its
+ perform functions. In case the `MEKF` did terminate before estimating the quaternion
+ and rotational rate, an info event will be triggered. Another info event can only be
+ triggered after the `MEKF` has run successfully again. If the `AcsController` tries to
+ perform any pointing mode and the `MEKF` fails, the `performPointingCtrl` function will
+ set the RWs to the last RW speeds and set a zero dipole vector. If the `MEKF` does not
+ recover within 5 cycles (2 mins) the `AcsController` triggers another event, resulting in
+ the `AcsSubsystem` being commanded to `SAFE`.
+- `MekfData` now includes `mekfStatus`
+- `CtrlValData` now includes `tgtRotRate`
+
+# [v1.30.0] 2023-02-22
+
+eive-tmtc: v2.14.0
+
+Event IDs for PDEC handler have changed in a breaking manner.
+
+## Added and Fixed
+
+- PDEC: Added basic FDIR to limit the number of allowed TC interrupts and to allow complete task
+ lockups in the case an IRQ is immediately re-raised by the PDEC module. This is done by only
+ allowing a certain number of handled IRQs (whether they yield a valid TC or not) during
+ time windows of one second. Right now, 800 IRQs/TCs are allowed per time window.
+ This time window is reset if a TC reception timeout after 500ms occurs. TBD whether the maximum
+ allowed number will be a configurable parameter. If the number of occured IRQs is exceeded,
+ an event is triggered and the task is delayed for 400 ms.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/393
+
+# [v1.29.1] 2023-02-21
+
+## Fixed
+
+- Limit number of handled messages for core TM handlers:
+ - https://egit.irs.uni-stuttgart.de/eive/eive-obsw/issues/391
+ - https://egit.irs.uni-stuttgart.de/eive/eive-obsw/issues/390
+ - https://egit.irs.uni-stuttgart.de/eive/eive-obsw/issues/389
+- HeaterHandler better handling for faulty message reception
+ Issue: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/issues/388
+- Disable stopwatch in MAX31865 polling task
+
+# [v1.29.0] 2023-02-21
+
+eive-tmtc: v2.13.0
+
+## Changed
+
+- Refactored IMTQ handlers to also perform low level I2C communication tasks in separate thread.
+ This avoids the various delays needed for I2C communication with that device inside the ACS PST.
+ (e.g. 1 ms delay between each transfer, or 10 ms integration delay for MGM measurements).
+
+## Added
+
+- Added new heater info set for the TCS controller. This set contains the heater switch states
+ and the current draw.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/351
+- The HeaterHandler now exposes a mode which reflects whether the heater power
+ is on or off. It also triggers mode events for its heater children objects
+ which show whether the specific heaters are on or off. The heater handler
+ will be part of the TCS tree.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/351
+
+# [v1.28.1] 2023-02-21
+
+## Fixed
+
+- Patch version which compiles for EM
+- CFDP Funnel bugfix: CCSDS wrapping was buggy and works properly now.
+- PDEC: Some adaptions to prevent task lockups on invalid FAR states.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/393
+- CMakeLists.txt fix which broke CI/CD builds when server could not retrieve full git SHA.
+- Possible regression in the MAX31865 polling task: Using a `ManualCsLockGuard` for reconfiguring
+ and then polling the sensor is problematic, invalid sensor values will be read.
+ CS probably needs to be de-asserted or some other HW/SPI specific issue. Letting the SPI ComIF
+ do the locking does the job.
+
+## Changed
+
+- Add `-Wshadow=local` shadowing warnings and fixed all of them
+- Updated generated CSV files: Support for skip directive and explicit
+ "No description" info string
+- The polling threads for actuator polling now have a slightly higher priority than the ACS PST
+ to ensure timing requirements are met.
+
+## Added
+
+- git post checkout hook which initializes and updates the submodules
+ automatically.
+
+# [v1.28.0] 2023-02-17
+
+eive-tmtc: v2.12.7
+
+## Added
+
+- In case the ACS Controller does recognize more than one RW to be invalid and therefore not
+ available, it does not perform pointing control but aborts shortly after `sensorProcessing`. If the
+ problem persits for 5 ACS cycles, the `MULTIPLE_RW_INVALID` event is triggered, which invokes the
+ transition of the `AcsSubsystem` to safe mode.
+
+## Changed
+
+- Igrf13 model vector now outputs as uT instead of nT
+- Changed timings for `AcsPst`, more time for sun sensors.
+- Added values for MGM sensor fusion
+- Refactored RW Software: Polling runs in separate thread, all RWs are now polled in under 60 ms.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/381
+- Bumped FSFW to allow initializing child modes in `SubsystemBase` derived objects.
+
+## Fixed
+
+- Fixed values for GYR sensor fusion
+- Fixed speed types for `rwHandlingParameter`
+- Pseudo inverse used for allocating torque to RWs and RW antistiction now actually consider the
+ state of the RWs
+
+# [v1.27.2] 2023-02-14
+
+Reaction Wheel handling was determined to be (quasi) broken and needs to be fixed in future release
+to be usable by ACS controller.
+
+eive-tmtc: v2.12.6
+
+## Added
+
+- Function for the ACS controller to command MTQ and RWs called by all subroutines
+- RwHandler now handles commanding of RW speeds via RwSpeedActuationSet
+- Tracing supports which allows checking whether threads are running as usual.
+
+## Changed
+
+- Remove 2 TCS threads.
+- Move low level polling into ACS PST, move high level device handlers into TCS system task.
+- ActCmds now returns command vectors as integers as required by the actuators
+ and scales them to the appropriate range
+- All RwHandler are now polled five times per ACS cycle
+- Remove 2 TCS threads. Move low level polling into ACS PST, move high level device handlers into
+ TCS system task.
+- Further reduce number of threads:
+ 1. Remove PUS low priority task, move assigned threads to the generic system task
+ 2. Group events and verification tasks into PUS high priority task
+ 3. Group all other components into PUS medium priority task
+ 4. Add SCEX device handler to PL task, remove dedicated thread
+
+## Removed
+
+- lwgps dependency not compiled anymore, is not used
+
+# [v1.27.1] 2023-02-13
+
+## Fixed
+
+- Fix for SPI ComIF: Set transfer size to 0 for failed transfers
+- Fix shadowing issue with locks in MAX31865 low level handler
+
+# [v1.27.0] 2023-02-13
+
+eive-tmtc: v2.12.5
+
+Added EIVE system top mode component. Currently, only SAFE and IDLE mode are
+implemented, and the system does not do more than commanding TCS and ACS
+into the correct modes. It does not have a lot of mode tracking capabilities
+yet because the ACS controller might alternate between SAFE and DETUMBLE.
+It takes around 5-10 seconds for the EIVE system to reach the SAFE mode.
+
+The new system is used at software boot to command the satellite into safe mode
+on each reboot. This behaviour can be disabled with the
+`OBSW_SWITCH_TO_NORMAL_MODE_AFTER_STARTUP` flag.
+
+## Added
+
+- New EIVE system component like explained above.
+
+## Changed
+
+- The satellite now commands itself into SAFE mode on each reboot, which
+ triggers a lot of events on each SW reboot. The TCS subsystem will commanded
+ to NORMAL mode immediately while the ACS subsystem will be commanded to
+ SAFE mode. The payload subsystem will be commanded OFF.
+- `RELEASE_BUILD` flag moved to `commonConfig.h`
+- The ACS subsystem transitions are now staggered: The SUS board assembly
+ is commanded as a separate transition. This reduces the risk of long bus lockups.
+- No INFO mode event translations for release builds to reduce number of
+ printouts.
+- More granular locking inside the MAX31865 low level read handler.
+
+## Fixed
+
+- More DHB thermal module fixes.
+- ACS PST frequency extended to 0.8 seconds in debug builds to avoid SPI
+ bus lockups.
+- Local datapool fixes for the `PlocSupervisorHandler`
+
+# [v1.26.4] 2023-02-10
+
+eive-tmtc: v2.12.3
+
+## Fixed
+
+- `SdCardManager.cpp` `isSdCardUsable`: Use `ext4` instead of `vfat` to check read-only state.
+
+# [v1.26.3] 2023-02-09
+
+eive-tmtc: v2.12.2
+
+## Added
+
+- First version of a TCS controller heater control loop, but
+ the loop is disabled for now.
+
+## Changed
+
+- Reworked dummy handling for the TCS controller.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/325
+- Generator scripts now generate files for hosted and for Q7S build.
+
+## Fixed
+
+- GPS Controller: Set fix value to 0 when switching off to allow
+ `GPS_FIX_CHANGE` to work when switching the GPS back on.
+
+# [v1.26.2] 2023-02-08
+
+## Changed
+
+- ACS Controller scheduling is now configurable via the `eive/definitions.h` file. Also ensured
+ that scheduling is done in big blocks to reduce risk of missed deadlines.
+- Replaced chained locks for polling new sensor data to the `AcsController`.
+- Made TM store even larger.
+
+## Fixed
+
+- Bugfix for PDEC handler which causes the PIR register of the PDEC to never
+ be cleared on release builds. The dummy variable used to read the register
+ needs to be declared volatile to avoid compiler optimizations.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/374
+- Bumped FSFW for fix of possible memory leaks in TCP/IP TMTC bridge
+ inside the FSFW.
+
+## Added
+
+- Create TCS controller for EM build.
+
+# [v1.26.1] 2023-02-08
+
+- Initialize parameter helper in ACS controller.
+
+# [v1.26.0] 2023-02-08
+
+eive-tmtc v2.12.1
+
+## Changed
+
+### ACS
+
+- Readded calibration matrices for MGM calibration.
+- Added calculation of satellite velocity vector from GPS position data
+- Added detumble mode using GYR values
+- Added inertial pointing mode
+- Added nadir pointing mode
+- Added ground station target mode
+- Added antistiction for RWs
+- Added `sunTargetSafe` differentiation for LEOP
+- Added check for existance of `SD_0_SKEWED_PTG_FILE` and `SD_1_SKEWED_PTG_FILE` to determine
+ which `sunTargetSafe` to use
+- Added `gpsVelocity` and `gpsPosition` to `gpsProcessed`
+- Removed deprecated `OutputValues`
+- Added `HasParametersIF` to `AcsParameters`
+- Added `ReceivesParameterMessagesIF` and `ParameterHelper` to `AcsController`
+- Updated `AcsParameters` with actual values and changed structure
+- Sun vector model and magnetic field vector model calculations are always executed now
+- `domainId` is now used as identifier for parameter structs
+- Changed onboard GYR value handling from deg/s to rad/s
+
+## Fixed
+
+- Single sourcing the version information into `CMakeLists.txt`. The `git describe` functionality
+ is only used to retrieve the git SHA hash now. Also removed `OBSWVersion.h` accordingly.
+- Build system: Fixed small bug, where the version itself was
+ stored as the git SHA hash in `commonConfig.h`. This will be
+ an empty string now for regular versions.
+- Bump FSFW for important fix in PUS mode service.
+
+### ACS
+
+- Bugfixes in 'SensorProcessing' where previously MGM values would be calibrated before being
+ transformed in body RF. However, the calibration values are in the body RF. Also fixed the
+ validity flag of 'mgmVecTotDerivative'.
+- Fixed calculation of model sun vector
+- Fixed calculation of model magnetic field vector
+- Fixed MEKF algorithm
+- Fixed several variable initializations
+- Fixed several variable types
+- Fixed use of `sunMagAngleMin` for safe mode
+- Fixed MEKF not using correct `sampleTime`
+- Fixed assignment of `SUS0` and `SUS6` calibration matrices due to wiring being mixed up
+- Various smaller bugfixes
+
+# [v1.25.0] 2023-02-06
+
+eive-tmtc version: v2.12.0
+
+## Changed
+
+- Updated Subsystem mode IDs to avoid clashes with regular device handler modes.
+
+## Fixed
+
+- `GpsHyperionLinuxController`: Fix `gpsd` polling by continuously calling `gps_read` in one cycle
+ until it does not have any data left anymore. Also, the data is now polled in a permanent loop,
+ where controller handling is done on 0.2 second timeouts.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/368
+
+# [v1.24.0] 2023-02-03
+
+- eive-tmtc v2.10.0
+- `AcsSubsystem`: OFF, SAFE and DETUMBLE mode were tested. Auto-transitions SAFE <-> DETUMBLE tested
+ as well. Other modes still need to be tested.
+
+## Fixed
+
+- `AcsController`: Parameter fix in `DetumbleParameter`.
+- Set GPS set entries to invalid on MODE_OFF command.
+- Bump FSFW for bugfix in `setNormalDatapoolEntriesInvalid` where the validity was not set to false
+ properly
+- Fixed usage of uint instead of int for commanding MTQ. Also fixed the range in which the ACS Ctrl
+ commands the MTQ to match the actual commanding range.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/363
+- Regression: Revert swap of SUS0 and SUS6. Those devices are on separate power lines. In a
+ future fix, the calibration matrices of SUS0 and SUS6 will be swapped.
+
+## Changed
+
+- Update ACS scheduling to represent the actual ACS design. There is one ACS PST now for all
+ timing sensitive ACS operations. In the debug builds, the new ACS polling sequence table
+ will have a period of 0.6 seconds, but will remain 0.4 seconds for the release build.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/365
+- `ACS::SensorValues` is now an ACS controller member to reduce the risk of stack overflow.
+- ACS Subsystem Sequence Mode IDs updated.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/365
+ TMTC PR: https://egit.irs.uni-stuttgart.de/eive/eive-tmtc/pulls/130
+- Update and tweak ACS subsystem to represent the actual ACS design
+- Event handling in the ACS subsystem for events triggered by the ACS controller.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/365
+
+# [v1.23.1] 2023-02-02
+
+TMTC rev: 15adb9bf2ec68304a4f87b8dd418c1a8353283a3
+
+## Fixed
+
+- Bugfix in FSFW where the sequence flags of the PUS packets were set to continuation segment (0b00)
+ instead of unsegmented (0b11).
+- Bugfix in FSFW where the MGM RM3100 value Z axis data was parse incorrectly.
+ PR: https://egit.irs.uni-stuttgart.de/eive/fsfw/pulls/123
+
+# [v1.23.0] 2023-02-01
+
+TMTC version: v2.9.0
+
+## Changed
+
+- Bumped FSFW to include improvements and bugfix for Health Service. The health service now
+ supports the announce all health info command.
+ PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/725
+
+## Fixed
+
+- Bumped FSFW to include fixes in the time service.
+ PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/726
+- The CCSDS handler starts the transmitter timer each time it is commanded to MODE_ON and times
+ out the timer when the handler is commanded to MODE_OFF
+- If the timer is timed out the CCSDS handler will disable the TX clock which will cause the
+ syrlinks to got to standby mode
+- PDEC handler now parses the FAR register also in interrupt mode
+
+
+# [v1.22.1] 2023-01-30
+
+## Changed
+
+- Updated FSFW to include addition where the `SO_REUSEADDR` option is set
+ on the TCP server, which should improve its ergonomics.
+
+# [v1.22.0] 2023-01-28
+
+TMTC version: v2.6.1
+
+## Added
+
+- First COM subsystem implementation. It mirrors the Syrlinks mode/submodes but also takes
+ care of commanding the CCSDS handler. It expects the Syrlinks submodes as mode commands.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/358
+- The CCSDS handler has has a new submode (3) to configure the default datarate.
+- Default datarate parameter commanding moved to COM subsystem.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/358
+
+# [v1.21.0] 2023-01-26
+
+TMTC version: v2.5.0
+Syrlinks PR: PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/353
+
+## Fixed
+
+- The `OBSW_SYRLINKS_SIMULATED` flag is set to 0 for for both EM and FM.
+- MGM4 handling in ACS sensor processing: Bugfix in `mulScalar` operation
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/354
+- Subsystem ID clash: CORE subsystem ID was the same as Syrlinks subsystem ID.
+
+## Changed
+
+- Startracker temperature set and PCDU switcher set are diagnostic now
+- `SyrlinksHkHandler` renamed to `SyrlinksHandler` to better reflect that it does more than
+ just HK and is also responsible for setting the TX mode of the device.
+- `SyrlinksHandler`: Go to startup immediately because the Syrlinks device should always be on
+ by default.
+- `SyrlinksHandler`: Go to normal mode at startup.
+
+## Added
+
+- The Syrlinks handler has submodes for the TX mode now: RX Only (0), RX and TX default
+ datarate (1), RX and TX Low Rate (2), RX and TX High Rate (3) and TX Carrier Wave (4).
+ The submodes apply for both ON and NORMAL mode. The default datarate can be updated using
+ a parameter command (domain ID 0 and unique ID 0) with value 0 for low rate and 1 for high rate.
+- The Syrlinks handler always sets TX to standby when switching off
+- The Syrlinks handler triggers a new TX_ON event when the transmitter was switched on successfully
+ and a TX_OFF event when it was switched off successfully.
+- Startracker temperature set and PCDU switcher set are diagnostic now
+- The CCSDS handler can accept mode commands now. It accepts ON and OFF commands. Furthermore
+ it has a submode for low datarate (1) and high datarate (2) for the ON command.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/352
+
+# [v1.20.0] 2023-01-24
+
+## Added
+
+- The Q7S SW now checks for a file named `boot_delay_secs.txt` in the home directory.
+ If it exists and the file is empty, it will delay for 6 seconds before continuing
+ with the regular boot. It can also try to read delay seconds from the file.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/340.
+- Basic TCS Subsystem component.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/319
+- Expose base set of STR periodic housekeeping packets
+
+## Changed
+
+- Moved some PDEC/PTME configuration to `common/config/eive/definitions.h`
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/319
+- The ACS Controller Gyro Sets (raw and processed) and the MEKF dataset are diagnostics now.
+- Bumped FSFW for Service 11 improvement which includes size and CRC check for contained TC
+- Syrlinks module now always included for both EM and FM
+- SA Deployment: Allow specifying the switch interval and the initial channel. This allows testing
+ the new deployment procedure where each channel is burned for half of the whole burn duration.
+ It also allows burning only one channel for the whole burn duration. The autonomous mechanism
+ was adapted to burn each channel for half of the burn time by default.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/347
+ TMTC PR: https://egit.irs.uni-stuttgart.de/eive/eive-tmtc/pulls/127
+- `Max31865RtdLowlevelHandler.cpp`: For each RTD device, the config is now re-written before
+ every read. This seems to fix some issue with invalid temperature sensor readings.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/345
+
+## Fixed
+
+- `GyroADIS1650XHandler`: Updated handler to determine correct dynamic range from `RANG_MDL`
+ register readout. This is because ADIS16505-3BMLZ devices are used on the ACS board and the
+ previous range setting was wrong. Also fixed a small error properly set internal state
+ on shut-down.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/342
+- Syrlinks Handler: Read RX frequency shift as 24 bit signed number now. Also include
+ validity handling for datasets.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/350
+- `GyroADIS1650XHandler`: Changed calculation of angular rate to be sensitivity based instead of
+ max. range based, as previous fix still left an margin of error between ADIS16505 sensors
+ and L3GD20 sensors.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/346
+
+# [v1.19.0] 2023-01-10
+
+## Changed
+
+- 5V stack is now off by default
+
+## Fixed
+
+- PLOC SUPV: Minor adaptions and important bugfix for UART manager
+- Allow cloning and building the hosted OBSW version without proprietary libraries,
+ which also avoids the need to have a Gitea account.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/337
+
+## Added
+
+- First version of ACS controller
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/329
+- Allow commanding the 5V stack internally in software
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/334
+- Add automatic 5V stack commanding for all connected devices
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/335
+
+# [v1.18.0] 2022-12-01
+
+## Changed
+
+- PLOC Supervisor: Changes baudrate to 921600
+- Renamed `/dev/ul-plsv` to `/dev/ploc_supv`, is not a UART lite anymore
+- Renamed `/dev/i2c_eive` to `/dev/i2c_pl` and `/dev/i2c-2` to `/dev/i2c_ps`.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/328
+
+# [v1.17.0] 2022-11-28
+
+## Added
+
+- PLOC Supervisor Update: Update SW to use newest PLOC SUPV version by TAS
+ PR 1: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/316
+ PR 2: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/324
+ PR 3: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/326
+
+# [v1.16.0] 2022-11-18
+
+- It is now possible to compile Linux components for the hosted build conditionally
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/322
+- ACS Subsystem. PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/231
+- Payload Subsystem. PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/231
+- Add IRQ mode for PDEC handler. PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/310
+- Extended TM funnels to allow multiple TM recipients.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/312
+- DHB: Transitions to normal mode now possible directly, which simplifies subsystem implementations
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/313
+- MAX3185 Low Level Handler and Device Handler: Simplifications and bugfixes to allow switching
+ off without triggering unrequested replies
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/313
+- Add remaining missing TMP1075 device handlers.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/318
+
+# [v1.15.0] 2022-10-27
+
+- Consistent device file naming
+- Remove rad sensor from EM build, lead to weird bugs on EM which
+ prevented `xsc_boot_copy` from working properly
+- CFDP closure handling is now working
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/307
+- Safety mechanism for SD card handling on graceful reboots
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/308
+- Solar Array Deployment handler update
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/305
+- IMTQ updates as preparation for ACS controller expansion
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/306
+- P60 Module: Reduce number of set IDs, use same set IDs for core, auxiliary
+ and config HK set across the three PCDU modules
+
+# [v1.14.1] 11.10.2022
+
+- Various bugfixes and regression fixes
+- General file handling at program initialization now works properly again
+- Scratch buffer preferred SD card handling works again
+- Use scoped locks in TCS controller to avoid deadlocks
+
+# [v1.14.0] 10.10.2022
+
+- Provide full SW update capability for the OBSW.
+ This includes very basic CFDP integration, a software update
+ procedure specified in detail in the README and some high level
+ commands to make this easier for operators.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/302
+- Update for FSFW: `HasReturnvaluesIF` class replaced by namespace `returnvalue`
+- Add some GomSpace clients as a submodule dependency. Use this dependency to deserialize the
+ GomSpace TM tables
+- Add API to retrieve GomSpace device parameter tables
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/287
+- Add API to save and load GomSpace config tables
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/293
+- Increase number of allowed consescutive action commands from 3 to 16
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/294
+- Fix for EM SW: Always create ACS Task
+- Added Scex device handler and Scex uart reader
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/303
+- ACS Subsystem. PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/228
+
+# [v1.13.0] 24.08.2022
+
+- Added first version of ACS Controller with gathers MGM data in a set
+- Some tweaks for IMTQ handler
+
+# [v1.12.1] 05.07.2022
+
+- Disable periodic TCS controller HK generation by default
+
+# [v1.12.0] 04.07.2022
+
+## Added
+
+- Dummy components to run OBSW without relying on external hardware
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/266
+- Basic Thermal Controller
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/266
+- PUS11 TC scheduler
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/259
+- Regular reboot command
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/242
+- Commands for individual RTD devices
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-tmtc/pulls/84
+- `RwAssembly` added to system components. Assembly works in principle,
+ issues making 4 consecutives RWs communicate at once..
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/224
+- Adds a yocto helper script which is able to install the release build binaries
+ (OBSW and Watchdog) into the `q7s-yocto` repository as long as the `q7s-package`
+ or `q7s-yocto` repo was cloned in the same directory the EIVE OBSW repo.
+ This makes updating the root filesystem a lot easier. It also creates and installs a
+ version file.
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/248
+- Create the generic image by default for the Q7S build. The unique binary with the
+ username appended at the end is created as a side-product now
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/248
+
+## Fixed
+
+- `q7s-cp.py` bugfix
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/256
+- Generator scripts output now produce platform-independent artifacts
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/267
+
+### Heater
+
+- Adds `HealthIF` to heaters. Heaters are own system object with queues now which allows to set them faulty.
+- SW will attempt to shut down heaters which are on but marked faulty
+- Some simplifications for `HeaterHandler`, use `std::vector` instead of `std::unordered_map` for primary container. Using the heater indexes 0 to 7 allows to use natural array indexing
+- Some additional input sanity checks in `executeAction`
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/236
+
+## Changed
+
+- CCSDS handler improvements
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/268
+- Build unittest as default side product of hosted builds
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/244
+- Let CI/CD build host build and run unittest side product in same step
+- Catch2 pre-installed in CI/CD docker container, Xiphos SDK installed in CI/CD docker
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/247
+- Sun Sensors have names denoting their location and poiting in the satellite now
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/245
+- Better RTD names denoting their purpose (and location consequently)
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/246
+
+# [v1.11.0]
+
+## Fixed
+
+- Host build working again
+
+## Added
+
+- Custom Syrlinks FDIR which disabled most of the default FDIR functionality
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/232
+- Custom Gomspace FDIR which disabled most of the default FDIR functionality
+- Custom Syrlinks FDIR which disabled most of the default FDIR functionality
+
+## Changed
+
+- PCDU handler only called once in PST, but can handle multiple messages now
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/221
+ Bugfix: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/235
+- Update rootfs base of Linux, all related OBSW changes
+- Add `/usr/local/bin` to PATH. All shell scripts are there now
+- Add Syrlinks and TMP devices to Software by default
+- Update GPS Linux Hyperion Handler to use socket interface. Still allows switching
+ back to SHM interface, but the SHM interface is a possible cause of SW crashes
+- Updated code for changed FSFW HAL GPIO API: `readGpio` prototype has changed
+ PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/240 and
+ https://egit.irs.uni-stuttgart.de/eive/fsfw/pulls/76
+
+### GPS
+
+PRs: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/239
+
+- Rename GPS device to `/dev/gps0`
+- Use gpsd version 3.17 now. Includes API changes
+
+### EM and FM splitup & Build Workflow improvements
+
+PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/238
+
+- Split up `bsp_q7s` in separate EM and FM build with module loading set to different
+ default values. The EM object factory is unique which allows building a parallel setup
+ with dummy components
+- All major BSPs have an own `OBSWConfig.h.in` file which simplifies the file significantly
+- Renamed Q7S primary build folders:
+ - `cmake-build-debug-q7s` for primary development build
+ - `cmake-build-release-q7s` for primary release build
+ - `cmake-build-debug-q7s-em` for primary development build of the EM software
+ - `cmake-build-release-q7s-em` for primary release build of the EM software
+- Refactored Q7S helper script handling. It is now intended and preferred to copy the environment
+ script to the same folder level as the `eive-obsw` and source it. This will also
+ add the path containing the shell helper scripts to `PATH`
+- The actual helper shell scripts were renamed as well to `q7s--.sh`
+
+# [v1.10.1]
+
+Version bump
+
+# [v1.10.0]
+
+For all releases equal or prior to v1.10.0,
+see [milestones](https://egit.irs.uni-stuttgart.de/eive/eive-obsw/milestones)
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..2914b88
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,581 @@
+# ##############################################################################
+# CMake support for the EIVE OBSW
+#
+# Author: R. Mueller
+# ##############################################################################
+
+# ##############################################################################
+# Pre-Project preparation
+# ##############################################################################
+cmake_minimum_required(VERSION 3.13)
+
+set(OBSW_VERSION_MAJOR 8)
+set(OBSW_VERSION_MINOR 2)
+set(OBSW_VERSION_REVISION 1)
+
+# set(CMAKE_VERBOSE TRUE)
+
+option(
+ EIVE_HARDCODED_TOOLCHAIN_FILE
+ "\
+For Linux Board Target BSPs, a default toolchain file will be set. Should be set to OFF \
+if a different toolchain file is set externally"
+ ON)
+
+if(NOT FSFW_OSAL)
+ set(FSFW_OSAL
+ linux
+ CACHE STRING "OS for the FSFW.")
+endif()
+
+if(TGT_BSP)
+ if(TGT_BSP MATCHES "arm/q7s"
+ OR TGT_BSP MATCHES "arm/raspberrypi"
+ OR TGT_BSP MATCHES "arm/beagleboneblack")
+ option(LINUX_CROSS_COMPILE ON)
+ endif()
+ if(TGT_BSP MATCHES "arm/raspberrypi" OR TGT_BSP MATCHES "arm/beagleboneblack")
+ option(EIVE_BUILD_GPSD_GPS_HANDLER "Build GPSD dependent GPS Handler" OFF)
+ elseif(TGT_BSP MATCHES "arm/q7s")
+ option(EIVE_Q7S_EM "Build configuration for the EM" OFF)
+ option(EIVE_BUILD_GPSD_GPS_HANDLER "Build GPSD dependent GPS Handler" ON)
+ endif()
+ option(EIVE_CREATE_UNIQUE_OBSW_BIN "Append username to generated binary name"
+ ON)
+else()
+ option(EIVE_CREATE_UNIQUE_OBSW_BIN "Append username to generated binary name"
+ OFF)
+endif()
+
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+# Perform steps like loading toolchain files where applicable.
+include(PreProjectConfig)
+pre_project_config()
+
+# Project Name
+project(eive-obsw)
+
+# Specify the C++ standard
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+include(EiveHelpers)
+
+option(EIVE_ADD_ETL_LIB "Add ETL library" ON)
+option(EIVE_ADD_JSON_LIB "Add JSON library" ON)
+
+set(OBSW_MAX_SCHEDULED_TCS 4000)
+
+if(EIVE_Q7S_EM)
+ set(OBSW_Q7S_EM
+ 1
+ CACHE STRING "Q7S EM configuration")
+ set(OBSW_Q7S_FM 0)
+ set(OBSW_STAR_TRACKER_GROUND_CONFIG 1)
+else()
+ set(OBSW_Q7S_EM
+ 0
+ CACHE STRING "Q7S EM configuration")
+ set(OBSW_Q7S_FM 1)
+ set(OBSW_STAR_TRACKER_GROUND_CONFIG 0)
+endif()
+
+set(OBSW_ADD_TMTC_TCP_SERVER
+ ${OBSW_Q7S_EM}
+ CACHE STRING "Add TCP TMTC Server")
+set(OBSW_ADD_TMTC_UDP_SERVER
+ 0
+ CACHE STRING "Add UDP TMTC Server")
+set(OBSW_ADD_MGT
+ ${OBSW_Q7S_FM}
+ CACHE STRING "Add MGT module")
+set(OBSW_ADD_BPX_BATTERY_HANDLER
+ ${OBSW_Q7S_FM}
+ CACHE STRING "Add BPX battery module")
+set(OBSW_ADD_STAR_TRACKER
+ 1
+ CACHE STRING "Add Startracker module")
+set(OBSW_ADD_SUN_SENSORS
+ ${OBSW_Q7S_FM}
+ CACHE STRING "Add sun sensor module")
+set(OBSW_ADD_SUS_BOARD_ASS
+ ${OBSW_Q7S_FM}
+ CACHE STRING "Add sun sensor board assembly")
+set(OBSW_ADD_THERMAL_TEMP_INSERTER
+ ${OBSW_Q7S_EM}
+ CACHE STRING "Add thermal sensor temperature inserter")
+set(OBSW_ADD_ACS_BOARD
+ 1
+ CACHE STRING "Add ACS board module")
+set(OBSW_ADD_GPS_CTRL
+ ${OBSW_Q7S_FM}
+ CACHE STRING "Add GPS controllers")
+set(OBSW_ADD_CCSDS_IP_CORES
+ 1
+ CACHE STRING "Add CCSDS IP cores")
+set(OBSW_TM_TO_PTME
+ 1
+ CACHE STRING "Send telemetry to PTME IP core")
+set(OBSW_TC_FROM_PDEC
+ 1
+ CACHE STRING "Poll telecommand from PDEC IP core")
+set(OBSW_ADD_TCS_CTRL
+ 1
+ CACHE STRING "Add TCS controllers")
+set(OBSW_ADD_HEATERS
+ 1
+ CACHE STRING "Add TCS heaters")
+set(OBSW_ADD_PLOC_SUPERVISOR
+ 1
+ CACHE STRING "Add PLOC supervisor handler")
+set(OBSW_ADD_SA_DEPL
+ ${OBSW_Q7S_FM}
+ CACHE STRING "Add SA deployment handler")
+set(OBSW_ADD_PLOC_MPSOC
+ 1
+ CACHE STRING "Add MPSoC handler")
+set(OBSW_ADD_ACS_CTRL
+ ${OBSW_Q7S_FM}
+ CACHE STRING "Add ACS controller")
+set(OBSW_ADD_RTD_DEVICES
+ ${OBSW_Q7S_FM}
+ CACHE STRING "Add RTD devices")
+set(OBSW_ADD_RAD_SENSORS
+ ${OBSW_Q7S_FM}
+ CACHE STRING "Add Rad Sensor module")
+set(OBSW_ADD_PL_PCDU
+ 1
+ CACHE STRING "Add Payload PCDU modukle")
+set(OBSW_ADD_SYRLINKS
+ 1
+ CACHE STRING "Add Syrlinks module")
+set(OBSW_ADD_TMP_DEVICES
+ 1
+ CACHE STRING "Add TMP devices")
+set(OBSW_ADD_GOMSPACE_PCDU
+ 1
+ CACHE STRING "Add GomSpace PCDU modules")
+set(OBSW_ADD_GOMSPACE_ACU
+ ${OBSW_Q7S_FM}
+ CACHE STRING "Add GomSpace ACU submodule")
+set(OBSW_ADD_RW
+ ${OBSW_Q7S_FM}
+ CACHE STRING "Add RW modules")
+set(OBSW_ADD_SCEX_DEVICE
+ 1
+ CACHE STRING "Add Solar Cell Experiment module")
+set(OBSW_SYRLINKS_SIMULATED
+ 0
+ CACHE STRING "Syrlinks is simulated")
+
+# ##############################################################################
+# Pre-Sources preparation
+# ##############################################################################
+
+# Version handling
+set(GIT_VER_HANDLING_OK FALSE)
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git)
+ determine_version_with_git("--exclude" "docker_*")
+ set(GIT_INFO
+ ${GIT_INFO}
+ CACHE STRING "Version information retrieved with git describe")
+ if(GIT_INFO)
+ set(GIT_INFO
+ ${GIT_INFO}
+ CACHE STRING "Version information retrieved with git describe")
+ # CMakeLists.txt is now single source of information. list(GET GIT_INFO 1
+ # OBSW_VERSION_MAJOR) list(GET GIT_INFO 2 OBSW_VERSION_MINOR) list(GET
+ # GIT_INFO 3 OBSW_VERSION_REVISION)
+ list(LENGTH GIT_INFO LIST_LEN)
+ if(LIST_LEN GREATER 4)
+ list(GET GIT_INFO 4 OBSW_VERSION_CST_GIT_SHA1)
+ endif()
+ set(GIT_VER_HANDLING_OK TRUE)
+ else()
+ set(GIT_VER_HANDLING_OK FALSE)
+ endif()
+endif()
+
+# Set names and variables
+set(OBSW_NAME ${CMAKE_PROJECT_NAME})
+set(WATCHDOG_NAME eive-watchdog)
+set(SIMPLE_OBSW_NAME eive-simple)
+set(UNITTEST_NAME eive-unittest)
+set(LIB_FSFW_NAME fsfw)
+set(LIB_EIVE_MISSION eive-mission)
+set(LIB_ETL_TARGET etl::etl)
+set(LIB_CSP_NAME libcsp)
+set(LIB_LWGPS_NAME lwgps)
+set(LIB_ARCSEC wire)
+set(LIB_GOMSPACE_CLIENTS gs_clients)
+set(LIB_GOMSPACE_CSP gs_csp)
+
+set(THIRD_PARTY_FOLDER thirdparty)
+set(LIB_CXX_FS -lstdc++fs)
+set(LIB_CATCH2 Catch2)
+set(LIB_GPS gps)
+set(LIB_JSON_NAME nlohmann_json::nlohmann_json)
+set(LIB_DUMMIES dummies)
+
+# Set path names
+set(FSFW_PATH fsfw)
+set(TEST_PATH test)
+set(UNITTEST_PATH unittest)
+set(LINUX_PATH linux)
+set(LIB_GOMSPACE_PATH ${THIRD_PARTY_FOLDER}/gomspace-sw)
+set(COMMON_PATH common)
+set(DUMMY_PATH dummies)
+set(WATCHDOG_PATH watchdog)
+set(COMMON_CONFIG_PATH ${COMMON_PATH}/config)
+set(UNITTEST_CFG_PATH ${UNITTEST_PATH}/testcfg)
+
+set(LIB_EIVE_MISSION_PATH mission)
+set(LIB_ETL_PATH ${THIRD_PARTY_FOLDER}/etl)
+set(LIB_CATCH2_PATH ${THIRD_PARTY_FOLDER}/Catch2)
+set(LIB_LWGPS_PATH ${THIRD_PARTY_FOLDER}/lwgps)
+set(LIB_ARCSEC_PATH ${THIRD_PARTY_FOLDER}/sagittactl)
+set(LIB_JSON_PATH ${THIRD_PARTY_FOLDER}/json)
+
+set(FSFW_WARNING_SHADOW_LOCAL_GCC OFF)
+set(EIVE_ADD_LINUX_FILES OFF)
+set(FSFW_ADD_TMSTORAGE ON)
+
+set(FSFW_ADD_COORDINATES ON)
+set(FSFW_ADD_SGP4_PROPAGATOR ON)
+
+# Analyse different OS and architecture/target options, determine BSP_PATH,
+# display information about compiler etc.
+pre_source_hw_os_config()
+
+if(TGT_BSP)
+ set(LIBGPS_VERSION_MAJOR 3)
+ # I assume a newer version than 3.17 will be installed on other Linux board
+ # than the Q7S
+ set(LIBGPS_VERSION_MINOR 20)
+ if(TGT_BSP MATCHES "arm/q7s"
+ OR TGT_BSP MATCHES "arm/raspberrypi"
+ OR TGT_BSP MATCHES "arm/beagleboneblack"
+ OR TGT_BSP MATCHES "arm/egse"
+ OR TGT_BSP MATCHES "arm/te0720-1cfa")
+ find_library(${LIB_GPS} gps)
+ set(FSFW_CONFIG_PATH "linux/fsfwconfig")
+ if(NOT BUILD_Q7S_SIMPLE_MODE)
+ set(EIVE_ADD_LINUX_FILES TRUE)
+ set(EIVE_ADD_LINUX_FSFWCONFIG TRUE)
+ set(ADD_GOMSPACE_CSP TRUE)
+ set(ADD_GOMSPACE_CLIENTS TRUE)
+ set(FSFW_HAL_ADD_LINUX ON)
+ set(FSFW_HAL_LINUX_ADD_LIBGPIOD ON)
+ set(FSFW_HAL_LINUX_ADD_PERIPHERAL_DRIVERS ON)
+ endif()
+ elseif(UNIX)
+ set(EIVE_ADD_LINUX_FILES ON)
+ endif()
+
+ if(TGT_BSP MATCHES "arm/raspberrypi")
+ # Used by configure file
+ set(RASPBERRY_PI ON)
+ set(FSFW_HAL_ADD_RASPBERRY_PI ON)
+ endif()
+
+ if(TGT_BSP MATCHES "arm/egse")
+ # Used by configure file
+ set(EGSE ON)
+ set(FSFW_HAL_LINUX_ADD_LIBGPIOD OFF)
+ set(OBSW_ADD_STAR_TRACKER 1)
+ set(OBSW_DEBUG_STARTRACKER 1)
+ endif()
+
+ if(TGT_BSP MATCHES "arm/beagleboneblack")
+ # Used by configure file
+ set(BEAGLEBONEBLACK ON)
+ endif()
+
+ if(TGT_BSP MATCHES "arm/q7s")
+ # Used by configure file
+ set(XIPHOS_Q7S ON)
+ set(LIBGPS_VERSION_MAJOR 3)
+ set(LIBGPS_VERSION_MINOR 17)
+ endif()
+
+ if(TGT_BSP MATCHES "arm/te0720-1cfa")
+ set(TE0720_1CFA ON)
+ endif()
+else()
+ # Required by FSFW library
+ set(FSFW_CONFIG_PATH "${BSP_PATH}/fsfwconfig")
+ if(UNIX)
+ set(EIVE_ADD_LINUX_FILES ON)
+ endif()
+endif()
+
+include(BuildType)
+set_build_type()
+
+set(FSFW_DEBUG_INFO 0)
+set(OBSW_ENABLE_PERIODIC_HK 1)
+set(Q7S_CHECK_FOR_ALREADY_RUNNING_IMG 0)
+if(RELEASE_BUILD MATCHES 0)
+ set(FSFW_DEBUG_INFO 1)
+ set(OBSW_ENABLE_PERIODIC_HK 0)
+ set(Q7S_CHECK_FOR_ALREADY_RUNNING_IMG 1)
+endif()
+
+# Configuration files
+configure_file(${COMMON_CONFIG_PATH}/commonConfig.h.in commonConfig.h)
+configure_file(${FSFW_CONFIG_PATH}/FSFWConfig.h.in FSFWConfig.h)
+configure_file(${BSP_PATH}/OBSWConfig.h.in OBSWConfig.h)
+if(TGT_BSP MATCHES "arm/q7s")
+ configure_file(${BSP_PATH}/boardconfig/q7sConfig.h.in q7sConfig.h)
+elseif(TGT_BSP MATCHES "arm/raspberrypi" OR TGT_BSP MATCHES "arm/egse")
+ configure_file(${BSP_PATH}/boardconfig/rpiConfig.h.in rpiConfig.h)
+endif()
+
+configure_file(${WATCHDOG_PATH}/watchdogConf.h.in watchdogConf.h)
+
+# Set common config path for FSFW
+set(FSFW_ADDITIONAL_INC_PATHS "${COMMON_PATH}/config"
+ ${CMAKE_CURRENT_BINARY_DIR})
+
+# ##############################################################################
+# Executable and Sources
+# ##############################################################################
+
+# global compiler options need to be set before adding executables
+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+ # Remove unused sections.
+ add_compile_options("-ffunction-sections" "-fdata-sections")
+
+ # Removed unused sections.
+ add_link_options("-Wl,--gc-sections")
+
+elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
+ set(COMPILER_FLAGS "/permissive-")
+endif()
+
+add_library(${LIB_EIVE_MISSION})
+add_library(${LIB_DUMMIES})
+
+# Add main executable
+add_executable(${OBSW_NAME})
+set(OBSW_BIN_NAME ${CMAKE_PROJECT_NAME})
+
+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+ set(WARNING_FLAGS
+ "-Wall"
+ "-Wextra"
+ "-Wimplicit-fallthrough=1"
+ "-Wno-unused-parameter"
+ "-Wno-psabi"
+ "-Wshadow=local"
+ "-Wduplicated-cond" # check for duplicate conditions
+ "-Wduplicated-branches" # check for duplicate branches
+ "-Wlogical-op" # Search for bitwise operations instead of logical
+ "-Wnull-dereference" # Search for NULL dereference
+ "-Wundef" # Warn if undefind marcos are used
+ "-Wformat=2" # Format string problem detection
+ "-Wformat-overflow=2" # Formatting issues in printf
+ "-Wformat-truncation=2" # Formatting issues in printf
+ "-Wformat-security" # Search for dangerous printf operations
+ "-Wstrict-overflow=3" # Warn if integer overflows might happen
+ "-Warray-bounds=2" # Some array bounds violations will be found
+ "-Wshift-overflow=2" # Search for bit left shift overflows (
+
+ EIVE On-Board Software
+======
+
+# Index
+
+1. [General](#general)
+2. [Prerequisites](#prereq)
+3. [Building the Software](#build)
+4. [Useful and Common Host Commands](#host-commands)
+5. [Setting up Prerequisites](#set-up-prereq)
+6. [Remote Debugging](#remote-debugging)
+6. [Remote Reset](#remote-reset)
+8. [TMTC testing](#tmtc-testing)
+9. [Direct Debugging](#direct-debugging)
+10. [Transfering Files to the Q7S](#file-transfer)
+11. [Q7S OBC](#q7s)
+12. [Static Code Analysis](#static-code-analysis)
+13. [Eclipse](#eclipse)
+14. [CLion](#clion)
+14. [Running the OBSW on a Raspberry Pi](#rpi)
+15. [Running OBSW on EGSE](#egse)
+16. [Manually preparing sysroots to compile gpsd](#gpsd)
+17. [FSFW](#fsfw)
+18. [Coding Style](#coding-style)
+
+# General information
+
+Target systems:
+
+* OBC with Linux OS
+ * Xiphos Q7S
+ * Based on Zynq-7020 SoC (xc7z020clg484-2)
+ * Dual-core ARM Cortex-A9
+ * 766 MHz
+ * Artix-7 FPGA (85K pogrammable logic cells)
+ * Datasheet at https://eive-cloud.irs.uni-stuttgart.de/index.php/apps/files/?dir=/EIVE_IRS/Arbeitsdaten/08_Used%20Components/Q7S&fileid=340648
+ * Also a lot of information about the Q7S can be found on
+ the [Xiphos Traq Platform](https://trac2.xiphos.ca/eive-q7). Press on index to find all
+ relevant pages. The most recent datasheet can be found
+ [here](https://trac2.xiphos.ca/manual/wiki/Q7RevB/UserManual).
+ * Linux OS built with Yocto 2.5. SDK and root filesystem can be rebuilt with
+ [yocto](https://egit.irs.uni-stuttgart.de/eive/q7s-yocto)
+ * [Linux Kernel](https://github.com/XiphosSystemsCorp/linux-xlnx.git) . EIVE version can be found
+ [here](https://github.com/spacefisch/linux-xlnx) . Pre-compiled files can be
+ found [here](https://eive-cloud.irs.uni-stuttgart.de/index.php/apps/files/?dir=/EIVE_IRS/Software/q7s-linux-components&fileid=777299).
+ * Q7S base project can be found [here](https://egit.irs.uni-stuttgart.de/eive/q7s-base)
+ * Minimal base project files and Xiphos SDK can be found
+ [here](https://eive-cloud.irs.uni-stuttgart.de/index.php/apps/files/?dir=/EIVE_IRS/Software/xiphos-q7s-sdk&fileid=510908)
+* Host System
+ * Generic software components which are not dependant on hardware can also
+ be run on a host system. All host code is contained in the `bsp_hosted` folder
+ * Tested for Linux (Ubuntu 20.04) and Windows 10
+* Raspberry Pi
+ * EIVE OBC can be built for Raspberry Pi as well (either directly on Raspberry Pi or by installing a cross compiler)
+
+The steps in the primary README are related to the main OBC target Q7S.
+The CMake build system can be used to generate build systems as well (see helper scripts in `cmake/scripts`:
+
+- Linux Raspberry Pi: See special section below. Uses the `bsp_linux_board` folder
+- Linux Trenz TE7020_1CFA: Uses the `bsp_te0720_1cfa` folder
+- Linux Host: Uses the `bsp_hosted` BSP folder and the CMake Unix Makefiles generator.
+- Windows Host: Uses the `bsp_hosted` BSP folder, the CMake MinGW Makefiles generator and MSYS2.
+
+# Prerequisites
+
+There is a separate [prerequisites](#set-up-prereq) which specifies how to set up all
+prerequisites.
+
+## Building the OBSW and flashing it on the Q7S
+
+1. ARM cross-compiler installed, either as part of [Vivado 2018.2 installation](#vivado) or
+ as a [separate download](#arm-toolchain). The Xiphos SDK also installs a cross-compiler,
+ but its version is currently too old to compile the OBSW (7.3.0).
+2. [Q7S sysroot](#sysroot) on local development machine. It is installed by the Xiphos SDK
+3. Recommended: Eclipse or [Vivado 2018.2 SDK](#vivado) for OBSW development
+3. [TCF agent](https://wiki.eclipse.org/TCF) running on Q7S
+
+## Hardware Design
+
+1. [Vivado 2018.2](#vivado) for programmable logic design
+
+# Building the software
+
+## CMake
+
+When using Windows, run theses steps in MSYS2.
+
+1. Clone the repository with
+
+ ```sh
+ git clone https://egit.irs.uni-stuttgart.de/eive/eive-obsw.git
+ ```
+
+2. Update all the submodules
+
+ ```sh
+ git submodule update --init
+ ```
+
+3. Create two system variables to pass the system root path and the cross-compiler path to the
+ build system. You only need to do this once when setting up the build system.
+ Example for Unix:
+
+ ```sh
+ export CROSS_COMPILE_BIN_PATH=
+ export ZYNQ_7020_SYSROOT=
+ ```
+
+4. Ensure that the cross-compiler is working with
+ `${CROSS_COMPILE_BIN_PATH}/arm-linux-gnueabihf-gcc --version` and that
+ the sysroot environmental variables have been set like specified in the
+ [root filesystem chapter](#sysroot).
+
+5. Run the CMake configuration to create the build system in a `build-Debug-Q7S` folder.
+ Add `-G "MinGW Makefiles` in MinGW64 on Windows.
+
+ ```sh
+ mkdir cmake-build-debug-q7s && cd cmake-build-debug-q7s
+ cmake -DTGT_BSP="arm/q7s" -DCMAKE_BUILD_TYPE=Debug ..
+ cmake --build . -j
+ ```
+
+ Please note that you can also use provided shell scripts to perform these commands.
+ ```sh
+ cp scripts/q7s-env.sh ..
+ cp scripts/q7s-env-em.sh ..
+ ```
+
+ Adapt these scripts for your needs by editing the `CROSS_COMPILE_BIN_PATH`
+ and `ZYNQ_7020_SYSROOT`. After that, you can run the following commands to set up
+ the FM build
+
+ ```sh
+ cd ..
+ ./q7s-env.sh
+ q7s-make-debug.sh
+ ```
+
+ You can build the EM setup by running
+
+ ```sh
+ export EIVE_Q7S_EM=1
+ ```
+
+ or by running the `q7s-env-em.sh` script instead before setting up the build
+ configuration.
+
+ The shell scripts will invoke a Python script which in turn invokes CMake with the correct
+ arguments to configure CMake for Q7S cross-compilation. You can look into the command
+ output to see which commands were run exactly.
+
+ There are also different values for `-DTGT_BSP` to build for the Raspberry Pi
+ or the Beagle Bone Black: `arm/raspberrypi` and `arm/beagleboneblack`.
+
+6. Build the software with
+
+ ```sh
+ cd cmake-build-debug-q7s
+ cmake --build . -j
+ ```
+
+## Preparing and executing an OBSW update
+
+A OBSW update consists of a `xz` compressed file `eive-sw-update.tar.xz`
+which contains the following two files:
+
+1. Stripped OBSW binary `eive-obsw-stripped`
+2. OBSW version text file with the name `obsw_version.txt`
+
+These files can be created manually:
+
+1. Build the release image inside `cmake-build-release-q7s`
+2. Switch into the build directory
+3. Run the following command to create the version file
+
+ ```sh
+ git describe --tags --always --exclude docker_* > obsw_version.txt
+ ```
+
+ You can also use the `create-version-file.sh` helper shell script
+ located in the `scripts` folder to do this.
+
+4. Set the Q7S user as the file owner for both files
+
+ ```sh
+ sudo chown root:root eive-obsw-stripped
+ sudo chown root:root obsw_version.txt
+ ```
+
+5. Run the following command to create the compressed archive
+
+ ```sh
+ tar -cJvf eive-sw-update.tar.xz eive-obsw-stripped obsw_version.txt
+ ```
+
+You can also use the helper script `create-sw-update.sh` inside the build folder
+after sourcing the `q7s-env.sh` helper script to perform all steps including
+a rebuild.
+
+After creating these files, they need to be transferred onto the Q7S
+to either the `/mnt/sd0/bin` or `/mnt/sd1/bin` folder if the OBSW update
+is performed from the SD card. It can also be transferred to the `/tmp` folder
+to perform the update from a temporary directory, which does not rely on any
+of the SD cards being on and mounted. However, all files in the temporary
+directory will be deleted if the Linux OS is rebooted for any reason.
+
+After both files are in place (this is checked by the OBSW), the example command
+sequence is used by the OBSW to write the OBSW update to the QSPI chip 0 and
+slot 0 using SD card 0:
+
+```sh
+tar -xJvf eive-update.tar.xz
+xsc_mount_copy 0 0
+cp eive-obsw-stripped /tmp/mntupdate-xdi-qspi0-nom-rootfs/usr/bin/eive-obsw
+cp obsw_update.txt /tmp/mntupdate-xdi-qspi0-nom-rootfs/usr/share/obsw_update.txt
+writeprotect 0 0 1
+```
+
+Some context information about the used commands:
+
+1. It mounts the target chip and copy combination into the `/tmp` folder
+ using the `xsc_mount_copy ` utility. This also unlocks the
+ writeprotection for the chip. The mount point name inside `/tmp` depends
+ on which chip and copy is used
+
+ - Chip 0 Copy 0: `/tmp/mntupdate-xdi-qspi0-nom-rootfs`
+ - Chip 0 Copy 1: `/tmp/mntupdate-xdi-qspi0-gold-rootfs`
+ - Slot 1 Copy 0: `/tmp/mntupdate-xdi-qspi1-nom-rootfs`
+ - Slot 1 Copy 1: `/tmp/mntupdate-xdi-qspi1-gold-rootfs`
+
+2. Writing the file with a regular `cp ` command
+3. Enabling the writeprotection using the `writeprotect 1` utility.
+
+## Build for the Q7S target root filesystem with `yocto`
+
+The EIVE root filesystem will contain the EIVE OBSW and the Watchdog component.
+It is currently generated with `yocto`, but the tool can not compile the primary
+OBSW due to toolchain version incompatibility. Therefore, the OBSW components
+are currently compiled using the toolchain specified in this README (e.g. installed by Vivado).
+
+However, it is still possible to install the two components using yocto. A few helper files were
+provided to make this process easier. The following steps can be used to install the OBSW
+components and a version file to the yocto sources for the generation of the complete EIVE root
+file system image. The steps here are shown for Ubuntu, you can use the according Windows
+helper scripts as well.
+
+1. Copy the `q7s-env.sh` script to the same layer as the `eive-obsw`.
+
+ ```sh
+ cp scripts/q7s-env.sh ..
+ cd ..
+ ./q7s-env.sh
+ q7s-make-release.sh
+ ```
+
+2. Compile the OBSW components in release mode
+
+ ```sh
+ cd cmake-build-release-q7s
+ cmake --build . -j
+ ```
+
+3. Make sure the [`q7s-yocto`](https://egit.irs.uni-stuttgart.de/eive/q7s-yocto)
+ repository or the [`q7s-package`](https://egit.irs.uni-stuttgart.de/eive/q7s-package.git)
+ repository and its `q7s-yocto` submodule were cloned in the same directory layer as
+ the `eive-obsw`.
+
+4. Run the install script to install the files into `q7s-yocto`.
+
+ ```sh
+ install-obsw-yocto.sh
+ ```
+
+5. Navigate into the `q7s-yocto` repo and review the changes. You can then add and push those
+ changes.
+
+6. You can now rebuild the root filesystem with the updated OBSW using `yocto`. This probably needs
+ to be done on another machine or in a VM. The [`q7s-yocto`](https://egit.irs.uni-stuttgart.de/eive/q7s-yocto)
+ repository contains details on how to best do this.
+
+## Building in Xilinx SDK 2018.2
+
+1. Open Xilinx SDK 2018.2
+2. Import project
+ * File → Import → C/C++ → Existing Code as Makefile Project
+3. Set build command. Replace \ with either debug or release.
+ * When on Linux right click project → Properties → C/C++ Build → Set build command to `make -j`
+ * -j causes the compiler to use all available cores
+ * The target is used to either compile the debug or the optimized release build.
+ * On windows create a make target additionally (Windows → Show View → Make Target)
+ * Right click eive_obsw → New
+ * Target name: all
+ * Uncheck "Same as the target name"
+ * Uncheck "Use builder settings"
+ * As build command type: `cmake --build .`
+ * In the Behaviour tab, you can enable build acceleration
+4. Run build command by double clicking the created target or by right clicking
+ the project folder and selecting Build Project.
+
+# Useful and Common Commands
+
+## Build generation
+
+Replace `Debug` with `Release` for release build. Add `-G "MinGW Makefiles` or `-G "Ninja"`
+on Windows or when `ninja` should be used. You can build with `cmake --build . -j` after
+build generation. You can finds scripts in `cmake/scripts` to perform the build commands
+automatically.
+
+### Q7S OBSW
+
+The EIVE OBSW is the default target if no target is specified.
+
+**Debug**
+
+```sh
+mkdir cmake-build-debug-q7s && cd cmake-build-debug-q7s
+cmake -DTGT_BSP=arm/q7s -DCMAKE_BUILD_TYPE=Debug ..
+cmake --build . -j
+```
+
+**Release**
+
+```sh
+mkdir cmake-build-release-q7s && cd cmake-build-release-q7s
+cmake -DTGT_BSP=arm/q7s -DCMAKE_BUILD_TYPE=Release ..
+cmake --build . -j
+```
+
+### Hosted OBSW
+
+You can also use the FSFW OSAL `host` to build on Windows or for generic OSes.
+You can use the `clone-submodules-no-privlibs.sh` script to only clone the required (non-private)
+submodules required to build the hosted OBSW.
+
+```sh
+mkdir cmake-build-debug && cd cmake-build-debug
+cmake -DFSFW_OSAL=host -DCMAKE_BUILD_TYPE=Debug ..
+cmake --build . -j
+```
+
+You can also use the `linux` OSAL:
+
+```sh
+mkdir cmake-build-debug && cd cmake-build-debug
+cmake -DFSFW_OSAL=linux -DCMAKE_BUILD_TYPE=Debug ..
+cmake --build . -j
+```
+
+Please note that some additional Linux setup might be necessary.
+You can find more information in the [Linux section of the FSFW example](https://egit.irs.uni-stuttgart.de/fsfw/fsfw-example-linux-mcu/src/branch/mueller/master/doc/README-linux.md#raising-message-queue-size-limit)
+
+### Q7S Watchdog
+
+The watchdog will be built along side the primary OBSW binary.
+
+### Unittests
+
+To build the unittests, the corresponding target must be specified in the build command.
+The configure steps do not need to be repeated if the folder has already been configured.
+
+```sh
+mkdir cmake-build-debug && cd cmake-build-debug
+cmake ..
+cmake --build . --target eive-unittests -j
+```
+
+## Connect to EIVE flatsat
+
+### DNS
+
+```sh
+ssh eive@flatsat.eive.absatvirt.lw
+```
+
+### IPv6
+```sh
+ssh eive@2001:7c0:2018:1099:babe:0:e1fe:f1a5
+```
+
+### IPv4
+
+```sh
+ssh eive@192.168.199.227
+```
+
+## Connecting to the serial console or ssh console
+
+A serial console session is up permanently in a `tmux` session
+
+### Serial console
+
+You can check whether the sessions exist with `tmux ls`.
+This is the command to connect to the serial interface of the FM using the
+RS422 interface of the flight preparation panel:
+
+```sh
+tmux a -t q7s-fm-fpp
+```
+
+If the session does not exist, you can create it like this
+
+```sh
+tmux new -s q7s-fm-fpp -t /bin/bash
+launch-q7s-fpp
+```
+
+Other useful tmux commands:
+- Enable scroll mode: You can press `ctrl + b` and then `[` (`AltGr + 8`) to enable scroll mode.
+ You can quit scroll mode with `q`.
+- Kill a tmux session: run `ctrl + b` and then `k`.
+- Detach from a tmux session: run `ctrl + b` and then `d`
+- [Pipe last 3000 lines](https://unix.stackexchange.com/questions/26548/write-all-tmux-scrollback-to-a-file)
+ into file for copying or analysis:
+ 1. `ctrl + b` and `:`
+ 2. `capture-pane -S -3000` + `enter`
+ 3. `save-buffer /tmp/tmux-output.txt` + `enter`
+
+
+### SSH console
+
+You can use the following command to connect to the Q7S with `ssh`:
+
+```sh
+q7s-fm-ssh
+```
+
+## Set up all port forwarding at once
+
+Port forwarding is necessary for remote-debugging using the `tcf-agent`, copying files
+with `scp` & `q7s-cp.py` and sending TMTC commands.
+You can specify the `-L` option multiple times to set up all port forwarding at once.
+
+```sh
+ssh -L 1534:192.168.155.55:1534 \
+ -L 1535:192.168.155.55:22 \
+ -L 1536:192.168.155.55:7301 \
+ -L 1537:127.0.0.1:7100 \
+ -L 1538:192.168.133.10:1534 \
+ -L 1539:192.168.133.10:22 \
+ -L 1540:192.168.133.10:7301 \
+ eive@2001:7c0:2018:1099:babe:0:e1fe:f1a5 \
+ -t 'CONSOLE_PREFIX="[Q7S Tunnel]" /bin/bash'
+```
+
+There is also a shell script called `q7s-port.sh` which can be used to achieve the same.
+
+# Setting up prerequisites
+
+## Getting system root for Linux cross-compilation
+
+Cross-compiling any program for an embedded Linux board generally required parts of the target root
+file system on the development/host computer. For the Q7S, you can install the cross-compilation
+root file system by simply installing the SDK. You can find the most recent SDK
+[here](https://eive-cloud.irs.uni-stuttgart.de/index.php/apps/files/?dir=/EIVE_IRS/Software/xiphos-q7s-sdk).
+
+If you are compiling for the Q7S or the TE7020, the `ZYNQ_7020_SYSROOT` environment variable
+must be set to the location of the SDK compile sysroot. Here is an example on how to do this
+in Ubuntu, assuming the SDK was installed in the default location
+
+```sh
+export ZYNQ_7020_SYSROOT="/opt/xiphos/sdk/ark/sysroots/cortexa9hf-neon-xiphos-linux-gnueabi"
+```
+
+If you are comiling for the Raspberry Pi, you have to set the `LINUX_ROOTFS` environmental
+variable instead. You can find a base root filesystem for the Raspberry Pi
+[here](https://eive-cloud.irs.uni-stuttgart.de/index.php/apps/files/?dir=/EIVE_IRS/Software/rootfs).
+
+## Installing Vivado and the Xilinx development tools
+
+It's also possible to perform debugging with a normal Eclipse installation by installing
+the TCF plugin and downloading the cross-compiler as specified in the section below. However,
+if you want to generate the `*.xdi` files necessary to update the firmware, you need to
+installed Vivado with the SDK core tools.
+
+* Install Vivado 2018.2 and
+ [Xilinx SDK](https://www.xilinx.com/support/download/index.html/content/xilinx/en/downloadNav/vivado-design-tools/archive.html).
+ Install the Vivado Design Suite - HLx Editions - 2018.2 Full Product Installation instead of
+ the updates. It is recommended to use the installer.
+
+* Install settings. In the Devices selection, it is sufficient to pick SoC → Zynq-7000:
+
+
+
+
+
+
+
+* For supported OS refer to https://www.xilinx.com/support/documentation/sw_manuals/xilinx2018_2/ug973-vivado-release-notes-install-license.pdf .
+ Installation was tested on Windows and Ubuntu 21.04.
+* Add path of linux cross-compiler to permanent environment variables (`.bashrc` file in Linux):
+ `\SDK\2018.2\gnu\aarch32\nt\gcc-arm-linux-gnueabi\bin`
+ or set up path each time before debugging.
+
+### Installing on Linux - Device List Issue
+
+When installing on Ubuntu, the installer might get stuck at the `Generating installed device list`
+step. When this happens, you can kill the installation process (might be necessara to kill a process
+twice) and generate this list manually with the following commands, according to
+[this forum entry](https://forums.xilinx.com/t5/Installation-and-Licensing/Vivado-2018-3-Final-Processing-hangs-at-Generating-installed/m-p/972114#M25861).
+
+1. Install the following library
+ ```sh
+ sudo apt install libncurses5
+ ```
+
+2. Execute the following command
+
+ ```sh
+ sudo /Vivado/2018.2/bin/vivado -nolog -nojournal -mode batch -source
+ /.xinstall/Vivado_2018.2/scripts/xlpartinfo.tcl -tclargs
+ /Vivado/2018.2/data/parts/installed_devices.txt
+ ```
+
+For Linux, you can also download a more recent version of the
+[Linaro 8.3.0 cross-compiler](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-a/downloads)
+from [here](https://developer.arm.com/-/media/Files/downloads/gnu-a/8.3-2019.03/binrel/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf.tar.xz?revision=e09a1c45-0ed3-4a8e-b06b-db3978fd8d56&la=en&hash=93ED4444B8B3A812B893373B490B90BBB28FD2E3)
+
+### Compatibility issues with wayland on more recent Linux distributions
+
+If Vivado crashes and you find following lines in the `hs_err_pid*` files:
+
+```sh
+#
+# An unexpected error has occurred (11)
+#
+Stack:
+/opt/Xilinx/Vivado/2017.4/tps/lnx64/jre/lib/amd64/server/libjvm.so(+0x923da9) [0x7f666cf5eda9]
+/opt/Xilinx/Vivado/2017.4/tps/lnx64/jre/lib/amd64/server/libjvm.so(JVM_handle_linux_signal+0xb6) [0x7f666cf653f6]
+/opt/Xilinx/Vivado/2017.4/tps/lnx64/jre/lib/amd64/server/libjvm.so(+0x9209d3) [0x7f666cf5b9d3]
+/lib/x86_64-linux-gnu/libc.so.6(+0x35fc0) [0x7f66a993efc0]
+/opt/Xilinx/Vivado/2017.4/tps/lnx64/jre/lib/amd64/libawt_xawt.so(+0x42028) [0x7f664e24d028]
+...
+```
+
+You can [solve this](https://forums.xilinx.com/t5/Design-Entry/Bug-Vivado-2017-4-crashing-on-rightclick-in-console-log/td-p/881811)
+by logging in with `xorg` like specified [here](https://www.maketecheasier.com/switch-xorg-wayland-ubuntu1710/).
+
+### Using `docnav` on more recent Linux versions
+
+If you want to use `docnav` for navigating Xilinx documentation, it is recommended to install
+it as a standalone version from [here](https://www.xilinx.com/support/download/index.html/content/xilinx/en/downloadNav/documentation-nav.html).
+This is because the `docnav` installed as part of version 2018.2 requires `libpng12`, which is not part of
+more recent disitributions anymore.
+
+## Installing toolchain without Vivado
+
+You can download the toolchains for Windows and Linux
+[from the EIVE cloud](https://eive-cloud.irs.uni-stuttgart.de/index.php/apps/files/?dir=/EIVE_IRS/Software/tools).
+
+## Installing CMake and MSYS2 on Windows
+
+1. Install [MSYS2](https://www.msys2.org/) and [CMake](https://cmake.org/download/) first.
+
+2. Open the MinGW64 console. It is recommended to set up aliases in `.bashrc` to navigate to the
+ software repository quickly
+
+3. Run the following commands in MinGW64
+
+ ```sh
+ pacman -Syu
+ ```
+
+ It is recommended to install the full base development toolchain
+
+ ```sh
+ pacman -S base-devel
+ ```
+
+ It is also possible to only install required packages
+
+ ```sh
+ pacman -S mingw-w64-x86_64-cmake mingw-w64-x86_64-make mingw-w64-x86_64-gcc mingw-w64-x86_64-gdb python3
+ ```
+
+## Installing CMake on Linux
+
+1. Run the following command
+
+ ```sh
+ sudo apt-get install cmake
+ ````
+
+### Updating system root for CI
+
+If the system root is updated, it needs to be manually updated on the buggy file server.
+If access on `buggy.irs.uni-stuttgart.de` is possible with `ssh` and the rootfs in the cloud
+[was updated](https://eive-cloud.irs.uni-stuttgart.de/index.php/apps/files/?dir=/EIVE_IRS/Software/rootfs&fileid=831849)
+as well, you can update the rootfs like this:
+
+```sh
+cd /var/www/eive/tools
+wget https://eive-cloud.irs.uni-stuttgart.de/index.php/s/SyXpdBBQX32xPgE/download/cortexa9hf-neon-xiphos-linux-gnueabi.tar.gz
+```
+
+## Setting up UNIX environment for real-time functionalities
+
+Please note that on most UNIX environments (e.g. Ubuntu), the real time functionalities
+used by the UNIX pthread module are restricted, which will lead to permission errors when creating
+these tasks and configuring real-time properites like scheduling priorities.
+
+To solve this issues, try following steps:
+
+1. Edit the /etc/security/limits.conf
+file and add following lines at the end:
+```sh
+ hard rtprio 99
+ soft rtprio 99
+```
+The soft limit can also be set in the console with `ulimit -Sr` if the hard
+limit has been increased, but it is recommended to add it to the file as well for convenience.
+If adding the second line is not desired for security reasons,
+the soft limit needs to be set for each session. If using an IDE like eclipse
+in that case, the IDE needs to be started from the console after setting
+the soft limit higher there. After adding the two lines to the file,
+the computer needs to be restarted.
+
+It is also recommended to perform the following change so that the unlockRealtime
+script does not need to be run anymore each time. The following steps
+raise the maximum allowed message queue length to a higher number permanently, which is
+required for some framework components. The recommended values for the new message
+length is 130.
+
+2. Edit the /etc/sysctl.conf file
+
+ ```sh
+ sudo nano /etc/sysctl.conf
+ ```
+
+ Append at end:
+ ```sh
+ fs/mqueue/msg_max =
+ ```
+
+ Apply changes with:
+ ```sh
+ sudo sysctl -p
+ ```
+
+ A possible solution which only persists for the current session is
+ ```sh
+ echo | sudo tee /proc/sys/fs/mqueue/msg_max
+ ```
+
+## TCF-Agent
+
+Most of the steps specified here were already automated
+
+1. On reboot, some steps have to be taken on the Q7S. Set static IP address and netmask
+
+ ```sh
+ ifconfig eth0 192.168.133.10
+ ifconfig eth0 netmask 255.255.255.0
+ ```
+
+2. `tcfagent` application should run automatically but this can be checked with
+ ```sh
+ systemctl status tcfagent
+ ```
+
+3. If the agent is not running, check whether `agent` is located inside `usr/bin`.
+ You can run it manually there. To perform auto-start on boot, have a look at the start-up
+ application section.
+
+# Remote Debugging
+
+Open SSH connection to flatsat PC:
+
+```sh
+ssh eive@flatsat.eive.absatvirt.lw
+```
+
+or
+
+```sh
+ssh eive@2001:7c0:2018:1099:babe:0:e1fe:f1a5
+```
+
+or
+
+```sh
+ssh eive@192.168.199.227
+```
+
+If the static IP address of the Q7S has already been set,
+you can access it with ssh
+
+```sh
+ssh root@192.168.133.10
+```
+
+If this has not been done yet, you can access the serial
+console of the Q7S like this
+
+```sh
+picocom -b 115200 /dev/q7sSerial
+```
+
+The flatsat has the aliases and shell scripts `q7s_ssh` and `q7s_serial` for this task as well.
+If the serial port is blocked for some reason, you can kill
+the process using it with `q7s_kill`.
+
+You can use `AltGr` + `X` to exit the picocom session.
+
+To debug an application, first make sure a static IP address is assigned to the Q7S. Run ifconfig
+on the Q7S serial console.
+
+```sh
+ifconfig
+```
+
+Set IP address and netmask with
+
+```sh
+ifconfig eth0 192.168.133.10
+ifconfig eth0 netmask 255.255.255.0
+```
+
+To launch application from Xilinx SDK setup port fowarding on the development machine
+(not on the flatsat!)
+
+```sh
+ssh -L 1534:192.168.133.10:1534 eive@2001:7c0:2018:1099:babe:0:e1fe:f1a5 -t bash
+```
+
+This forwards any requests to localhost:1534 to the port 1534 of the Q7S with the IP address
+192.168.133.10. This needs to be done every time, so it is recommended to create an
+alias or shell script to do this quickly.
+
+Note: When now setting up a debug session in the Xilinx SDK or Eclipse, the host must be set
+to localhost instead of the IP address of the Q7S.
+
+# Remote Reset
+1. Launch xilinx hardware server on flatsat with alias
+````
+launch-hwserver-xilinx
+````
+2. On host PC start xsc
+3. In xsct console type the follwing command to connect to the hardware server (replace with the IP address of the flatsat PC. Can be found out with ifconfig)
+````
+connect -url tcp::3121
+````
+4. The following command will list all available devices
+````
+targets
+````
+5. Connect to the APU of the Q7S
+````
+target
+````
+6. Perform reset
+````
+rst
+````
+
+# TMTC testing
+
+The OBSW supports sending PUS TM packets via TCP or the PDEC IP Core which transmits the data as
+CADU frames. To make the CADU frames receivabel by the
+[TMTC porgram](https://egit.irs.uni-stuttgart.de/eive/eive-tmtc), a python script is running as
+`systemd` service named `tmtc_bridge` on the flatsat PC which forwards TCP commands to the TCP
+server of the OBC and reads CADU frames from a serial interface.
+
+You can check whether the service is running the following command on the flatsat PC
+
+```sh
+systemctl status tmtc_bridge
+```
+
+The PUS packets transported with the CADU frames are extracted
+and forwared to the TMTC program's TCP client. The code of the TMTC bridge can be found
+[here](https://egit.irs.uni-stuttgart.de/eive/tmtc-bridge). To connect the TMTC program to the
+TMTC-bridge a port forwarding from a host must be set up with the following command:
+
+```sh
+ssh -L 1537:127.0.0.1:7100 eive@2001:7c0:2018:1099:babe:0:e1fe:f1a5 -t bash
+```
+
+You can print the output of the `systemd` service with
+
+```sh
+journalctl -u tmtc_bridge
+```
+
+This can be helpful to determine whether any TCs arrive or TMs are coming back.
+
+Note: The encoding of the TM packets and conversion of CADU frames takes some time.
+Thus the replies are received with a larger delay compared to a direct TCP connection.
+
+# Direct Debugging
+
+1. Assign static IP address to Q7S
+ * Open serial console of Q7S (Accessible via the micro-USB of the PIM, see also Q7S user
+ manual chapter 10.3)
+ * Baudrate 115200
+ * Login to Q7S:
+ * user: root
+ * pw: root
+
+2. Connect Q7S to workstation via ethernet
+3. Make sure the netmask of the ehternet interface of the workstation matches the netmask of the Q7S
+ * When IP address is set to 192.168.133.10 and the netmask is 255.255.255.0, an example IP address for the workstation
+ is 192.168.133.2
+4. Make sure th `tcf-agent` is running by checking `systemctl status tcf-agent`
+5. In Xilinx SDK 2018.2 right click on project → Debug As → Debug Configurations
+6. Right click Xilinx C/C++ applicaton (System Debugger) → New →
+7. Set Debug Type to Linux Application Debug and Connectin to Linux Agent
+8. Click New
+9. Give connection a name
+10. Set Host to static IP address of Q7S. e.g. 192.168.133.10
+11. Test connection (This ensures the TCF Agent is running on the Q7S)
+12. Select Application tab
+ * Project Name: eive_obsw
+ * Local File Path: Path to OBSW application image with debug symbols (non-stripped)
+ * Remote File Path: `/tmp/`
+
+# Transfering Files to the Q7S
+
+To transfer files from the local machine to the Q7S, use port forwarding
+
+```sh
+ssh -L 1535:192.168.133.10:22 eive@2001:7c0:2018:1099:babe:0:e1fe:f1a5
+```
+
+An `example` file can be copied like this
+
+```sh
+scp -P 1535 example root@localhost:/tmp
+```
+
+Copying a file from Q7S to flatsat PC
+````
+scp -P 22 root@192.168.133.10:/tmp/kernel-config /tmp
+````
+
+From a windows machine files can be copied with putty tools (note: use IPv4 address)
+````
+pscp -scp -P 22 eive@192.168.199.227:/example-file
+````
+
+A helper script named `q7s-cp.py` can be used together with the `q7s-port.sh`
+script to make this process easier.
+
+# Q7S OBC
+
+## Launching an application at start-up - deprecated
+
+This way to enable auto-startup is deprecated. It is instead recommended to tweak the yocto
+recipes file for the related `systemd` service to enable auto-startup with `SYSTEMD_AUTO_ENABLE`.
+
+You can also do the steps performed here on a host computer inside the `q7s-rootfs` directory
+of the [Q7S base repository](https://egit.irs.uni-stuttgart.de/eive/q7s-base). This might
+be more convenient while also allowing to update all images at once with the finished `rootfs.xdi`.
+
+Load the root partiton from the flash memory (there are to nor-flash memories and each flash holds
+two xdi images). Note: It is not possible to modify the currently loaded root partition, e.g.
+creating directories. To do this, the parition needs to be mounted.
+
+1. Disable write protection of the desired root partition
+
+ ```sh
+ writeprotect 0 0 0 # unlocks nominal image on nor-flash 0
+ ```
+
+2. Mount the root partition
+
+ ```sh
+ xsc_mount_copy 0 0 # Mounts the nominal image from nor-flash 0
+ ```
+ The mounted partition will be located inside the `/tmp` folder
+
+3. Copy the executable to `/usr/bin`
+
+4. Make sure the permissions to execute the application are set
+
+ ```sh
+ chmod +x application
+ ```
+
+5. Create systemd service in `/etc/systemd/system`. The following shows an example service.
+
+ ```sh
+ cat > example.service
+ [Unit]
+ Description=Example Service
+ StartLimitIntervalSec=0
+
+ [Service]
+ Type=simple
+ Restart=always
+ RestartSec=1
+ User=root
+ ExecStart=/usr/bin/application
+
+ [Install]
+ WantedBy=multi-user.target
+ ```
+
+6. Enable the service. This is normally done with `systemctl enable ` which would create
+ a symlink in the `multi-user.target.wants` directory. However, this is not possible
+ when the service is created for a mounted root partition. It is also not possible during run
+ time because symlinks can't be created in a read-only filesystem. Therefore, relative symlinks
+ are used like this:
+
+ ```sh
+ cd etc/systemd/system/multi-user.target.wants/
+ ln -s ../example.service example.service
+ ```
+
+ You can check the symlinnks with `ls -l`
+
+7. The modified root partition is written back when the partion is locked again.
+ ```sh
+ writeprotect 0 0 1
+ ```
+8. Now verify the application start by booting from the modified image
+ ```sh
+ xsc_boot_copy 0 0
+ ````
+
+9. After booting verify if the service is running
+ ```sh
+ systemctl status example
+ ```
+
+## Current user systemd services
+
+The following custom `systemd` services are currently running on the Q7S and can be found in
+the `/etc/systemd/system` folder.
+You can query that status of a service by running `systemctl status `.
+
+### `eive-watchdog`
+
+The watchdog will create a pipe at `/tmp/watchdog-pipe` which can be used both by the watchdog and
+the EIVE OBSW. The watchdog will only read from this pipe while the OBSW will only write
+to this pipe. The watchdog checks for basic ASCII commands as a first basic feature set.
+The most important functionality is that the watchdog cant detect if a timeout
+has happened. This can happen beause the OBSW is hanging (or at least the CoreController thread) or
+there is simply now OBSW running on the system. It does to by checking whether the FIFO is
+regulary written to, which means the EIVE OBSW is alive.
+
+If the EIVE OBSW is alive, a special file called `/tmp/obsw-running` will be created.
+This file can be used by any other software component to query whether the EIVE OBSW is running.
+The EIVE OBSW itself can be configured to check whether this file exists, which prevents two
+EIVE OBSW instances running on the Q7S at once.
+
+If a timeout occurs, this special file will be deleted as well.
+The watchdog and its configuration will be directly integrated into this repostory, which
+makes adaptions easy.
+
+### `tcf-agent`
+
+This starts the `/usr/bin/tcf-agent` program to allows remote debugging
+
+### `eive-early-config`
+
+This is a configuration script which runs early after `local-fs.target` and `sysinit.target`
+Currently only pipes the output of `xsc_boot_copy` into the file `/tmp/curr_copy.txt` which can be
+used by other software components to read the current chip and copy.
+
+### `eive-post-ntpd-config`
+
+This is a configuration scripts which runs after the Network Time Protocol has run. This script
+currently sets the static IP address `192.168.133.10` and starts the `can` interface.
+
+## Initial boot delay
+
+You can create a file named `boot_delays_secs.txt` inside the home folder to delay the OBSW boot
+for 6 seconds if the file is empty of for the number of seconds specified inside the file. This
+can be helpful if something inside the OBSW leads to an immediate crash of the OBC.
+
+## PCDU
+
+Connect to serial console of P60 Dock
+````
+picocom -b 500000 /dev/ttyUSBx
+````
+General information
+````
+cmp ident
+````
+List parameter table:
+x values: 1,2 or 4
+````
+param list x
+````
+Table 4 lists HK parameters
+Changing parameters
+First switch to table where parameter shall be changed (here table id is 1)
+````
+p60-dock # param mem 1
+p60-dock # param set out_en[0] 1
+p60-dock # param get out_en[0]
+GET out_en[0] = 1
+````
+
+## Core commands
+
+Display currently running image:
+
+```sh
+xsc_boot_copy
+```
+
+Rebooting currently running image:
+
+```sh
+xsc_boot_copy -r
+```
+
+### Setting time on Q7S
+Setting date and time (only timezone UTC available)
+````
+timedatectl set-time 'YYYY-MM-DD HH:MM:SS'
+````
+Setting UNIX time
+````
+date +%s -s @1626337522
+````
+This only sets the system time and does not updating the time of the real time clock. To harmonize
+the system time with the real time clock run
+````
+hwclock -w
+````
+Reading the real time clock
+````
+hwclock --show
+````
+
+## pa3tool Host Tool
+
+The `pa3tool` is a host tool to interface with the ProASIC3 on the Q7S board. It was
+installed on the clean room PC but it can also be found
+[on the Traq platform](https://trac2.xiphos.ca/manual/attachment/wiki/WikiStart/libpa3-1.3.4.tar.gz).
+
+For more information, see Q7S datasheet.
+
+## Creating files with cat and echo
+
+The only folder which can be written in the root filesystem is the `tmp` folder.
+
+You can create a simple file with initial content with `echo`
+
+```sh
+echo "Hallo Welt" > /tmp/test.txt
+cat /tmp/test.txt
+```
+
+For more useful combinations, see this [link](https://www.freecodecamp.org/news/the-cat-command-in-linux-how-to-create-a-text-file-with-cat-or-touch/).
+
+## Using the scratch buffer of the ProASIC3
+
+The ProASIC3 has a 1024 byte scratch buffer. The values in this scratch buffer will survive
+a reboot, so this buffer can be used as an alternative to the SD cards to exchange information
+between images or to store mission critical information.
+
+You can use `xsc_scratch --help` for more information.
+
+Write to scratch buffer:
+
+```sh
+xsc_scratch write TEST "1"
+```
+
+Read from scratch buffer:
+
+```sh
+xsc_scratch read TEST
+```
+
+Read all keys:
+
+```sh
+xsc_scratch read
+
+```
+
+Get fill count:
+
+```sh
+xsc_scratch read | wc -c
+```
+
+## Custom device names in Linux with the `udev` module
+
+You can assign custom device names using the Linux `udev` system.
+This works by specifying a rules file inside the `/etc/udev/rules.d` folder
+which creates a SYMLINK if certain device properties are true.
+
+Each rule is a new line inside a rules file.
+For example, the rule
+
+```txt
+SUBSYSTEM=="tty", ATTRS{interface}=="Dual RS232-HS", ATTRS{bInterfaceNumber}=="01", SYMLINK+="ploc_supv
+```
+
+Will create a symlink `/dev/ploc_supv` if a connected USB device has the
+same `interface` and `bInterfaceNumber` properties as shown above.
+
+You can list the properties for a given connected device using `udevadm`.
+For example, you can do this for a connected example device `/dev/ttyUSB0`
+by using
+
+```txt
+udevadm info -a /dev/ttyUSB0
+```
+
+## Using `system` when debugging
+
+Please note that when using a `system` call in C++/C code and debugging, a new thread will be
+spawned which will appear on the left in Eclipse or Xilinx SDK as a `sh` program.
+The debugger might attach to this child process automatically, depending on debugger configuration,
+and the process needs to be selected and continued/started manually. You can enable or disable
+this behaviour by selecting or deselecting the `Attach Process Children` option in the Remote
+Application Configuration for the TCF plugin like shown in the following picture
+
+
+
+## Libgpiod
+
+Detect all gpio device files:
+````
+gpiodetect
+````
+Get info about a specific gpio group:
+````
+gpioinfo
+````
+The following sets the gpio 18 from gpio group gpiochip7 to high level.
+````
+gpioset gpiochip7 18=1
+````
+Setting the gpio to low.
+````
+gpioset gpiochip7 18=0
+````
+Show options for setting gpios.
+````
+gpioset -h
+````
+To get the state of a gpio:
+````
+gpioget
+````
+Example to get state:
+gpioget gpiochip7 14
+
+Both the MIOs and EMIOs can be accessed via the zynq_gpio instance which
+comprises 118 pins (54 MIOs and 64 EMIOs).
+
+## Xilinx UARTLIE
+
+Get info about ttyUL* devices
+````
+cat /proc/tty/driver
+````
+
+## I2C
+
+Getting information about some I2C device
+
+```sh
+ls /sys/class/i2c-dev/i2c-0/device/device/driver
+```
+This shows the memory mapping of `/dev/i2c-0`.
+
+You can use the `i2cdetect` utility to scan for I2C devices.
+For example, to do this for bus 0 (`/dev/i2c-0`), you can use
+
+```sh
+i2cdetect -r -y 0
+```
+
+## CAN
+
+```sh
+ip link set can0 down
+ip link set can0 type can loopback off
+ip link set can0 up type can bitrate 1000000
+```
+
+Following command sends 8 bytes to device with id 99 (for petalinux)
+````
+cansend can0 -i99 99 88 77 11 33 11 22 99
+````
+For Q7S use this:
+````
+cansend can0 5A1#11.22.33.44.55.66.77.88
+````
+Turn loopback mode on:
+````
+ip link set can0 type can bitrate 1000000 loopback on
+````
+Reading data from CAN:
+````
+candump can0
+````
+
+## Dump content of file in hex
+````
+cat file.bin | hexdump -C
+````
+All content will be printed with
+````
+cat file.bin | hexdump -v
+````
+To print only the first X bytes of a file
+````
+cat file.bin | hexdump -v -n X
+````
+
+## Preparation of a fresh rootfs and SD card
+
+See [q7s-package repository README](https://egit.irs.uni-stuttgart.de/eive/q7s-package)
+
+# Running cppcheck on the Software
+
+Static code analysis can be useful to find bugs.
+`cppcheck` can be used for this purpose. On Windows you can use MinGW64 to do this.
+
+```sh
+pacman -S mingw-w64-x86_64-cppcheck
+```
+
+On Ubuntu, install with
+
+```sh
+sudo apt-get install cppcheck
+```
+
+You can use the Eclipse integration or you can perform the scanning manually from the command line.
+CMake will be used for this.
+
+Run the CMake build generation commands specified above but supply
+`-DCMAKE_EXPORT_COMPILE_COMMANDS=ON` to the build generation. Invoking the build command will
+generate a `compile_commands.json` file which can be used by cppcheck.
+
+```sh
+cppcheck --project=compile_commands.json --xml 2> report.xml
+```
+
+Finally, you can convert the generated `.xml` file to HTML with the following command
+
+```sh
+cppcheck-htmlreport --file=report.xml --report-dir=cppcheck --source-dir=..
+```
+
+# CLion
+
+CLion is the recommended IDE for the development of the hosted version of EIVE.
+You can also set up CLion for cross-compilation of the primary OBSW.
+
+There is a shared `.idea/cmake.xml` file to get started with this.
+To make cross-compilation work, two special environment variables
+need to be set:
+
+- `ZYNQ_7020_ROOTFS` pointing to the root filesystem
+- `CROSS_COMPILE` pointing to the the full path of the cross-compiler
+ without the specific tool suffix. For example, if the the cross-compiler
+ tools are located at `/opt/q7s-gcc/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin`,
+ this variable would be set
+ to `/opt/q7s-gcc/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin/arm-linux-gnueabihf`
+
+# Eclipse
+
+When using Eclipse, there are two special build variables in the project properties
+→ C/C++ Build → Build Variables called `Q7S_SYSROOT` or `RPI_SYSROOT`. You can set
+the sysroot path in those variables to get any additional includes like `gpiod.h` in the
+Eclipse indexer.
+
+## Setting up default Eclipse for Q7S projects - TCF agent
+
+The [TCF agent](https://wiki.eclipse.org/TCF) can be used to perform remote debugging on the Q7S.
+
+1. Copy the `.cproject` file and the `.project` file inside the `misc/eclipse` folder into the
+ repo root
+
+ ```sh
+ cd eive-obsw
+ cp misc/eclipse/.cproject .
+ cp misc/eclipse/.project .
+ ```
+
+2. Open the repo in Eclipse as a folder.
+
+3. Install the TCF agent plugin in Eclipse from
+ the [releases](https://www.eclipse.org/tcf/downloads.php). Go to
+ Help → Install New Software and use the download page, for
+ example https://download.eclipse.org/tools/tcf/releases/1.7/1.7.0/ to search for the plugin and
+ install it. You can find the newest version [here](https://www.eclipse.org/tcf/downloads.php)
+
+4. Go to Window → Perspective → Open Perspective and open the **Target Explorer Perspective**.
+ Here, the Q7S should show up if the local port forwarding was set up as explained previously.
+ Please note that you have to connect to `localhost` and port `1534` with port forwaring set up.
+
+5. A launch configuration was provided, but it might be necessary to adapt it for your own needs.
+ Alternatively:
+
+ - Create a new **TCF Remote Application** by pressing the cogs button at the top or going to
+ Run → Debug Configurations → Remote Application and creating a new one there.
+
+ - Set up the correct image in the main tab (it might be necessary to send the image to the
+ Q7S manually once) and file transfer properties
+
+ - It is also recommended to link the correct Eclipse project.
+
+After that, comfortable remote debugging should be possible with the Debug button.
+
+A build configuration and a shell helper script has been provided to set up the path variables and
+build the Q7S binary on Windows, but a launch configuration needs to be newly created because the
+IP address and path settings differ from machine to machine.
+
+# Running the EIVE OBSW on a Raspberry Pi
+
+Special section for running the EIVE OBSW on the Raspberry Pi.
+The Raspberry Pi build uses the `bsp_rpi` BSP folder, and a very similar cross-compiler.
+
+For running the software on a Raspberry Pi, it is recommended to follow the steps specified in
+[the fsfw example](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_example/src/branch/mueller/master/doc/README-rpi.md#top)
+and using the TCF agent to have a similar set-up process also required for the Q7S.
+You should run the following command first on your Raspberry Pi
+
+```sh
+sudo apt-get install gpiod libgpiod-dev
+```
+
+to install the required GPIO libraries before cloning the system root folder.
+
+# Running OBSW on EGSE
+The EGSE is a test system from arcsec build arround a raspberry pi 4 to test the star tracker. The IP address of the EGSE (raspberry pi) is 192.168.18.31. An ssh session can be opened with
+````
+ssh pi@192.168.18.31
+````
+Password: raspberry
+
+To run the obsw perform the following steps:
+1. Build the cmake EGSE Configuration
+ * the sysroots for the EGSE can be found [here](https://eive-cloud.irs.uni-stuttgart.de/index.php/apps/files/?dir=/EIVE_IRS/Software/egse&fileid=1190471)
+ * toolchain for linux host can be downloaded from [here](https://github.com/Pro/raspi-toolchain)
+ * toolchain for windows host from [here](https://gnutoolchains.com/raspberry/) (the raspios-buster-armhf toolchain is the right one for the EGSE)
+2. Disable the ser2net systemd service on the EGSE
+````sh
+$ sudo systemctl stop ser2net.service
+````
+3. Power on the star tracker by running
+````sh
+$ ~/powerctrl/enable0.sh`
+````
+4. Run portforwarding script for tmtc tcp connection and tcf agent on host PC
+````sh
+$ ./scripts/egse-port.sh
+````
+5. The star tracker can be powered off by running
+````sh
+$ ~/powerctrl/disable0.sh
+````
+
+# Manually preparing sysroots to compile gpsd
+Copy all header files from [here](https://eive-cloud.irs.uni-stuttgart.de/index.php/apps/files/?dir=/EIVE_IRS/Software/gpsd&fileid=1189985) to the /usr/include directory and all static libraries to /usr/lib.
+
+# Flight Software Framework (FSFW)
+
+An EIVE fork of the FSFW is submodules into this repository.
+To add the master upstream branch and merge changes and updates from it
+into the fork, run the following command in the fsfw folder first:
+
+```sh
+git remote add upstream https://egit.irs.uni-stuttgart.de/fsfw/fsfw.git
+git remote update --prune
+```
+
+After that, an update can be merged by running
+
+```sh
+git merge upstream/master
+```
+
+Alternatively, changes from other upstreams (forks) and branches can be merged like that
+in the same way.
+
+# Coding Style
+
+* the formatting is based on the clang-format tools
+
+## Setting up auto-formatter with clang-format in Xilinx SDK
+
+1. Help → Install New Software → Add
+2. In location insert the link http://www.cppstyle.com/luna
+3. The software package CppStyle should now be available for installation
+4. On windows download the clang-formatting tools from https://llvm.org/builds/. On linux clang-format can be installed with the package manager.
+5. Navigate to Preferences → C/C++ → CppStyle
+6. Insert the path to the clang-format executable
+7. Under C/C++ → Code Style → Formatter, change the formatter to CppStyle (clang-format)
+8. Code can now be formatted with the clang tool by using the key combination Ctrl + Shift + f
+
+## Setting up auto-fromatter with clang-format in eclipse
+1. Help → Eclipse market place → Search for "Cppstyle" and install
+2. On windows download the clang-formatting tools from https://llvm.org/builds/. On linux clang-format can be installed with the package manager.
+3. Navigate to Preferences → C/C++ → CppStyle
+4. Insert the path to the clang-format executable
+5. Under C/C++ → Code Style → Formatter, change the formatter to CppStyle (clang-format)
+6. Code can now be formatted with the clang tool by using the key combination Ctrl + Shift + f
diff --git a/archive/PlocMpsocHandler.cpp b/archive/PlocMpsocHandler.cpp
new file mode 100644
index 0000000..ecb0bed
--- /dev/null
+++ b/archive/PlocMpsocHandler.cpp
@@ -0,0 +1,1559 @@
+#include
+#include
+#include
+
+#include "OBSWConfig.h"
+#include "fsfw/datapool/PoolReadGuard.h"
+#include "fsfw/globalfunctions/CRC.h"
+#include "fsfw/ipc/QueueFactory.h"
+#include "fsfw/parameters/HasParametersIF.h"
+
+PlocMpsocHandler::PlocMpsocHandler(object_id_t objectId, object_id_t uartComIFid,
+ CookieIF* comCookie,
+ PlocMpsocSpecialComHelperLegacy* plocMPSoCHelper,
+ Gpio uartIsolatorSwitch, object_id_t supervisorHandler)
+ : DeviceHandlerBase(objectId, uartComIFid, comCookie),
+ hkReport(this),
+ specialComHelper(plocMPSoCHelper),
+ uartIsolatorSwitch(uartIsolatorSwitch),
+ supervisorHandler(supervisorHandler),
+ commandActionHelper(this) {
+ if (comCookie == nullptr) {
+ sif::error << "PlocMPSoCHandler: Invalid communication cookie" << std::endl;
+ }
+ eventQueue = QueueFactory::instance()->createMessageQueue(10);
+ commandActionHelperQueue = QueueFactory::instance()->createMessageQueue(10);
+ spParams.maxSize = sizeof(commandBuffer);
+ spParams.buf = commandBuffer;
+}
+
+PlocMpsocHandler::~PlocMpsocHandler() {}
+
+ReturnValue_t PlocMpsocHandler::initialize() {
+ ReturnValue_t result = returnvalue::OK;
+ result = DeviceHandlerBase::initialize();
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ uartComIf = dynamic_cast(communicationInterface);
+ if (uartComIf == nullptr) {
+ sif::warning << "PlocMPSoCHandler::initialize: Invalid uart com if" << std::endl;
+ return ObjectManagerIF::CHILD_INIT_FAILED;
+ }
+
+ EventManagerIF* manager = ObjectManager::instance()->get(objects::EVENT_MANAGER);
+ if (manager == nullptr) {
+#if FSFW_CPP_OSTREAM_ENABLED == 1
+ sif::error << "PlocMPSoCHandler::initialize: Invalid event manager" << std::endl;
+#endif
+ return ObjectManagerIF::CHILD_INIT_FAILED;
+ ;
+ }
+ result = manager->registerListener(eventQueue->getId());
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = manager->subscribeToEvent(
+ eventQueue->getId(),
+ event::getEventId(PlocMpsocSpecialComHelperLegacy::MPSOC_FLASH_WRITE_FAILED));
+ if (result != returnvalue::OK) {
+ return ObjectManagerIF::CHILD_INIT_FAILED;
+ }
+ result = manager->subscribeToEvent(
+ eventQueue->getId(),
+ event::getEventId(PlocMpsocSpecialComHelperLegacy::MPSOC_FLASH_WRITE_SUCCESSFUL));
+ if (result != returnvalue::OK) {
+ return ObjectManagerIF::CHILD_INIT_FAILED;
+ }
+ result = manager->subscribeToEvent(
+ eventQueue->getId(),
+ event::getEventId(PlocMpsocSpecialComHelperLegacy::MPSOC_FLASH_READ_SUCCESSFUL));
+ if (result != returnvalue::OK) {
+ return ObjectManagerIF::CHILD_INIT_FAILED;
+ }
+ result = manager->subscribeToEvent(
+ eventQueue->getId(),
+ event::getEventId(PlocMpsocSpecialComHelperLegacy::MPSOC_FLASH_READ_FAILED));
+ if (result != returnvalue::OK) {
+ return ObjectManagerIF::CHILD_INIT_FAILED;
+ }
+
+ result = specialComHelper->setComIF(communicationInterface);
+ if (result != returnvalue::OK) {
+ return ObjectManagerIF::CHILD_INIT_FAILED;
+ }
+ specialComHelper->setComCookie(comCookie);
+ specialComHelper->setSequenceCount(&sequenceCount);
+ result = commandActionHelper.initialize();
+ if (result != returnvalue::OK) {
+ return ObjectManagerIF::CHILD_INIT_FAILED;
+ }
+ return result;
+}
+
+void PlocMpsocHandler::performOperationHook() {
+ if (commandIsPending and cmdCountdown.hasTimedOut()) {
+ sif::warning << "PlocMpsocHandler: Command " << getPendingCommand() << " has timed out"
+ << std::endl;
+ commandIsPending = false;
+ // TODO: Better returnvalue?
+ cmdDoneHandler(false, returnvalue::FAILED);
+ }
+ EventMessage event;
+ for (ReturnValue_t result = eventQueue->receiveMessage(&event); result == returnvalue::OK;
+ result = eventQueue->receiveMessage(&event)) {
+ switch (event.getMessageId()) {
+ case EventMessage::EVENT_MESSAGE:
+ handleEvent(&event);
+ break;
+ default:
+ sif::debug << "PlocMPSoCHandler::performOperationHook: Did not subscribe to this event"
+ << " message" << std::endl;
+ break;
+ }
+ }
+ CommandMessage message;
+ for (ReturnValue_t result = commandActionHelperQueue->receiveMessage(&message);
+ result == returnvalue::OK; result = commandActionHelperQueue->receiveMessage(&message)) {
+ result = commandActionHelper.handleReply(&message);
+ if (result == returnvalue::OK) {
+ continue;
+ }
+ }
+}
+
+ReturnValue_t PlocMpsocHandler::executeAction(ActionId_t actionId, MessageQueueId_t commandedBy,
+ const uint8_t* data, size_t size) {
+ ReturnValue_t result = returnvalue::OK;
+ switch (actionId) {
+ case mpsoc::SET_UART_TX_TRISTATE: {
+ uartIsolatorSwitch.pullLow();
+ return EXECUTION_FINISHED;
+ break;
+ }
+ case mpsoc::RELEASE_UART_TX: {
+ uartIsolatorSwitch.pullHigh();
+ return EXECUTION_FINISHED;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (specialComHelperExecuting) {
+ return mpsoc::MPSOC_HELPER_EXECUTING;
+ }
+
+ switch (actionId) {
+ case mpsoc::TC_FLASH_WRITE_FULL_FILE: {
+ mpsoc::FlashBasePusCmd flashWritePusCmd;
+ result = flashWritePusCmd.extractFields(data, size);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = specialComHelper->startFlashWrite(flashWritePusCmd.getObcFile(),
+ flashWritePusCmd.getMPSoCFile());
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ specialComHelperExecuting = true;
+ return EXECUTION_FINISHED;
+ }
+ case mpsoc::TC_FLASH_READ_FULL_FILE: {
+ mpsoc::FlashReadPusCmd flashReadPusCmd;
+ result = flashReadPusCmd.extractFields(data, size);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = specialComHelper->startFlashRead(flashReadPusCmd.getObcFile(),
+ flashReadPusCmd.getMPSoCFile(),
+ flashReadPusCmd.getReadSize());
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ specialComHelperExecuting = true;
+ return EXECUTION_FINISHED;
+ }
+ case (mpsoc::OBSW_RESET_SEQ_COUNT): {
+ sequenceCount = 0;
+ return EXECUTION_FINISHED;
+ }
+ default:
+ break;
+ }
+ // For longer commands, do not set these.
+ commandIsPending = true;
+ cmdCountdown.resetTimer();
+ return DeviceHandlerBase::executeAction(actionId, commandedBy, data, size);
+}
+
+void PlocMpsocHandler::doStartUp() {
+ if (startupState == StartupState::IDLE) {
+ startupState = StartupState::HW_INIT;
+ }
+ if (startupState == StartupState::HW_INIT) {
+ if (handleHwStartup()) {
+ startupState = StartupState::DONE;
+ }
+ }
+ if (startupState == StartupState::DONE) {
+ setMode(_MODE_TO_ON);
+ hkReport.setReportingEnabled(true);
+ powerState = PowerState::IDLE;
+ startupState = StartupState::IDLE;
+ }
+}
+
+void PlocMpsocHandler::doShutDown() {
+ if (handleHwShutdown()) {
+ hkReport.setReportingEnabled(false);
+ setMode(_MODE_POWER_DOWN);
+ commandIsPending = false;
+ sequenceCount = 0;
+ powerState = PowerState::IDLE;
+ startupState = StartupState::IDLE;
+ }
+}
+
+ReturnValue_t PlocMpsocHandler::buildNormalDeviceCommand(DeviceCommandId_t* id) {
+ if (not commandIsPending and not specialComHelperExecuting) {
+ *id = mpsoc::TC_GET_HK_REPORT;
+ commandIsPending = true;
+ return buildCommandFromCommand(*id, nullptr, 0);
+ }
+ return NOTHING_TO_SEND;
+}
+
+ReturnValue_t PlocMpsocHandler::buildTransitionDeviceCommand(DeviceCommandId_t* id) {
+ return NOTHING_TO_SEND;
+}
+
+ReturnValue_t PlocMpsocHandler::buildCommandFromCommand(DeviceCommandId_t deviceCommand,
+ const uint8_t* commandData,
+ size_t commandDataLen) {
+ spParams.buf = commandBuffer;
+ ReturnValue_t result = returnvalue::OK;
+ switch (deviceCommand) {
+ case (mpsoc::TC_MEM_WRITE): {
+ result = prepareTcMemWrite(commandData, commandDataLen);
+ break;
+ }
+ case (mpsoc::TC_MEM_READ): {
+ result = prepareTcMemRead(commandData, commandDataLen);
+ break;
+ }
+ case (mpsoc::TC_FLASHDELETE): {
+ result = prepareTcFlashDelete(commandData, commandDataLen);
+ break;
+ }
+ case (mpsoc::TC_REPLAY_START): {
+ result = prepareTcReplayStart(commandData, commandDataLen);
+ break;
+ }
+ case (mpsoc::TC_REPLAY_STOP): {
+ result = prepareTcReplayStop();
+ break;
+ }
+ case (mpsoc::TC_DOWNLINK_PWR_ON): {
+ result = prepareTcDownlinkPwrOn(commandData, commandDataLen);
+ break;
+ }
+ case (mpsoc::TC_DOWNLINK_PWR_OFF): {
+ result = prepareTcDownlinkPwrOff();
+ break;
+ }
+ case (mpsoc::TC_REPLAY_WRITE_SEQUENCE): {
+ result = prepareTcReplayWriteSequence(commandData, commandDataLen);
+ break;
+ }
+ case (mpsoc::TC_GET_HK_REPORT): {
+ result = prepareTcGetHkReport();
+ break;
+ }
+ case (mpsoc::TC_FLASH_GET_DIRECTORY_CONTENT): {
+ result = prepareTcGetDirContent(commandData, commandDataLen);
+ break;
+ }
+ case (mpsoc::TC_MODE_REPLAY): {
+ result = prepareTcModeReplay();
+ break;
+ }
+ case (mpsoc::TC_MODE_IDLE): {
+ result = prepareTcModeIdle();
+ break;
+ }
+ case (mpsoc::TC_CAM_CMD_SEND): {
+ result = prepareTcCamCmdSend(commandData, commandDataLen);
+ break;
+ }
+ case (mpsoc::TC_CAM_TAKE_PIC): {
+ result = prepareTcCamTakePic(commandData, commandDataLen);
+ break;
+ }
+ case (mpsoc::TC_SIMPLEX_SEND_FILE): {
+ result = prepareTcSimplexSendFile(commandData, commandDataLen);
+ break;
+ }
+ case (mpsoc::TC_DOWNLINK_DATA_MODULATE): {
+ result = prepareTcDownlinkDataModulate(commandData, commandDataLen);
+ break;
+ }
+ case (mpsoc::TC_MODE_SNAPSHOT): {
+ result = prepareTcModeSnapshot();
+ break;
+ }
+ default:
+ sif::debug << "PlocMPSoCHandler::buildCommandFromCommand: Command not implemented"
+ << std::endl;
+ result = DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED;
+ break;
+ }
+
+ if (result == returnvalue::OK) {
+ /**
+ * Flushing the receive buffer to make sure there are no data left from a faulty reply.
+ */
+ uartComIf->flushUartRxBuffer(comCookie);
+ }
+
+ return result;
+}
+
+void PlocMpsocHandler::fillCommandAndReplyMap() {
+ this->insertInCommandMap(mpsoc::TC_MEM_WRITE);
+ this->insertInCommandMap(mpsoc::TC_MEM_READ);
+ this->insertInCommandMap(mpsoc::TC_FLASHDELETE);
+ insertInCommandMap(mpsoc::TC_FLASH_WRITE_FULL_FILE);
+ insertInCommandMap(mpsoc::TC_FLASH_READ_FULL_FILE);
+ this->insertInCommandMap(mpsoc::TC_REPLAY_START);
+ this->insertInCommandMap(mpsoc::TC_REPLAY_STOP);
+ this->insertInCommandMap(mpsoc::TC_DOWNLINK_PWR_ON);
+ this->insertInCommandMap(mpsoc::TC_DOWNLINK_PWR_OFF);
+ this->insertInCommandMap(mpsoc::TC_REPLAY_WRITE_SEQUENCE);
+ this->insertInCommandMap(mpsoc::TC_MODE_REPLAY);
+ this->insertInCommandMap(mpsoc::TC_MODE_IDLE);
+ this->insertInCommandMap(mpsoc::TC_CAM_CMD_SEND);
+ this->insertInCommandMap(mpsoc::TC_GET_HK_REPORT);
+ this->insertInCommandMap(mpsoc::RELEASE_UART_TX);
+ this->insertInCommandMap(mpsoc::SET_UART_TX_TRISTATE);
+ this->insertInCommandMap(mpsoc::TC_CAM_TAKE_PIC);
+ this->insertInCommandMap(mpsoc::TC_FLASH_GET_DIRECTORY_CONTENT);
+ this->insertInCommandMap(mpsoc::TC_SIMPLEX_SEND_FILE);
+ this->insertInCommandMap(mpsoc::TC_DOWNLINK_DATA_MODULATE);
+ this->insertInCommandMap(mpsoc::TC_MODE_SNAPSHOT);
+ this->insertInReplyMap(mpsoc::ACK_REPORT, 3, nullptr, mpsoc::SIZE_ACK_REPORT);
+ this->insertInReplyMap(mpsoc::EXE_REPORT, 3, nullptr, mpsoc::SIZE_EXE_REPORT);
+ this->insertInReplyMap(mpsoc::TM_MEMORY_READ_REPORT, 2, nullptr, mpsoc::SIZE_TM_MEM_READ_REPORT);
+ this->insertInReplyMap(mpsoc::TM_GET_HK_REPORT, 5, nullptr, mpsoc::SIZE_TM_HK_REPORT);
+ this->insertInReplyMap(mpsoc::TM_CAM_CMD_RPT, 2, nullptr, mpsoc::SP_MAX_SIZE);
+ this->insertInReplyMap(mpsoc::TM_FLASH_DIRECTORY_CONTENT, 2, nullptr, mpsoc::SP_MAX_SIZE);
+}
+
+ReturnValue_t PlocMpsocHandler::scanForReply(const uint8_t* start, size_t remainingSize,
+ DeviceCommandId_t* foundId, size_t* foundLen) {
+ ReturnValue_t result = returnvalue::OK;
+
+ SpacePacketReader spacePacket;
+ spacePacket.setReadOnlyData(start, remainingSize);
+ if (DEBUG_MPSOC_COMMUNICATION) {
+ sif::debug << "RECV MPSOC packet. APID 0x" << std::hex << std::setw(3) << spacePacket.getApid()
+ << std::dec << " Size " << spacePacket.getFullPacketLen() << " SSC "
+ << spacePacket.getSequenceCount() << std::endl;
+ }
+ if (spacePacket.isNull()) {
+ return returnvalue::FAILED;
+ }
+ auto res = spacePacket.checkSize();
+ if (res != returnvalue::OK) {
+ return res;
+ }
+ uint16_t apid = spacePacket.getApid();
+
+ auto handleDedicatedReply = [&](DeviceCommandId_t replyId) {
+ *foundLen = spacePacket.getFullPacketLen();
+ foundPacketLen = *foundLen;
+ *foundId = replyId;
+ };
+ switch (apid) {
+ case (mpsoc::apid::ACK_SUCCESS):
+ *foundLen = mpsoc::SIZE_ACK_REPORT;
+ *foundId = mpsoc::ACK_REPORT;
+ break;
+ case (mpsoc::apid::ACK_FAILURE):
+ *foundLen = mpsoc::SIZE_ACK_REPORT;
+ *foundId = mpsoc::ACK_REPORT;
+ break;
+ case (mpsoc::apid::TM_MEMORY_READ_REPORT):
+ *foundLen = tmMemReadReport.rememberRequestedSize;
+ *foundId = mpsoc::TM_MEMORY_READ_REPORT;
+ break;
+ case (mpsoc::apid::TM_CAM_CMD_RPT):
+ handleDedicatedReply(mpsoc::TM_CAM_CMD_RPT);
+ break;
+ case (mpsoc::apid::TM_HK_GET_REPORT): {
+ handleDedicatedReply(mpsoc::TM_GET_HK_REPORT);
+ break;
+ }
+ case (mpsoc::apid::TM_FLASH_DIRECTORY_CONTENT): {
+ handleDedicatedReply(mpsoc::TM_FLASH_DIRECTORY_CONTENT);
+ break;
+ }
+ case (mpsoc::apid::EXE_SUCCESS):
+ *foundLen = mpsoc::SIZE_EXE_REPORT;
+ *foundId = mpsoc::EXE_REPORT;
+ break;
+ case (mpsoc::apid::EXE_FAILURE):
+ *foundLen = mpsoc::SIZE_EXE_REPORT;
+ *foundId = mpsoc::EXE_REPORT;
+ break;
+ default: {
+ sif::debug << "PlocMPSoCHandler::scanForReply: Reply has invalid APID 0x" << std::hex
+ << std::setfill('0') << std::setw(2) << apid << std::dec << std::endl;
+ *foundLen = remainingSize;
+ return mpsoc::INVALID_APID;
+ }
+ }
+
+ uint16_t recvSeqCnt = ((*(start + 2) << 8) | *(start + 3)) & PACKET_SEQUENCE_COUNT_MASK;
+ if (recvSeqCnt != sequenceCount) {
+ triggerEvent(MPSOC_HANDLER_SEQUENCE_COUNT_MISMATCH, sequenceCount, recvSeqCnt);
+ sequenceCount = recvSeqCnt;
+ }
+ // This sequence count ping pong does not make any sense but it is how the MPSoC expects it.
+ sequenceCount++;
+
+ return result;
+}
+
+ReturnValue_t PlocMpsocHandler::interpretDeviceReply(DeviceCommandId_t id, const uint8_t* packet) {
+ ReturnValue_t result = returnvalue::OK;
+
+ switch (id) {
+ case mpsoc::ACK_REPORT: {
+ result = handleAckReport(packet);
+ break;
+ }
+ case (mpsoc::TM_MEMORY_READ_REPORT): {
+ result = handleMemoryReadReport(packet);
+ break;
+ }
+ case (mpsoc::TM_GET_HK_REPORT): {
+ result = handleGetHkReport(packet);
+ break;
+ }
+ case (mpsoc::TM_CAM_CMD_RPT): {
+ result = handleCamCmdRpt(packet);
+ break;
+ }
+ case (mpsoc::TM_FLASH_DIRECTORY_CONTENT): {
+ result = verifyPacket(packet, foundPacketLen);
+ if (result == mpsoc::CRC_FAILURE) {
+ sif::warning << "PLOC MPSoC: Flash directory content reply invalid CRC" << std::endl;
+ }
+ /** Send data to commanding queue */
+ handleDeviceTm(packet + mpsoc::DATA_FIELD_OFFSET,
+ foundPacketLen - mpsoc::DATA_FIELD_OFFSET - mpsoc::CRC_SIZE,
+ mpsoc::TM_FLASH_DIRECTORY_CONTENT);
+ nextReplyId = mpsoc::EXE_REPORT;
+ return result;
+ }
+ case (mpsoc::EXE_REPORT): {
+ result = handleExecutionReport(packet);
+ break;
+ }
+ default: {
+ sif::debug << "PlocMPSoCHandler::interpretDeviceReply: Unknown device reply id" << std::endl;
+ return DeviceHandlerIF::UNKNOWN_DEVICE_REPLY;
+ }
+ }
+
+ return result;
+}
+
+void PlocMpsocHandler::setNormalDatapoolEntriesInvalid() {
+ PoolReadGuard pg(&hkReport);
+ hkReport.setValidity(false, true);
+}
+
+uint32_t PlocMpsocHandler::getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) { return 15000; }
+
+ReturnValue_t PlocMpsocHandler::initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
+ LocalDataPoolManager& poolManager) {
+ localDataPoolMap.emplace(mpsoc::poolid::STATUS, &peStatus);
+ localDataPoolMap.emplace(mpsoc::poolid::MODE, &peMode);
+ localDataPoolMap.emplace(mpsoc::poolid::DOWNLINK_PWR_ON, &peDownlinkPwrOn);
+ localDataPoolMap.emplace(mpsoc::poolid::DOWNLINK_REPLY_ACTIIVE, &peDownlinkReplyActive);
+ localDataPoolMap.emplace(mpsoc::poolid::DOWNLINK_JESD_SYNC_STATUS, &peDownlinkJesdSyncStatus);
+ localDataPoolMap.emplace(mpsoc::poolid::DOWNLINK_DAC_STATUS, &peDownlinkDacStatus);
+ localDataPoolMap.emplace(mpsoc::poolid::CAM_STATUS, &peCameraStatus);
+ localDataPoolMap.emplace(mpsoc::poolid::CAM_SDI_STATUS, &peCameraSdiStatus);
+ localDataPoolMap.emplace(mpsoc::poolid::CAM_FPGA_TEMP, &peCameraFpgaTemp);
+ localDataPoolMap.emplace(mpsoc::poolid::CAM_SOC_TEMP, &peCameraSocTemp);
+ localDataPoolMap.emplace(mpsoc::poolid::SYSMON_TEMP, &peSysmonTemp);
+ localDataPoolMap.emplace(mpsoc::poolid::SYSMON_VCCINT, &peSysmonVccInt);
+ localDataPoolMap.emplace(mpsoc::poolid::SYSMON_VCCAUX, &peSysmonVccAux);
+ localDataPoolMap.emplace(mpsoc::poolid::SYSMON_VCCBRAM, &peSysmonVccBram);
+ localDataPoolMap.emplace(mpsoc::poolid::SYSMON_VCCPAUX, &peSysmonVccPaux);
+ localDataPoolMap.emplace(mpsoc::poolid::SYSMON_VCCPINT, &peSysmonVccPint);
+ localDataPoolMap.emplace(mpsoc::poolid::SYSMON_VCCPDRO, &peSysmonVccPdro);
+ localDataPoolMap.emplace(mpsoc::poolid::SYSMON_MB12V, &peSysmonMb12V);
+ localDataPoolMap.emplace(mpsoc::poolid::SYSMON_MB3V3, &peSysmonMb3V3);
+ localDataPoolMap.emplace(mpsoc::poolid::SYSMON_MB1V8, &peSysmonMb1V8);
+ localDataPoolMap.emplace(mpsoc::poolid::SYSMON_VCC12V, &peSysmonVcc12V);
+ localDataPoolMap.emplace(mpsoc::poolid::SYSMON_VCC5V, &peSysmonVcc5V);
+ localDataPoolMap.emplace(mpsoc::poolid::SYSMON_VCC3V3, &peSysmonVcc3V3);
+ localDataPoolMap.emplace(mpsoc::poolid::SYSMON_VCC3V3VA, &peSysmonVcc3V3VA);
+ localDataPoolMap.emplace(mpsoc::poolid::SYSMON_VCC2V5DDR, &peSysmonVcc2V5DDR);
+ localDataPoolMap.emplace(mpsoc::poolid::SYSMON_VCC1V2DDR, &peSysmonVcc1V2DDR);
+ localDataPoolMap.emplace(mpsoc::poolid::SYSMON_VCC0V9, &peSysmonVcc0V9);
+ localDataPoolMap.emplace(mpsoc::poolid::SYSMON_VCC0V6VTT, &peSysmonVcc0V6VTT);
+ localDataPoolMap.emplace(mpsoc::poolid::SYSMON_SAFE_COTS_CUR, &peSysmonSafeCotsCur);
+ localDataPoolMap.emplace(mpsoc::poolid::SYSMON_NVM4_XO_CUR, &peSysmonNvm4XoCur);
+ localDataPoolMap.emplace(mpsoc::poolid::SEM_UNCORRECTABLE_ERRS, &peSemUncorrectableErrs);
+ localDataPoolMap.emplace(mpsoc::poolid::SEM_CORRECTABLE_ERRS, &peSemCorrectableErrs);
+ localDataPoolMap.emplace(mpsoc::poolid::SEM_STATUS, &peSemStatus);
+ localDataPoolMap.emplace(mpsoc::poolid::REBOOT_MPSOC_REQUIRED, &peRebootMpsocRequired);
+ poolManager.subscribeForRegularPeriodicPacket(
+ subdp::RegularHkPeriodicParams(hkReport.getSid(), false, 10.0));
+ return returnvalue::OK;
+}
+
+void PlocMpsocHandler::handleEvent(EventMessage* eventMessage) {
+ object_id_t objectId = eventMessage->getReporter();
+ switch (objectId) {
+ case objects::PLOC_MPSOC_HELPER: {
+ specialComHelperExecuting = false;
+ break;
+ }
+ default:
+ sif::debug << "PlocMPSoCHandler::handleEvent: Did not subscribe to this event" << std::endl;
+ break;
+ }
+}
+
+ReturnValue_t PlocMpsocHandler::prepareTcMemWrite(const uint8_t* commandData,
+ size_t commandDataLen) {
+ ReturnValue_t result = returnvalue::OK;
+ mpsoc::TcMemWrite tcMemWrite(spParams, sequenceCount);
+ result = tcMemWrite.setPayload(commandData, commandDataLen);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(tcMemWrite);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocHandler::prepareTcMemRead(const uint8_t* commandData,
+ size_t commandDataLen) {
+ ReturnValue_t result = returnvalue::OK;
+ mpsoc::TcMemRead tcMemRead(spParams, sequenceCount);
+ result = tcMemRead.setPayload(commandData, commandDataLen);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(tcMemRead);
+ tmMemReadReport.rememberRequestedSize = tcMemRead.getMemLen() * 4 + TmMemReadReport::FIX_SIZE;
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocHandler::prepareTcFlashDelete(const uint8_t* commandData,
+ size_t commandDataLen) {
+ if (commandDataLen > config::MAX_PATH_SIZE + config::MAX_FILENAME_SIZE) {
+ return mpsoc::NAME_TOO_LONG;
+ }
+ ReturnValue_t result = returnvalue::OK;
+ mpsoc::TcFlashDelete tcFlashDelete(spParams, sequenceCount);
+ std::string filename = std::string(reinterpret_cast(commandData), commandDataLen);
+ result = tcFlashDelete.setPayload(filename);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(tcFlashDelete);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocHandler::prepareTcReplayStart(const uint8_t* commandData,
+ size_t commandDataLen) {
+ ReturnValue_t result = returnvalue::OK;
+ mpsoc::TcReplayStart tcReplayStart(spParams, sequenceCount);
+ result = tcReplayStart.setPayload(commandData, commandDataLen);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(tcReplayStart);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocHandler::prepareTcReplayStop() {
+ mpsoc::TcReplayStop tcReplayStop(spParams, sequenceCount);
+ finishTcPrep(tcReplayStop);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocHandler::prepareTcDownlinkPwrOn(const uint8_t* commandData,
+ size_t commandDataLen) {
+ ReturnValue_t result = returnvalue::OK;
+ mpsoc::TcDownlinkPwrOn tcDownlinkPwrOn(spParams, sequenceCount);
+ result = tcDownlinkPwrOn.setPayload(commandData, commandDataLen);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(tcDownlinkPwrOn);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocHandler::prepareTcDownlinkPwrOff() {
+ mpsoc::TcDownlinkPwrOff tcDownlinkPwrOff(spParams, sequenceCount);
+ finishTcPrep(tcDownlinkPwrOff);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocHandler::prepareTcGetHkReport() {
+ mpsoc::TcGetHkReport tcGetHkReport(spParams, sequenceCount);
+ finishTcPrep(tcGetHkReport);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocHandler::prepareTcReplayWriteSequence(const uint8_t* commandData,
+ size_t commandDataLen) {
+ mpsoc::TcReplayWriteSeq tcReplayWriteSeq(spParams, sequenceCount);
+ ReturnValue_t result = tcReplayWriteSeq.setPayload(commandData, commandDataLen);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(tcReplayWriteSeq);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocHandler::prepareTcModeReplay() {
+ mpsoc::TcModeReplay tcModeReplay(spParams, sequenceCount);
+ finishTcPrep(tcModeReplay);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocHandler::prepareTcModeIdle() {
+ mpsoc::TcModeIdle tcModeIdle(spParams, sequenceCount);
+ finishTcPrep(tcModeIdle);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocHandler::prepareTcCamCmdSend(const uint8_t* commandData,
+ size_t commandDataLen) {
+ mpsoc::TcCamcmdSend tcCamCmdSend(spParams, sequenceCount);
+ ReturnValue_t result = tcCamCmdSend.setPayload(commandData, commandDataLen);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(tcCamCmdSend);
+ nextReplyId = mpsoc::TM_CAM_CMD_RPT;
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocHandler::prepareTcCamTakePic(const uint8_t* commandData,
+ size_t commandDataLen) {
+ mpsoc::TcCamTakePic tcCamTakePic(spParams, sequenceCount);
+ ReturnValue_t result = tcCamTakePic.setPayload(commandData, commandDataLen);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(tcCamTakePic);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocHandler::prepareTcSimplexSendFile(const uint8_t* commandData,
+ size_t commandDataLen) {
+ mpsoc::TcSimplexSendFile tcSimplexSendFile(spParams, sequenceCount);
+ ReturnValue_t result = tcSimplexSendFile.setPayload(commandData, commandDataLen);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(tcSimplexSendFile);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocHandler::prepareTcGetDirContent(const uint8_t* commandData,
+ size_t commandDataLen) {
+ mpsoc::TcGetDirContent tcGetDirContent(spParams, sequenceCount);
+ ReturnValue_t result = tcGetDirContent.setPayload(commandData, commandDataLen);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(tcGetDirContent);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocHandler::prepareTcDownlinkDataModulate(const uint8_t* commandData,
+ size_t commandDataLen) {
+ mpsoc::TcDownlinkDataModulate tcDownlinkDataModulate(spParams, sequenceCount);
+ ReturnValue_t result = tcDownlinkDataModulate.setPayload(commandData, commandDataLen);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(tcDownlinkDataModulate);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocHandler::prepareTcModeSnapshot() {
+ mpsoc::TcModeSnapshot tcModeSnapshot(spParams, sequenceCount);
+ finishTcPrep(tcModeSnapshot);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocHandler::finishTcPrep(mpsoc::TcBase& tcBase) {
+ nextReplyId = mpsoc::ACK_REPORT;
+ ReturnValue_t result = tcBase.finishPacket();
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ rawPacket = commandBuffer;
+ rawPacketLen = tcBase.getFullPacketLen();
+ sequenceCount++;
+
+ if (DEBUG_MPSOC_COMMUNICATION) {
+ sif::debug << "SEND MPSOC packet. APID 0x" << std::hex << std::setw(3) << tcBase.getApid()
+ << " Size " << std::dec << tcBase.getFullPacketLen() << " SSC "
+ << tcBase.getSeqCount() << std::endl;
+ }
+ cmdCountdown.resetTimer();
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocHandler::verifyPacket(const uint8_t* start, size_t foundLen) {
+ if (CRC::crc16ccitt(start, foundLen) != 0) {
+ return mpsoc::CRC_FAILURE;
+ }
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocHandler::handleAckReport(const uint8_t* data) {
+ ReturnValue_t result = returnvalue::OK;
+
+ result = verifyPacket(data, mpsoc::SIZE_ACK_REPORT);
+ if (result == mpsoc::CRC_FAILURE) {
+ sif::warning << "PlocMPSoCHandler::handleAckReport: CRC failure" << std::endl;
+ nextReplyId = mpsoc::NONE;
+ replyRawReplyIfnotWiretapped(data, mpsoc::SIZE_ACK_REPORT);
+ triggerEvent(MPSOC_HANDLER_CRC_FAILURE);
+ sendFailureReport(mpsoc::ACK_REPORT, mpsoc::CRC_FAILURE);
+ disableAllReplies();
+ return IGNORE_REPLY_DATA;
+ }
+
+ uint16_t apid = (*(data) << 8 | *(data + 1)) & APID_MASK;
+
+ switch (apid) {
+ case mpsoc::apid::ACK_FAILURE: {
+ DeviceCommandId_t commandId = getPendingCommand();
+ uint16_t status = mpsoc::getStatusFromRawData(data);
+ sif::warning << "MPSoC ACK Failure: " << mpsoc::getStatusString(status) << std::endl;
+ if (commandId != DeviceHandlerIF::NO_COMMAND_ID) {
+ triggerEvent(ACK_FAILURE, commandId, status);
+ }
+ sendFailureReport(mpsoc::ACK_REPORT, status);
+ disableAllReplies();
+ nextReplyId = mpsoc::NONE;
+ result = IGNORE_REPLY_DATA;
+ break;
+ }
+ case mpsoc::apid::ACK_SUCCESS: {
+ setNextReplyId();
+ break;
+ }
+ default: {
+ sif::debug << "PlocMPSoCHandler::handleAckReport: Invalid APID in Ack report" << std::endl;
+ result = returnvalue::FAILED;
+ break;
+ }
+ }
+
+ return result;
+}
+
+ReturnValue_t PlocMpsocHandler::handleExecutionReport(const uint8_t* data) {
+ ReturnValue_t result = returnvalue::OK;
+
+ result = verifyPacket(data, mpsoc::SIZE_EXE_REPORT);
+ if (result == mpsoc::CRC_FAILURE) {
+ sif::warning << "PlocMPSoCHandler::handleExecutionReport: CRC failure" << std::endl;
+ nextReplyId = mpsoc::NONE;
+ return result;
+ }
+
+ uint16_t apid = (*(data) << 8 | *(data + 1)) & APID_MASK;
+
+ switch (apid) {
+ case (mpsoc::apid::EXE_SUCCESS): {
+ cmdDoneHandler(true, result);
+ break;
+ }
+ case (mpsoc::apid::EXE_FAILURE): {
+ DeviceCommandId_t commandId = getPendingCommand();
+ if (commandId == DeviceHandlerIF::NO_COMMAND_ID) {
+ sif::debug << "PlocMPSoCHandler::handleExecutionReport: Unknown command id" << std::endl;
+ }
+ uint16_t status = mpsoc::getStatusFromRawData(data);
+ sif::warning << "MPSoC EXE Failure: " << mpsoc::getStatusString(status) << std::endl;
+ triggerEvent(EXE_FAILURE, commandId, status);
+ sendFailureReport(mpsoc::EXE_REPORT, mpsoc::RECEIVED_EXE_FAILURE);
+ result = IGNORE_REPLY_DATA;
+ cmdDoneHandler(false, mpsoc::RECEIVED_EXE_FAILURE);
+ break;
+ }
+ default: {
+ sif::warning << "PlocMPSoCHandler::handleExecutionReport: Unknown APID" << std::endl;
+ result = returnvalue::FAILED;
+ break;
+ }
+ }
+ nextReplyId = mpsoc::NONE;
+ return result;
+}
+
+ReturnValue_t PlocMpsocHandler::handleMemoryReadReport(const uint8_t* data) {
+ ReturnValue_t result = returnvalue::OK;
+ result = verifyPacket(data, tmMemReadReport.rememberRequestedSize);
+ if (result == mpsoc::CRC_FAILURE) {
+ sif::warning << "PlocMPSoCHandler::handleMemoryReadReport: Memory read report has invalid crc"
+ << std::endl;
+ }
+ uint16_t memLen =
+ *(data + mpsoc::MEM_READ_RPT_LEN_OFFSET) << 8 | *(data + mpsoc::MEM_READ_RPT_LEN_OFFSET + 1);
+ /** Send data to commanding queue */
+ handleDeviceTm(data + mpsoc::DATA_FIELD_OFFSET, mpsoc::SIZE_MEM_READ_RPT_FIX + memLen * 4,
+ mpsoc::TM_MEMORY_READ_REPORT);
+ nextReplyId = mpsoc::EXE_REPORT;
+ return result;
+}
+
+ReturnValue_t PlocMpsocHandler::handleGetHkReport(const uint8_t* data) {
+ ReturnValue_t result = verifyPacket(data, foundPacketLen);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ SpacePacketReader packetReader(data, foundPacketLen);
+ const uint8_t* dataStart = data + 6;
+ PoolReadGuard pg(&hkReport);
+ size_t deserLen = mpsoc::SIZE_TM_HK_REPORT;
+ SerializeIF::Endianness endianness = SerializeIF::Endianness::NETWORK;
+ result = SerializeAdapter::deSerialize(&hkReport.status.value, &dataStart, &deserLen, endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.mode.value, &dataStart, &deserLen, endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.downlinkPwrOn.value, &dataStart, &deserLen,
+ endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.downlinkReplyActive.value, &dataStart, &deserLen,
+ endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.downlinkJesdSyncStatus.value, &dataStart,
+ &deserLen, endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.downlinkDacStatus.value, &dataStart, &deserLen,
+ endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result =
+ SerializeAdapter::deSerialize(&hkReport.camStatus.value, &dataStart, &deserLen, endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.camSdiStatus.value, &dataStart, &deserLen,
+ endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result =
+ SerializeAdapter::deSerialize(&hkReport.camFpgaTemp.value, &dataStart, &deserLen, endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result =
+ SerializeAdapter::deSerialize(&hkReport.sysmonTemp.value, &dataStart, &deserLen, endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.sysmonVccInt.value, &dataStart, &deserLen,
+ endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.sysmonVccAux.value, &dataStart, &deserLen,
+ endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.sysmonVccBram.value, &dataStart, &deserLen,
+ endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.sysmonVccPaux.value, &dataStart, &deserLen,
+ endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.sysmonVccPint.value, &dataStart, &deserLen,
+ endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.sysmonVccPdro.value, &dataStart, &deserLen,
+ endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result =
+ SerializeAdapter::deSerialize(&hkReport.sysmonMb12V.value, &dataStart, &deserLen, endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result =
+ SerializeAdapter::deSerialize(&hkReport.sysmonMb3V3.value, &dataStart, &deserLen, endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result =
+ SerializeAdapter::deSerialize(&hkReport.sysmonMb1V8.value, &dataStart, &deserLen, endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.sysmonVcc12V.value, &dataStart, &deserLen,
+ endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result =
+ SerializeAdapter::deSerialize(&hkReport.sysmonVcc5V.value, &dataStart, &deserLen, endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.sysmonVcc3V3.value, &dataStart, &deserLen,
+ endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.sysmonVcc3V3VA.value, &dataStart, &deserLen,
+ endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.sysmonVcc2V5DDR.value, &dataStart, &deserLen,
+ endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.sysmonVcc1V2DDR.value, &dataStart, &deserLen,
+ endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.sysmonVcc0V9.value, &dataStart, &deserLen,
+ endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.sysmonVcc0V6VTT.value, &dataStart, &deserLen,
+ endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.sysmonSafeCotsCur.value, &dataStart, &deserLen,
+ endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.sysmonNvm4XoCur.value, &dataStart, &deserLen,
+ endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.semUncorrectableErrs.value, &dataStart,
+ &deserLen, endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(&hkReport.semCorrectableErrs.value, &dataStart, &deserLen,
+ endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result =
+ SerializeAdapter::deSerialize(&hkReport.semStatus.value, &dataStart, &deserLen, endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ // Skip the weird filename
+ dataStart += 256;
+ result = SerializeAdapter::deSerialize(&hkReport.rebootMpsocRequired, &dataStart, &deserLen,
+ endianness);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ hkReport.setValidity(true, true);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocHandler::handleCamCmdRpt(const uint8_t* data) {
+ ReturnValue_t result = verifyPacket(data, foundPacketLen);
+ if (result == mpsoc::CRC_FAILURE) {
+ sif::warning << "PlocMPSoCHandler::handleCamCmdRpt: CRC failure" << std::endl;
+ }
+ SpacePacketReader packetReader(data, foundPacketLen);
+ const uint8_t* dataFieldPtr = data + mpsoc::SPACE_PACKET_HEADER_SIZE + sizeof(uint16_t);
+ std::string camCmdRptMsg(reinterpret_cast(dataFieldPtr),
+ foundPacketLen - mpsoc::SPACE_PACKET_HEADER_SIZE - sizeof(uint16_t) - 3);
+#if OBSW_DEBUG_PLOC_MPSOC == 1
+ uint8_t ackValue = *(packetReader.getFullData() + packetReader.getFullPacketLen() - 2);
+ sif::info << "PlocMPSoCHandler: CamCmdRpt message: " << camCmdRptMsg << std::endl;
+ sif::info << "PlocMPSoCHandler: CamCmdRpt Ack value: 0x" << std::hex
+ << static_cast(ackValue) << std::endl;
+#endif /* OBSW_DEBUG_PLOC_MPSOC == 1 */
+ handleDeviceTm(packetReader.getPacketData() + sizeof(uint16_t),
+ packetReader.getPacketDataLen() - 1, mpsoc::TM_CAM_CMD_RPT);
+ return result;
+}
+
+ReturnValue_t PlocMpsocHandler::enableReplyInReplyMap(DeviceCommandMap::iterator command,
+ uint8_t expectedReplies, bool useAlternateId,
+ DeviceCommandId_t alternateReplyID) {
+ ReturnValue_t result = returnvalue::OK;
+
+ uint8_t enabledReplies = 0;
+
+ auto enableThreeReplies = [&](DeviceCommandId_t replyId) {
+ enabledReplies = 3;
+ result = DeviceHandlerBase::enableReplyInReplyMap(command, enabledReplies, true, replyId);
+ if (result != returnvalue::OK) {
+ sif::debug << "PlocMPSoCHandler::enableReplyInReplyMap: Reply with id "
+ << mpsoc::TM_MEMORY_READ_REPORT << " not in replyMap" << std::endl;
+ return result;
+ }
+ return returnvalue::OK;
+ };
+ switch (command->first) {
+ case mpsoc::TC_MEM_WRITE:
+ case mpsoc::TC_FLASHDELETE:
+ case mpsoc::TC_REPLAY_START:
+ case mpsoc::TC_REPLAY_STOP:
+ case mpsoc::TC_DOWNLINK_PWR_ON:
+ case mpsoc::TC_DOWNLINK_PWR_OFF:
+ case mpsoc::TC_REPLAY_WRITE_SEQUENCE:
+ case mpsoc::TC_MODE_REPLAY:
+ case mpsoc::TC_MODE_IDLE:
+ case mpsoc::TC_CAM_TAKE_PIC:
+ case mpsoc::TC_SIMPLEX_SEND_FILE:
+ case mpsoc::TC_DOWNLINK_DATA_MODULATE:
+ case mpsoc::TC_MODE_SNAPSHOT:
+ enabledReplies = 2;
+ break;
+ case mpsoc::TC_GET_HK_REPORT: {
+ result = enableThreeReplies(mpsoc::TM_GET_HK_REPORT);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ break;
+ }
+ case mpsoc::TC_MEM_READ: {
+ result = enableThreeReplies(mpsoc::TM_MEMORY_READ_REPORT);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ break;
+ }
+ case mpsoc::TC_CAM_CMD_SEND: {
+ result = enableThreeReplies(mpsoc::TM_CAM_CMD_RPT);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ break;
+ }
+ case mpsoc::TC_FLASH_GET_DIRECTORY_CONTENT: {
+ result = enableThreeReplies(mpsoc::TM_FLASH_DIRECTORY_CONTENT);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ break;
+ }
+ case mpsoc::OBSW_RESET_SEQ_COUNT:
+ break;
+ default:
+ sif::debug << "PlocMPSoCHandler::enableReplyInReplyMap: Unknown command id" << std::endl;
+ break;
+ }
+
+ /**
+ * Every command causes at least one acknowledgment and one execution report. Therefore both
+ * replies will be enabled here.
+ */
+ result =
+ DeviceHandlerBase::enableReplyInReplyMap(command, enabledReplies, true, mpsoc::ACK_REPORT);
+ if (result != returnvalue::OK) {
+ sif::debug << "PlocMPSoCHandler::enableReplyInReplyMap: Reply with id " << mpsoc::ACK_REPORT
+ << " not in replyMap" << std::endl;
+ }
+
+ result =
+ DeviceHandlerBase::enableReplyInReplyMap(command, enabledReplies, true, mpsoc::EXE_REPORT);
+ if (result != returnvalue::OK) {
+ sif::debug << "PlocMPSoCHandler::enableReplyInReplyMap: Reply with id " << mpsoc::EXE_REPORT
+ << " not in replyMap" << std::endl;
+ }
+
+ switch (command->first) {
+ case mpsoc::TC_REPLAY_WRITE_SEQUENCE: {
+ DeviceReplyIter iter = deviceReplyMap.find(mpsoc::EXE_REPORT);
+ // Overwrite delay cycles because replay write sequence command can required up to
+ // 30 seconds for execution
+ iter->second.delayCycles = mpsoc::TC_WRITE_SEQ_EXECUTION_DELAY;
+ break;
+ }
+ case mpsoc::TC_DOWNLINK_PWR_ON: {
+ DeviceReplyIter iter = deviceReplyMap.find(mpsoc::EXE_REPORT);
+ iter->second.delayCycles = mpsoc::TC_DOWNLINK_PWR_ON_EXECUTION_DELAY;
+ break;
+ }
+ case mpsoc::TC_CAM_TAKE_PIC: {
+ DeviceReplyIter iter = deviceReplyMap.find(mpsoc::EXE_REPORT);
+ iter->second.delayCycles = mpsoc::TC_CAM_TAKE_PIC_EXECUTION_DELAY;
+ break;
+ }
+ case mpsoc::TC_SIMPLEX_SEND_FILE: {
+ DeviceReplyIter iter = deviceReplyMap.find(mpsoc::EXE_REPORT);
+ iter->second.delayCycles = mpsoc::TC_SIMPLEX_SEND_FILE_DELAY;
+ break;
+ }
+ default:
+ break;
+ }
+
+ return returnvalue::OK;
+}
+
+void PlocMpsocHandler::setNextReplyId() {
+ switch (getPendingCommand()) {
+ case mpsoc::TC_MEM_READ:
+ nextReplyId = mpsoc::TM_MEMORY_READ_REPORT;
+ break;
+ case mpsoc::TC_FLASH_GET_DIRECTORY_CONTENT: {
+ nextReplyId = mpsoc::TM_FLASH_DIRECTORY_CONTENT;
+ break;
+ }
+ case mpsoc::TC_GET_HK_REPORT: {
+ nextReplyId = mpsoc::TM_GET_HK_REPORT;
+ break;
+ }
+ default:
+ /* If no telemetry is expected the next reply is always the execution report */
+ nextReplyId = mpsoc::EXE_REPORT;
+ break;
+ }
+}
+
+size_t PlocMpsocHandler::getNextReplyLength(DeviceCommandId_t commandId) {
+ size_t replyLen = 0;
+
+ if (nextReplyId == mpsoc::NONE) {
+ return replyLen;
+ }
+
+ DeviceReplyIter iter = deviceReplyMap.find(nextReplyId);
+
+ if (iter != deviceReplyMap.end()) {
+ if (iter->second.delayCycles == 0) {
+ /* Reply inactive */
+ return replyLen;
+ }
+ switch (nextReplyId) {
+ case mpsoc::TM_MEMORY_READ_REPORT: {
+ replyLen = tmMemReadReport.rememberRequestedSize;
+ break;
+ }
+ case mpsoc::TM_CAM_CMD_RPT:
+ // Read acknowledgment, camera and execution report in one go because length of camera
+ // report is not fixed
+ replyLen = mpsoc::SP_MAX_SIZE;
+ break;
+ case mpsoc::TM_FLASH_DIRECTORY_CONTENT:
+ // I think the reply size is not fixed either.
+ replyLen = mpsoc::SP_MAX_SIZE;
+ break;
+ default: {
+ replyLen = iter->second.replyLen;
+ break;
+ }
+ }
+ } else {
+ sif::debug << "PlocMPSoCHandler::getNextReplyLength: No entry for reply with reply id "
+ << std::hex << nextReplyId << " in deviceReplyMap" << std::endl;
+ }
+
+ return replyLen;
+}
+
+ReturnValue_t PlocMpsocHandler::doSendReadHook() {
+ // Prevent DHB from polling UART during commands executed by the mpsoc helper task
+ if (specialComHelperExecuting) {
+ return returnvalue::FAILED;
+ }
+ return returnvalue::OK;
+}
+
+MessageQueueIF* PlocMpsocHandler::getCommandQueuePtr() { return commandActionHelperQueue; }
+
+void PlocMpsocHandler::stepSuccessfulReceived(ActionId_t actionId, uint8_t step) { return; }
+
+void PlocMpsocHandler::stepFailedReceived(ActionId_t actionId, uint8_t step,
+ ReturnValue_t returnCode) {
+ switch (actionId) {
+ case supv::START_MPSOC: {
+ sif::warning << "PlocMPSoCHandler::stepFailedReceived: Failed to start MPSoC" << std::endl;
+ break;
+ }
+ case supv::SHUTDOWN_MPSOC: {
+ triggerEvent(MPSOC_SHUTDOWN_FAILED);
+ sif::warning << "PlocMPSoCHandler::stepFailedReceived: Failed to shutdown MPSoC" << std::endl;
+ break;
+ }
+ default:
+ sif::debug << "PlocMPSoCHandler::stepFailedReceived: Received unexpected action reply"
+ << std::endl;
+ break;
+ }
+ powerState = PowerState::SUPV_FAILED;
+}
+
+void PlocMpsocHandler::dataReceived(ActionId_t actionId, const uint8_t* data, uint32_t size) {
+ return;
+}
+
+void PlocMpsocHandler::completionSuccessfulReceived(ActionId_t actionId) {
+ if (actionId == supv::ACK_REPORT) {
+ // I seriously don't know why this happens..
+ // sif::warning
+ // << "PlocMpsocHandler::completionSuccessfulReceived: Only received ACK report. Consider
+ // "
+ // "increasing the MPSoC boot timer."
+ // << std::endl;
+ } else if (actionId != supv::EXE_REPORT) {
+ sif::warning << "PlocMpsocHandler::completionSuccessfulReceived: Did not expect the action "
+ << "ID " << actionId << std::endl;
+ return;
+ }
+ switch (powerState) {
+ case PowerState::PENDING_STARTUP: {
+ mpsocBootTransitionCd.resetTimer();
+ powerState = PowerState::DONE;
+ break;
+ }
+ case PowerState::PENDING_SHUTDOWN: {
+ powerState = PowerState::DONE;
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
+void PlocMpsocHandler::completionFailedReceived(ActionId_t actionId, ReturnValue_t returnCode) {
+ handleActionCommandFailure(actionId);
+}
+
+void PlocMpsocHandler::handleDeviceTm(const uint8_t* data, size_t dataSize,
+ DeviceCommandId_t replyId) {
+ ReturnValue_t result = returnvalue::OK;
+
+ if (wiretappingMode == RAW) {
+ /* Data already sent in doGetRead() */
+ return;
+ }
+
+ DeviceReplyMap::iterator iter = deviceReplyMap.find(replyId);
+ if (iter == deviceReplyMap.end()) {
+ sif::debug << "PlocMPSoCHandler::handleDeviceTM: Unknown reply id" << std::endl;
+ return;
+ }
+ MessageQueueId_t queueId = iter->second.command->second.sendReplyTo;
+
+ if (queueId == NO_COMMANDER) {
+ return;
+ }
+
+ result = actionHelper.reportData(queueId, replyId, data, dataSize);
+ if (result != returnvalue::OK) {
+ sif::debug << "PlocMPSoCHandler::handleDeviceTM: Failed to report data" << std::endl;
+ }
+}
+
+void PlocMpsocHandler::disableAllReplies() {
+ using namespace mpsoc;
+ DeviceReplyMap::iterator iter;
+
+ /* Disable ack reply */
+ iter = deviceReplyMap.find(ACK_REPORT);
+ DeviceReplyInfo* info = &(iter->second);
+ info->delayCycles = 0;
+ info->command = deviceCommandMap.end();
+
+ DeviceCommandId_t commandId = getPendingCommand();
+
+ auto disableCommandWithReply = [&](DeviceCommandId_t replyId) {
+ iter = deviceReplyMap.find(replyId);
+ info = &(iter->second);
+ info->delayCycles = 0;
+ info->active = false;
+ info->command = deviceCommandMap.end();
+ };
+ /* If the command expects a telemetry packet the appropriate tm reply will be disabled here */
+ switch (commandId) {
+ case TC_MEM_WRITE:
+ case TC_FLASHDELETE:
+ case TC_REPLAY_START:
+ case TC_REPLAY_STOP:
+ case TC_DOWNLINK_PWR_ON:
+ case TC_DOWNLINK_PWR_OFF:
+ case TC_REPLAY_WRITE_SEQUENCE:
+ case TC_MODE_REPLAY:
+ case TC_MODE_IDLE:
+ case TC_CAM_TAKE_PIC:
+ case TC_SIMPLEX_SEND_FILE:
+ case TC_DOWNLINK_DATA_MODULATE:
+ case TC_MODE_SNAPSHOT:
+ break;
+ case TC_MEM_READ: {
+ disableCommandWithReply(TM_MEMORY_READ_REPORT);
+ break;
+ }
+ case TC_GET_HK_REPORT: {
+ disableCommandWithReply(TM_GET_HK_REPORT);
+ break;
+ }
+ case TC_FLASH_GET_DIRECTORY_CONTENT: {
+ disableCommandWithReply(TM_FLASH_DIRECTORY_CONTENT);
+ break;
+ }
+ case TC_CAM_CMD_SEND: {
+ disableCommandWithReply(TM_CAM_CMD_RPT);
+ break;
+ }
+ default: {
+ sif::debug << "PlocMPSoCHandler::disableAllReplies: Unknown command id: " << commandId
+ << std::endl;
+ break;
+ }
+ }
+
+ /* We always need to disable the execution report reply here */
+ disableExeReportReply();
+ nextReplyId = mpsoc::NONE;
+}
+
+void PlocMpsocHandler::sendFailureReport(DeviceCommandId_t replyId, ReturnValue_t status) {
+ DeviceReplyIter iter = deviceReplyMap.find(replyId);
+ if (iter == deviceReplyMap.end()) {
+ sif::debug << "PlocMPSoCHandler::sendFailureReport: Reply not in reply map" << std::endl;
+ return;
+ }
+ DeviceCommandInfo* info = &(iter->second.command->second);
+ if (info == nullptr) {
+ sif::debug << "PlocMPSoCHandler::sendFailureReport: Reply has no active command" << std::endl;
+ return;
+ }
+ if (info->sendReplyTo != NO_COMMANDER) {
+ actionHelper.finish(false, info->sendReplyTo, iter->first, status);
+ }
+ info->isExecuting = false;
+}
+
+void PlocMpsocHandler::disableExeReportReply() {
+ DeviceReplyIter iter = deviceReplyMap.find(mpsoc::EXE_REPORT);
+ DeviceReplyInfo* info = &(iter->second);
+ info->delayCycles = 0;
+ info->command = deviceCommandMap.end();
+ /* Expected replies is set to one here. The value will be set to 0 in replyToReply() */
+ info->command->second.expectedReplies = 0;
+}
+
+void PlocMpsocHandler::stopSpecialComHelper() {
+ if (specialComHelper != nullptr) {
+ specialComHelper->stopProcess();
+ }
+ specialComHelperExecuting = false;
+}
+
+bool PlocMpsocHandler::handleHwStartup() {
+#if OBSW_MPSOC_JTAG_BOOT == 1
+ uartIsolatorSwitch.pullHigh();
+ startupState = StartupState::WAIT_CYCLES;
+ return true;
+#endif
+ if (powerState == PowerState::IDLE) {
+ if (skipSupvCommandingToOn) {
+ powerState = PowerState::DONE;
+ } else {
+ if (supv::SUPV_ON) {
+ commandActionHelper.commandAction(supervisorHandler, supv::START_MPSOC);
+ supvTransitionCd.resetTimer();
+ powerState = PowerState::PENDING_STARTUP;
+ } else {
+ triggerEvent(SUPV_NOT_ON, 1);
+ // Set back to OFF for now, failing the transition.
+ setMode(MODE_OFF);
+ }
+ }
+ }
+ if (powerState == PowerState::SUPV_FAILED) {
+ setMode(MODE_OFF);
+ powerState = PowerState::IDLE;
+ return false;
+ }
+ if (powerState == PowerState::PENDING_STARTUP) {
+ if (supvTransitionCd.hasTimedOut()) {
+ // Process with transition nonetheless..
+ triggerEvent(SUPV_REPLY_TIMEOUT);
+ powerState = PowerState::DONE;
+ } else {
+ return false;
+ }
+ }
+ if (powerState == PowerState::DONE) {
+ if (mpsocBootTransitionCd.hasTimedOut()) {
+ // Wait a bit for the MPSoC to fully boot.
+ uartIsolatorSwitch.pullHigh();
+ powerState = PowerState::IDLE;
+ } else {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool PlocMpsocHandler::handleHwShutdown() {
+ stopSpecialComHelper();
+ uartIsolatorSwitch.pullLow();
+#if OBSW_MPSOC_JTAG_BOOT == 1
+ powerState = PowerState::DONE;
+ return true;
+#endif
+
+ if (powerState == PowerState::IDLE) {
+ if (supv::SUPV_ON) {
+ commandActionHelper.commandAction(supervisorHandler, supv::SHUTDOWN_MPSOC);
+ supvTransitionCd.resetTimer();
+ powerState = PowerState::PENDING_SHUTDOWN;
+ } else {
+ triggerEvent(SUPV_NOT_ON, 0);
+ powerState = PowerState::DONE;
+ }
+ }
+ if (powerState == PowerState::PENDING_SHUTDOWN) {
+ if (supvTransitionCd.hasTimedOut()) {
+ powerState = PowerState::DONE;
+ // Process with transition nonetheless..
+ triggerEvent(SUPV_REPLY_TIMEOUT);
+ return true;
+ } else {
+ // Wait till power state is OFF.
+ return false;
+ }
+ }
+ return true;
+}
+
+void PlocMpsocHandler::handleActionCommandFailure(ActionId_t actionId) {
+ switch (actionId) {
+ case supv::ACK_REPORT:
+ case supv::EXE_REPORT:
+ break;
+ default:
+ sif::warning << "PlocMPSoCHandler::handleActionCommandFailure: Did not expect the action ID "
+ << actionId << std::endl;
+ return;
+ }
+ switch (powerState) {
+ case PowerState::PENDING_STARTUP: {
+ sif::info << "PlocMPSoCHandler::handleActionCommandFailure: MPSoC boot command failed"
+ << std::endl;
+ // This is commonly the case when the MPSoC is already operational. Thus the power state is
+ // set to on here
+ break;
+ }
+ case PowerState::PENDING_SHUTDOWN: {
+ // FDIR will intercept event and switch PLOC power off
+ triggerEvent(MPSOC_SHUTDOWN_FAILED);
+ sif::warning << "PlocMPSoCHandler::handleActionCommandFailure: Failed to shutdown MPSoC"
+ << std::endl;
+ break;
+ }
+ default:
+ break;
+ }
+ powerState = PowerState::SUPV_FAILED;
+ return;
+}
+
+LocalPoolDataSetBase* PlocMpsocHandler::getDataSetHandle(sid_t sid) {
+ if (sid == hkReport.getSid()) {
+ return &hkReport;
+ }
+ return nullptr;
+}
+
+bool PlocMpsocHandler::dontCheckQueue() {
+ // The TC and TMs need to be handled strictly sequentially, so while a command is pending,
+ // more specifically while replies are still expected, do not check the queue.
+ return commandIsPending;
+}
+
+void PlocMpsocHandler::cmdDoneHandler(bool success, ReturnValue_t result) {
+ commandIsPending = false;
+ auto commandIter = deviceCommandMap.find(getPendingCommand());
+ if (commandIter != deviceCommandMap.end()) {
+ commandIter->second.isExecuting = false;
+ if (commandIter->second.sendReplyTo != MessageQueueIF::NO_QUEUE) {
+ actionHelper.finish(success, commandIter->second.sendReplyTo, getPendingCommand(), result);
+ }
+ }
+ disableAllReplies();
+}
+
+ReturnValue_t PlocMpsocHandler::checkModeCommand(Mode_t commandedMode, Submode_t commandedSubmode,
+ uint32_t* msToReachTheMode) {
+ if (commandedMode != MODE_OFF) {
+ PoolReadGuard pg(&enablePl);
+ if (pg.getReadResult() == returnvalue::OK) {
+ if (enablePl.plUseAllowed.isValid() and not enablePl.plUseAllowed.value) {
+ return NON_OP_STATE_OF_CHARGE;
+ }
+ }
+ }
+ return DeviceHandlerBase::checkModeCommand(commandedMode, commandedSubmode, msToReachTheMode);
+}
+
+ReturnValue_t PlocMpsocHandler::getParameter(uint8_t domainId, uint8_t uniqueId,
+ ParameterWrapper* parameterWrapper,
+ const ParameterWrapper* newValues,
+ uint16_t startAtIndex) {
+ if (uniqueId == mpsoc::ParamId::SKIP_SUPV_ON_COMMANDING) {
+ uint8_t value = 0;
+ newValues->getElement(&value);
+ if (value > 1) {
+ return HasParametersIF::INVALID_VALUE;
+ }
+ parameterWrapper->set(skipSupvCommandingToOn);
+ return returnvalue::OK;
+ }
+ return DeviceHandlerBase::getParameter(domainId, uniqueId, parameterWrapper, newValues,
+ startAtIndex);
+}
diff --git a/archive/PlocMpsocHandler.h b/archive/PlocMpsocHandler.h
new file mode 100644
index 0000000..f3f2485
--- /dev/null
+++ b/archive/PlocMpsocHandler.h
@@ -0,0 +1,322 @@
+#ifndef BSP_Q7S_DEVICES_PLOC_PLOCMPSOCHANDLER_H_
+#define BSP_Q7S_DEVICES_PLOC_PLOCMPSOCHANDLER_H_
+
+#include
+#include
+#include
+#include
+
+#include "fsfw/action/CommandActionHelper.h"
+#include "fsfw/action/CommandsActionsIF.h"
+#include "fsfw/devicehandlers/DeviceHandlerBase.h"
+#include "fsfw/tmtcservices/SourceSequenceCounter.h"
+#include "fsfw_hal/linux/gpio/Gpio.h"
+#include "fsfw_hal/linux/serial/SerialComIF.h"
+
+static constexpr bool DEBUG_MPSOC_COMMUNICATION = true;
+
+/**
+ * @brief This is the device handler for the MPSoC of the payload computer.
+ *
+ * @details The PLOC uses the space packet protocol for communication. Each command will be
+ * answered with at least one acknowledgment and one execution report.
+ * Flight manual:
+ * https://egit.irs.uni-stuttgart.de/redmine/projects/eive-flight-manual/wiki/PLOC_MPSoC ICD:
+ * https://eive-cloud.irs.uni-stuttgart.de/index.php/apps/files/?dir=/EIVE_TAS-ILH-IRS/ICD-PLOC/ILH&fileid=1030263
+ *
+ * @note The sequence count in the space packets must be incremented with each received and sent
+ * packet otherwise the MPSoC will reply with an acknowledgment failure report.
+ *
+ * NOTE: This is not an example for a good device handler, DO NOT USE THIS AS A REFERENCE HANDLER.
+ * @author J. Meier, R. Mueller
+ */
+class PlocMpsocHandler : public DeviceHandlerBase, public CommandsActionsIF {
+ public:
+ /**
+ * @brief Constructor
+ *
+ * @param ojectId Object ID of the MPSoC handler
+ * @param uartcomIFid Object ID of the UART communication interface
+ * @param comCookie UART communication cookie
+ * @param plocMPSoCHelper Pointer to MPSoC helper object
+ * @param uartIsolatorSwitch Gpio object representing the GPIO connected to the UART isolator
+ * module in the programmable logic
+ * @param supervisorHandler Object ID of the supervisor handler
+ */
+ PlocMpsocHandler(object_id_t objectId, object_id_t uartComIFid, CookieIF* comCookie,
+ PlocMpsocSpecialComHelperLegacy* plocMPSoCHelper, Gpio uartIsolatorSwitch,
+ object_id_t supervisorHandler);
+ virtual ~PlocMpsocHandler();
+ virtual ReturnValue_t initialize() override;
+ ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy,
+ const uint8_t* data, size_t size) override;
+ void performOperationHook() override;
+ MessageQueueIF* getCommandQueuePtr() override;
+ void stepSuccessfulReceived(ActionId_t actionId, uint8_t step) override;
+ void stepFailedReceived(ActionId_t actionId, uint8_t step, ReturnValue_t returnCode) override;
+ void dataReceived(ActionId_t actionId, const uint8_t* data, uint32_t size) override;
+ void completionSuccessfulReceived(ActionId_t actionId) override;
+ void completionFailedReceived(ActionId_t actionId, ReturnValue_t returnCode) override;
+
+ protected:
+ void doStartUp() override;
+ void doShutDown() override;
+ ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t* id) override;
+ ReturnValue_t buildTransitionDeviceCommand(DeviceCommandId_t* id) override;
+ void fillCommandAndReplyMap() override;
+ ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand, const uint8_t* commandData,
+ size_t commandDataLen) override;
+ ReturnValue_t scanForReply(const uint8_t* start, size_t remainingSize, DeviceCommandId_t* foundId,
+ size_t* foundLen) override;
+ ReturnValue_t interpretDeviceReply(DeviceCommandId_t id, const uint8_t* packet) override;
+ void setNormalDatapoolEntriesInvalid() override;
+ uint32_t getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) override;
+ ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
+ LocalDataPoolManager& poolManager) override;
+ ReturnValue_t enableReplyInReplyMap(DeviceCommandMap::iterator command,
+ uint8_t expectedReplies = 1, bool useAlternateId = false,
+ DeviceCommandId_t alternateReplyID = 0) override;
+ size_t getNextReplyLength(DeviceCommandId_t deviceCommand) override;
+ ReturnValue_t doSendReadHook() override;
+ LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override;
+ bool dontCheckQueue() override;
+
+ private:
+ static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::PLOC_MPSOC_HANDLER;
+
+ //! [EXPORT] : [COMMENT] PLOC crc failure in telemetry packet
+ static const Event MEMORY_READ_RPT_CRC_FAILURE = MAKE_EVENT(1, severity::LOW);
+ //! [EXPORT] : [COMMENT] PLOC receive acknowledgment failure report
+ //! P1: Command Id which leads the acknowledgment failure report
+ //! P2: The status field inserted by the MPSoC into the data field
+ static const Event ACK_FAILURE = MAKE_EVENT(2, severity::LOW);
+ //! [EXPORT] : [COMMENT] PLOC receive execution failure report
+ //! P1: Command Id which leads the execution failure report
+ //! P2: The status field inserted by the MPSoC into the data field
+ static const Event EXE_FAILURE = MAKE_EVENT(3, severity::LOW);
+ //! [EXPORT] : [COMMENT] PLOC reply has invalid crc
+ static const Event MPSOC_HANDLER_CRC_FAILURE = MAKE_EVENT(4, severity::LOW);
+ //! [EXPORT] : [COMMENT] Packet sequence count in received space packet does not match expected
+ //! count P1: Expected sequence count P2: Received sequence count
+ static const Event MPSOC_HANDLER_SEQUENCE_COUNT_MISMATCH = MAKE_EVENT(5, severity::LOW);
+ //! [EXPORT] : [COMMENT] Supervisor fails to shutdown MPSoC. Requires to power off the PLOC and
+ //! thus also to shutdown the supervisor.
+ static const Event MPSOC_SHUTDOWN_FAILED = MAKE_EVENT(6, severity::HIGH);
+ //! [EXPORT] : [COMMENT] SUPV not on for boot or shutdown process. P1: 0 for OFF transition, 1 for
+ //! ON transition.
+ static constexpr Event SUPV_NOT_ON = event::makeEvent(SUBSYSTEM_ID, 7, severity::LOW);
+ static constexpr Event SUPV_REPLY_TIMEOUT = event::makeEvent(SUBSYSTEM_ID, 8, severity::LOW);
+
+ static const uint16_t APID_MASK = 0x7FF;
+ static const uint16_t PACKET_SEQUENCE_COUNT_MASK = 0x3FFF;
+
+ mpsoc::HkReport hkReport;
+ Countdown mpsocBootTransitionCd = Countdown(6500);
+ Countdown supvTransitionCd = Countdown(3000);
+
+ MessageQueueIF* eventQueue = nullptr;
+ MessageQueueIF* commandActionHelperQueue = nullptr;
+
+ SourceSequenceCounter sequenceCount = SourceSequenceCounter(0);
+
+ uint8_t commandBuffer[mpsoc::MAX_COMMAND_SIZE];
+ SpacePacketCreator creator;
+ ploc::SpTcParams spParams = ploc::SpTcParams(creator);
+
+ PoolEntry peStatus = PoolEntry();
+ PoolEntry peMode = PoolEntry();
+ PoolEntry peDownlinkPwrOn = PoolEntry();
+ PoolEntry peDownlinkReplyActive = PoolEntry();
+ PoolEntry peDownlinkJesdSyncStatus = PoolEntry();
+ PoolEntry peDownlinkDacStatus = PoolEntry();
+ PoolEntry peCameraStatus = PoolEntry();
+ PoolEntry peCameraSdiStatus = PoolEntry();
+ PoolEntry peCameraFpgaTemp = PoolEntry();
+ PoolEntry peCameraSocTemp = PoolEntry();
+ PoolEntry peSysmonTemp = PoolEntry();
+ PoolEntry peSysmonVccInt = PoolEntry();
+ PoolEntry peSysmonVccAux = PoolEntry();
+ PoolEntry peSysmonVccBram = PoolEntry();
+ PoolEntry peSysmonVccPaux = PoolEntry();
+ PoolEntry peSysmonVccPint = PoolEntry();
+ PoolEntry peSysmonVccPdro = PoolEntry();
+ PoolEntry peSysmonMb12V = PoolEntry();
+ PoolEntry peSysmonMb3V3 = PoolEntry();
+ PoolEntry peSysmonMb1V8 = PoolEntry();
+ PoolEntry peSysmonVcc12V = PoolEntry();
+ PoolEntry peSysmonVcc5V = PoolEntry();
+ PoolEntry peSysmonVcc3V3 = PoolEntry();
+ PoolEntry peSysmonVcc3V3VA = PoolEntry();
+ PoolEntry peSysmonVcc2V5DDR = PoolEntry();
+ PoolEntry peSysmonVcc1V2DDR = PoolEntry();
+ PoolEntry peSysmonVcc0V9 = PoolEntry();
+ PoolEntry peSysmonVcc0V6VTT = PoolEntry();
+ PoolEntry peSysmonSafeCotsCur = PoolEntry();
+ PoolEntry peSysmonNvm4XoCur = PoolEntry();
+ PoolEntry peSemUncorrectableErrs = PoolEntry();
+ PoolEntry peSemCorrectableErrs = PoolEntry();
+ PoolEntry peSemStatus = PoolEntry();
+ PoolEntry peRebootMpsocRequired = PoolEntry();
+
+ /**
+ * This variable is used to store the id of the next reply to receive. This is necessary
+ * because the PLOC sends as reply to each command at least one acknowledgment and execution
+ * report.
+ */
+ DeviceCommandId_t nextReplyId = mpsoc::NONE;
+
+ SerialComIF* uartComIf = nullptr;
+
+ PlocMpsocSpecialComHelperLegacy* specialComHelper = nullptr;
+ Gpio uartIsolatorSwitch;
+ object_id_t supervisorHandler = 0;
+ CommandActionHelper commandActionHelper;
+
+ // Used to block incoming commands when MPSoC helper class is currently executing a command
+ bool specialComHelperExecuting = false;
+ bool commandIsPending = false;
+
+ struct TmMemReadReport {
+ static const uint8_t FIX_SIZE = 14;
+ size_t rememberRequestedSize = 0;
+ };
+
+ TmMemReadReport tmMemReadReport;
+ Countdown cmdCountdown = Countdown(15000);
+
+ struct TelemetryBuffer {
+ uint16_t length = 0;
+ uint8_t buffer[mpsoc::SP_MAX_SIZE];
+ };
+
+ size_t foundPacketLen = 0;
+ TelemetryBuffer tmBuffer;
+
+ enum class StartupState { IDLE, HW_INIT, DONE } startupState = StartupState::IDLE;
+ enum class PowerState { IDLE, PENDING_STARTUP, PENDING_SHUTDOWN, SUPV_FAILED, DONE };
+
+ PowerState powerState = PowerState::IDLE;
+
+ uint8_t skipSupvCommandingToOn = false;
+
+ /**
+ * @brief Handles events received from the PLOC MPSoC helper
+ */
+ void handleEvent(EventMessage* eventMessage);
+
+ ReturnValue_t prepareTcMemWrite(const uint8_t* commandData, size_t commandDataLen);
+ ReturnValue_t prepareTcMemRead(const uint8_t* commandData, size_t commandDataLen);
+ ReturnValue_t prepareTcFlashDelete(const uint8_t* commandData, size_t commandDataLen);
+ ReturnValue_t prepareTcReplayStart(const uint8_t* commandData, size_t commandDataLen);
+ ReturnValue_t prepareTcReplayStop();
+ ReturnValue_t prepareTcDownlinkPwrOn(const uint8_t* commandData, size_t commandDataLen);
+ ReturnValue_t prepareTcDownlinkPwrOff();
+ ReturnValue_t prepareTcGetHkReport();
+ ReturnValue_t prepareTcGetDirContent(const uint8_t* commandData, size_t commandDataLen);
+ ReturnValue_t prepareTcReplayWriteSequence(const uint8_t* commandData, size_t commandDataLen);
+ ReturnValue_t prepareTcCamCmdSend(const uint8_t* commandData, size_t commandDataLen);
+ ReturnValue_t prepareTcModeIdle();
+ ReturnValue_t prepareTcCamTakePic(const uint8_t* commandData, size_t commandDataLen);
+ ReturnValue_t prepareTcSimplexSendFile(const uint8_t* commandData, size_t commandDataLen);
+ ReturnValue_t prepareTcDownlinkDataModulate(const uint8_t* commandData, size_t commandDataLen);
+ ReturnValue_t prepareTcModeSnapshot();
+ ReturnValue_t finishTcPrep(mpsoc::TcBase& tcBase);
+
+ /**
+ * @brief This function checks the crc of the received PLOC reply.
+ *
+ * @param start Pointer to the first byte of the reply.
+ * @param foundLen Pointer to the length of the whole packet.
+ *
+ * @return returnvalue::OK if CRC is ok, otherwise CRC_FAILURE.
+ */
+ ReturnValue_t verifyPacket(const uint8_t* start, size_t foundLen);
+
+ /**
+ * @brief This function handles the acknowledgment report.
+ *
+ * @param data Pointer to the data holding the acknowledgment report.
+ *
+ * @return returnvalue::OK if successful, otherwise an error code.
+ */
+ ReturnValue_t handleAckReport(const uint8_t* data);
+
+ /**
+ * @brief This function handles the data of a execution report.
+ *
+ * @param data Pointer to the received data packet.
+ *
+ * @return returnvalue::OK if successful, otherwise an error code.
+ */
+ ReturnValue_t handleExecutionReport(const uint8_t* data);
+
+ /**
+ * @brief This function handles the memory read report.
+ *
+ * @param data Pointer to the data buffer holding the memory read report.
+ *
+ * @return returnvalue::OK if successful, otherwise an error code.
+ */
+ ReturnValue_t handleMemoryReadReport(const uint8_t* data);
+
+ ReturnValue_t handleGetHkReport(const uint8_t* data);
+ ReturnValue_t handleCamCmdRpt(const uint8_t* data);
+
+ /**
+ * @brief Depending on the current active command, this function sets the reply id of the
+ * next reply after a successful acknowledgment report has been received. This is
+ * required by the function getNextReplyLength() to identify the length of the next
+ * reply to read.
+ */
+ void setNextReplyId();
+
+ /**
+ * @brief This function handles action message replies in case the telemetry has been
+ * requested by another object.
+ *
+ * @param data Pointer to the telemetry data.
+ * @param dataSize Size of telemetry in bytes.
+ * @param replyId Id of the reply. This will be added to the ActionMessage.
+ */
+ void handleDeviceTm(const uint8_t* data, size_t dataSize, DeviceCommandId_t replyId);
+
+ /**
+ * @brief In case an acknowledgment failure reply has been received this function disables
+ * all previously enabled commands and resets the exepected replies variable of an
+ * active command.
+ */
+ void disableAllReplies();
+
+ /**
+ * @brief This function sends a failure report if the active action was commanded by an other
+ * object.
+ *
+ * @param replyId The id of the reply which signals a failure.
+ * @param status A status byte which gives information about the failure type.
+ */
+ void sendFailureReport(DeviceCommandId_t replyId, ReturnValue_t status);
+
+ /**
+ * @brief This function disables the execution report reply. Within this function also the
+ * the variable expectedReplies of an active command will be set to 0.
+ */
+ void disableExeReportReply();
+
+ ReturnValue_t prepareTcModeReplay();
+
+ void cmdDoneHandler(bool success, ReturnValue_t result);
+ bool handleHwStartup();
+ bool handleHwShutdown();
+ void stopSpecialComHelper();
+
+ void handleActionCommandFailure(ActionId_t actionId);
+
+ pwrctrl::EnablePl enablePl = pwrctrl::EnablePl(objects::POWER_CONTROLLER);
+ ReturnValue_t checkModeCommand(Mode_t commandedMode, Submode_t commandedSubmode,
+ uint32_t* msToReachTheMode) override;
+
+ ReturnValue_t getParameter(uint8_t domainId, uint8_t uniqueId, ParameterWrapper* parameterWrapper,
+ const ParameterWrapper* newValues, uint16_t startAtIndex) override;
+};
+
+#endif /* BSP_Q7S_DEVICES_PLOC_PLOCMPSOCHANDLER_H_ */
diff --git a/archive/PlocMpsocSpecialComHelperLegacy.cpp b/archive/PlocMpsocSpecialComHelperLegacy.cpp
new file mode 100644
index 0000000..5dcaeb1
--- /dev/null
+++ b/archive/PlocMpsocSpecialComHelperLegacy.cpp
@@ -0,0 +1,545 @@
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#ifdef XIPHOS_Q7S
+#include "bsp_q7s/fs/FilesystemHelper.h"
+#endif
+
+#include "mission/utility/Timestamp.h"
+
+using namespace ploc;
+
+PlocMpsocSpecialComHelperLegacy::PlocMpsocSpecialComHelperLegacy(object_id_t objectId)
+ : SystemObject(objectId) {
+ spParams.buf = commandBuffer;
+ spParams.maxSize = sizeof(commandBuffer);
+}
+
+PlocMpsocSpecialComHelperLegacy::~PlocMpsocSpecialComHelperLegacy() {}
+
+ReturnValue_t PlocMpsocSpecialComHelperLegacy::initialize() {
+#ifdef XIPHOS_Q7S
+ sdcMan = SdCardManager::instance();
+ if (sdcMan == nullptr) {
+ sif::warning << "PlocMPSoCHelper::initialize: Invalid SD Card Manager" << std::endl;
+ return returnvalue::FAILED;
+ }
+#endif
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocSpecialComHelperLegacy::performOperation(uint8_t operationCode) {
+ ReturnValue_t result = returnvalue::OK;
+ semaphore.acquire();
+ while (true) {
+#if OBSW_THREAD_TRACING == 1
+ trace::threadTrace(opCounter, "PLOC MPSOC Helper");
+#endif
+ switch (internalState) {
+ case InternalState::IDLE: {
+ semaphore.acquire();
+ break;
+ }
+ case InternalState::FLASH_WRITE: {
+ result = performFlashWrite();
+ if (result == returnvalue::OK) {
+ triggerEvent(MPSOC_FLASH_WRITE_SUCCESSFUL, sequenceCount->get());
+ } else {
+ triggerEvent(MPSOC_FLASH_WRITE_FAILED, sequenceCount->get());
+ }
+ internalState = InternalState::IDLE;
+ break;
+ }
+ case InternalState::FLASH_READ: {
+ result = performFlashRead();
+ if (result == returnvalue::OK) {
+ triggerEvent(MPSOC_FLASH_READ_SUCCESSFUL, sequenceCount->get());
+ } else {
+ triggerEvent(MPSOC_FLASH_READ_FAILED, sequenceCount->get());
+ }
+ internalState = InternalState::IDLE;
+ break;
+ }
+ default:
+ sif::debug << "PlocMPSoCHelper::performOperation: Invalid state" << std::endl;
+ break;
+ }
+ }
+}
+
+ReturnValue_t PlocMpsocSpecialComHelperLegacy::setComIF(
+ DeviceCommunicationIF* communicationInterface_) {
+ uartComIF = dynamic_cast(communicationInterface_);
+ if (uartComIF == nullptr) {
+ sif::warning << "PlocMPSoCHelper::initialize: Invalid uart com if" << std::endl;
+ return returnvalue::FAILED;
+ }
+ return returnvalue::OK;
+}
+
+void PlocMpsocSpecialComHelperLegacy::setComCookie(CookieIF* comCookie_) { comCookie = comCookie_; }
+
+void PlocMpsocSpecialComHelperLegacy::setSequenceCount(SourceSequenceCounter* sequenceCount_) {
+ sequenceCount = sequenceCount_;
+}
+
+ReturnValue_t PlocMpsocSpecialComHelperLegacy::startFlashWrite(std::string obcFile,
+ std::string mpsocFile) {
+ if (internalState != InternalState::IDLE) {
+ return returnvalue::FAILED;
+ }
+ ReturnValue_t result = startFlashReadOrWriteBase(std::move(obcFile), std::move(mpsocFile));
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ internalState = InternalState::FLASH_WRITE;
+ return semaphore.release();
+}
+
+ReturnValue_t PlocMpsocSpecialComHelperLegacy::startFlashRead(std::string obcFile,
+ std::string mpsocFile,
+ size_t readFileSize) {
+ if (internalState != InternalState::IDLE) {
+ return returnvalue::FAILED;
+ }
+ ReturnValue_t result = startFlashReadOrWriteBase(std::move(obcFile), std::move(mpsocFile));
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ flashReadAndWrite.totalReadSize = readFileSize;
+ internalState = InternalState::FLASH_READ;
+ return semaphore.release();
+}
+
+void PlocMpsocSpecialComHelperLegacy::resetHelper() {
+ spParams.buf = commandBuffer;
+ terminate = false;
+ uartComIF->flushUartRxBuffer(comCookie);
+}
+
+void PlocMpsocSpecialComHelperLegacy::stopProcess() { terminate = true; }
+
+ReturnValue_t PlocMpsocSpecialComHelperLegacy::performFlashWrite() {
+ ReturnValue_t result = returnvalue::OK;
+ std::ifstream file(flashReadAndWrite.obcFile, std::ifstream::binary);
+ if (file.bad()) {
+ return returnvalue::FAILED;
+ }
+ result = flashfopen(mpsoc::FileAccessModes::WRITE | mpsoc::FileAccessModes::OPEN_ALWAYS);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ // Set position of next character to end of file input stream
+ file.seekg(0, file.end);
+ // tellg returns position of character in input stream
+ size_t remainingSize = file.tellg();
+ size_t dataLength = 0;
+ size_t bytesRead = 0;
+ while (remainingSize > 0) {
+ if (terminate) {
+ return returnvalue::OK;
+ }
+ // The minus 4 is necessary for unknown reasons. Maybe some bug in the ILH software?
+ if (remainingSize > mpsoc::MAX_FLASH_WRITE_DATA_SIZE - 4) {
+ dataLength = mpsoc::MAX_FLASH_WRITE_DATA_SIZE - 4;
+ } else {
+ dataLength = remainingSize;
+ }
+ if (file.bad() or not file.is_open()) {
+ return FILE_WRITE_ERROR;
+ }
+ file.seekg(bytesRead, file.beg);
+ file.read(reinterpret_cast(fileBuf.data()), dataLength);
+ bytesRead += dataLength;
+ remainingSize -= dataLength;
+ mpsoc::TcFlashWrite tc(spParams, *sequenceCount);
+ result = tc.setPayload(fileBuf.data(), dataLength);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = tc.finishPacket();
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ (*sequenceCount)++;
+ result = handlePacketTransmissionNoReply(tc);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ }
+ result = flashfclose();
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ return result;
+}
+
+ReturnValue_t PlocMpsocSpecialComHelperLegacy::performFlashRead() {
+ std::error_code e;
+ std::ofstream ofile(flashReadAndWrite.obcFile, std::ios::trunc | std::ios::binary);
+ if (ofile.bad()) {
+ return returnvalue::FAILED;
+ }
+ ReturnValue_t result = flashfopen(mpsoc::FileAccessModes::READ);
+ if (result != returnvalue::OK) {
+ std::filesystem::remove(flashReadAndWrite.obcFile, e);
+ return result;
+ }
+ size_t readSoFar = 0;
+ size_t nextReadSize = mpsoc::MAX_FLASH_READ_DATA_SIZE;
+ while (readSoFar < flashReadAndWrite.totalReadSize) {
+ if (terminate) {
+ std::filesystem::remove(flashReadAndWrite.obcFile, e);
+ return returnvalue::OK;
+ }
+ nextReadSize = mpsoc::MAX_FLASH_READ_DATA_SIZE;
+ if (flashReadAndWrite.totalReadSize - readSoFar < mpsoc::MAX_FLASH_READ_DATA_SIZE) {
+ nextReadSize = flashReadAndWrite.totalReadSize - readSoFar;
+ }
+ if (ofile.bad() or not ofile.is_open()) {
+ std::filesystem::remove(flashReadAndWrite.obcFile, e);
+ return FILE_READ_ERROR;
+ }
+ mpsoc::TcFlashRead flashReadRequest(spParams, *sequenceCount);
+ result = flashReadRequest.setPayload(nextReadSize);
+ if (result != returnvalue::OK) {
+ std::filesystem::remove(flashReadAndWrite.obcFile, e);
+ return result;
+ }
+ result = flashReadRequest.finishPacket();
+ if (result != returnvalue::OK) {
+ std::filesystem::remove(flashReadAndWrite.obcFile, e);
+ return result;
+ }
+ (*sequenceCount)++;
+ result = handlePacketTransmissionFlashRead(flashReadRequest, ofile, nextReadSize);
+ if (result != returnvalue::OK) {
+ std::filesystem::remove(flashReadAndWrite.obcFile, e);
+ return result;
+ }
+ readSoFar += nextReadSize;
+ }
+ result = flashfclose();
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ return result;
+}
+
+ReturnValue_t PlocMpsocSpecialComHelperLegacy::flashfopen(uint8_t mode) {
+ spParams.buf = commandBuffer;
+ mpsoc::FlashFopen flashFopen(spParams, *sequenceCount);
+ ReturnValue_t result = flashFopen.setPayload(flashReadAndWrite.mpsocFile, mode);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = flashFopen.finishPacket();
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ (*sequenceCount)++;
+ result = handlePacketTransmissionNoReply(flashFopen);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocSpecialComHelperLegacy::flashfclose() {
+ spParams.buf = commandBuffer;
+ mpsoc::FlashFclose flashFclose(spParams, *sequenceCount);
+ ReturnValue_t result = flashFclose.finishPacket();
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ (*sequenceCount)++;
+ result = handlePacketTransmissionNoReply(flashFclose);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ return result;
+}
+
+ReturnValue_t PlocMpsocSpecialComHelperLegacy::handlePacketTransmissionFlashRead(
+ mpsoc::TcFlashRead& tc, std::ofstream& ofile, size_t expectedReadLen) {
+ ReturnValue_t result = sendCommand(tc);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = handleAck();
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = handleTmReception();
+ if (result != returnvalue::OK) {
+ return result;
+ }
+
+ // We have the nominal case where the flash read report appears first, or the case where we
+ // get an EXE failure immediately.
+ if (spReader.getApid() == mpsoc::apid::TM_FLASH_READ_REPORT) {
+ result = handleFlashReadReply(ofile, expectedReadLen);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ return handleExe();
+ } else if (spReader.getApid() == mpsoc::apid::EXE_FAILURE) {
+ handleExeFailure();
+ } else {
+ triggerEvent(MPSOC_EXE_INVALID_APID, spReader.getApid(), static_cast(internalState));
+ sif::warning << "PLOC MPSoC: Expected execution report "
+ << "but received space packet with apid " << std::hex << spReader.getApid()
+ << std::endl;
+ }
+ return returnvalue::FAILED;
+}
+
+ReturnValue_t PlocMpsocSpecialComHelperLegacy::handlePacketTransmissionNoReply(ploc::SpTcBase& tc) {
+ ReturnValue_t result = sendCommand(tc);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = handleAck();
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ return handleExe();
+}
+
+ReturnValue_t PlocMpsocSpecialComHelperLegacy::sendCommand(ploc::SpTcBase& tc) {
+ ReturnValue_t result = returnvalue::OK;
+ result = uartComIF->sendMessage(comCookie, tc.getFullPacket(), tc.getFullPacketLen());
+ if (result != returnvalue::OK) {
+ sif::warning << "PlocMPSoCHelper::sendCommand: Failed to send command" << std::endl;
+ triggerEvent(MPSOC_SENDING_COMMAND_FAILED, result, static_cast(internalState));
+ return result;
+ }
+ return result;
+}
+
+ReturnValue_t PlocMpsocSpecialComHelperLegacy::handleAck() {
+ ReturnValue_t result = returnvalue::OK;
+ result = handleTmReception();
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = checkReceivedTm();
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ uint16_t apid = spReader.getApid();
+ if (apid != mpsoc::apid::ACK_SUCCESS) {
+ handleAckApidFailure(spReader);
+ return returnvalue::FAILED;
+ }
+ return returnvalue::OK;
+}
+
+void PlocMpsocSpecialComHelperLegacy::handleAckApidFailure(const ploc::SpTmReader& reader) {
+ uint16_t apid = reader.getApid();
+ if (apid == mpsoc::apid::ACK_FAILURE) {
+ uint16_t status = mpsoc::getStatusFromRawData(reader.getFullData());
+ sif::warning << "PLOC MPSoC ACK Failure: " << mpsoc::getStatusString(status) << std::endl;
+ triggerEvent(MPSOC_ACK_FAILURE_REPORT, static_cast(internalState), status);
+ } else {
+ triggerEvent(MPSOC_ACK_INVALID_APID, apid, static_cast(internalState));
+ sif::warning << "PlocMPSoCHelper::handleAckApidFailure: Expected acknowledgement report "
+ << "but received space packet with apid " << std::hex << apid << std::endl;
+ }
+}
+
+ReturnValue_t PlocMpsocSpecialComHelperLegacy::handleExe() {
+ ReturnValue_t result = returnvalue::OK;
+
+ result = handleTmReception();
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = checkReceivedTm();
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ uint16_t apid = spReader.getApid();
+ if (apid == mpsoc::apid::EXE_FAILURE) {
+ handleExeFailure();
+ return returnvalue::FAILED;
+ } else if (apid != mpsoc::apid::EXE_SUCCESS) {
+ triggerEvent(MPSOC_EXE_INVALID_APID, apid, static_cast(internalState));
+ sif::warning << "PLOC MPSoC: Expected execution report "
+ << "but received space packet with apid " << std::hex << apid << std::endl;
+ }
+ return returnvalue::OK;
+}
+
+void PlocMpsocSpecialComHelperLegacy::handleExeFailure() {
+ uint16_t status = mpsoc::getStatusFromRawData(spReader.getFullData());
+ sif::warning << "PLOC MPSoC EXE Failure: " << mpsoc::getStatusString(status) << std::endl;
+ triggerEvent(MPSOC_EXE_FAILURE_REPORT, static_cast(internalState));
+}
+
+ReturnValue_t PlocMpsocSpecialComHelperLegacy::handleTmReception() {
+ ReturnValue_t result = returnvalue::OK;
+ tmCountdown.resetTimer();
+ size_t readBytes = 0;
+ size_t currentBytes = 0;
+ uint32_t usleepDelay = 5;
+ size_t fullPacketLen = 0;
+ while (true) {
+ if (tmCountdown.hasTimedOut()) {
+ triggerEvent(MPSOC_READ_TIMEOUT, tmCountdown.getTimeoutMs());
+ return returnvalue::FAILED;
+ }
+ result = receive(tmBuf.data() + readBytes, 6, ¤tBytes);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ spReader.setReadOnlyData(tmBuf.data(), tmBuf.size());
+ fullPacketLen = spReader.getFullPacketLen();
+ readBytes += currentBytes;
+ if (readBytes == 6) {
+ break;
+ }
+ usleep(usleepDelay);
+ if (usleepDelay < 200000) {
+ usleepDelay *= 4;
+ }
+ }
+ while (true) {
+ if (tmCountdown.hasTimedOut()) {
+ triggerEvent(MPSOC_READ_TIMEOUT, tmCountdown.getTimeoutMs());
+ return returnvalue::FAILED;
+ }
+ result = receive(tmBuf.data() + readBytes, fullPacketLen - readBytes, ¤tBytes);
+ readBytes += currentBytes;
+ if (fullPacketLen == readBytes) {
+ break;
+ }
+ usleep(usleepDelay);
+ if (usleepDelay < 200000) {
+ usleepDelay *= 4;
+ }
+ }
+ // arrayprinter::print(tmBuf.data(), readBytes);
+ return result;
+}
+
+ReturnValue_t PlocMpsocSpecialComHelperLegacy::handleFlashReadReply(std::ofstream& ofile,
+ size_t expectedReadLen) {
+ ReturnValue_t result = checkReceivedTm();
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ uint16_t apid = spReader.getApid();
+ if (apid != mpsoc::apid::TM_FLASH_READ_REPORT) {
+ triggerEvent(MPSOC_FLASH_READ_PACKET_ERROR, FlashReadErrorType::FLASH_READ_APID_ERROR);
+ sif::warning << "PLOC MPSoC Flash Read: Unexpected APID" << std::endl;
+ return result;
+ }
+ const uint8_t* packetData = spReader.getPacketData();
+ size_t deserDummy = spReader.getPacketDataLen() - mpsoc::CRC_SIZE;
+ uint32_t receivedReadLen = 0;
+ // I think this is buggy, weird stuff in the short name field.
+ // std::string receivedShortName = std::string(reinterpret_cast(packetData), 12);
+ // if (receivedShortName != flashReadAndWrite.mpsocFile.substr(0, 11)) {
+ // sif::warning << "PLOC MPSoC Flash Read: Missmatch between request file name and "
+ // "received file name"
+ // << std::endl;
+ // triggerEvent(MPSOC_FLASH_READ_PACKET_ERROR, FlashReadErrorType::FLASH_READ_FILENAME_ERROR);
+ // return returnvalue::FAILED;
+ // }
+ packetData += 12;
+ result = SerializeAdapter::deSerialize(&receivedReadLen, &packetData, &deserDummy,
+ SerializeIF::Endianness::NETWORK);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ if (receivedReadLen != expectedReadLen) {
+ sif::warning << "PLOC MPSoC Flash Read: Missmatch between request read length and "
+ "received read length"
+ << std::endl;
+ triggerEvent(MPSOC_FLASH_READ_PACKET_ERROR, FlashReadErrorType::FLASH_READ_READLEN_ERROR);
+ return returnvalue::FAILED;
+ }
+ ofile.write(reinterpret_cast(packetData), receivedReadLen);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocSpecialComHelperLegacy::fileCheck(std::string obcFile) {
+#ifdef XIPHOS_Q7S
+ ReturnValue_t result = FilesystemHelper::checkPath(obcFile);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+#elif defined(TE0720_1CFA)
+ if (not std::filesystem::exists(obcFile)) {
+ sif::warning << "PlocMPSoCHelper::startFlashWrite: File " << obcFile << "does not exist"
+ << std::endl;
+ return returnvalue::FAILED;
+ }
+#endif
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocSpecialComHelperLegacy::startFlashReadOrWriteBase(std::string obcFile,
+ std::string mpsocFile) {
+ ReturnValue_t result = fileCheck(obcFile);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+
+ flashReadAndWrite.obcFile = std::move(obcFile);
+ flashReadAndWrite.mpsocFile = std::move(mpsocFile);
+ resetHelper();
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocSpecialComHelperLegacy::checkReceivedTm() {
+ ReturnValue_t result = spReader.checkSize();
+ if (result != returnvalue::OK) {
+ sif::error << "PLOC MPSoC: Size check on received TM failed" << std::endl;
+ triggerEvent(MPSOC_TM_SIZE_ERROR);
+ return result;
+ }
+ result = spReader.checkCrc();
+ if (result != returnvalue::OK) {
+ sif::warning << "PLOC MPSoC: CRC check failed" << std::endl;
+ triggerEvent(MPSOC_TM_CRC_MISSMATCH, *sequenceCount);
+ return result;
+ }
+ uint16_t recvSeqCnt = spReader.getSequenceCount();
+ if (recvSeqCnt != *sequenceCount) {
+ triggerEvent(MPSOC_HELPER_SEQ_CNT_MISMATCH, *sequenceCount, recvSeqCnt);
+ *sequenceCount = recvSeqCnt;
+ }
+ // This sequence count ping pong does not make any sense but it is how the MPSoC expects it.
+ (*sequenceCount)++;
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocMpsocSpecialComHelperLegacy::receive(uint8_t* data, size_t requestBytes,
+ size_t* readBytes) {
+ ReturnValue_t result = returnvalue::OK;
+ uint8_t* buffer = nullptr;
+ result = uartComIF->requestReceiveMessage(comCookie, requestBytes);
+ if (result != returnvalue::OK) {
+ sif::warning << "PlocMPSoCHelper::receive: Failed to request reply" << std::endl;
+ triggerEvent(MPSOC_HELPER_REQUESTING_REPLY_FAILED, result,
+ static_cast(static_cast(internalState)));
+ return returnvalue::FAILED;
+ }
+ result = uartComIF->readReceivedMessage(comCookie, &buffer, readBytes);
+ if (result != returnvalue::OK) {
+ sif::warning << "PlocMPSoCHelper::receive: Failed to read received message" << std::endl;
+ triggerEvent(MPSOC_HELPER_READING_REPLY_FAILED, result, static_cast(internalState));
+ return returnvalue::FAILED;
+ }
+ if (*readBytes > 0) {
+ std::memcpy(data, buffer, *readBytes);
+ }
+ return result;
+}
diff --git a/archive/PlocMpsocSpecialComHelperLegacy.h b/archive/PlocMpsocSpecialComHelperLegacy.h
new file mode 100644
index 0000000..e3d5370
--- /dev/null
+++ b/archive/PlocMpsocSpecialComHelperLegacy.h
@@ -0,0 +1,200 @@
+#ifndef BSP_Q7S_DEVICES_PLOCMPSOCHELPER_H_
+#define BSP_Q7S_DEVICES_PLOCMPSOCHELPER_H_
+
+#include
+#include
+
+#include
+
+#include "OBSWConfig.h"
+#include "fsfw/devicehandlers/CookieIF.h"
+#include "fsfw/objectmanager/SystemObject.h"
+#include "fsfw/osal/linux/BinarySemaphore.h"
+#include "fsfw/returnvalues/returnvalue.h"
+#include "fsfw/tasks/ExecutableObjectIF.h"
+#include "fsfw/tmtcservices/SourceSequenceCounter.h"
+#include "fsfw_hal/linux/serial/SerialComIF.h"
+#ifdef XIPHOS_Q7S
+#include "bsp_q7s/fs/SdCardManager.h"
+#endif
+
+/**
+ * @brief Helper class for MPSoC of PLOC intended to accelerate large data transfers between
+ * MPSoC and OBC.
+ * @author J. Meier
+ */
+class PlocMpsocSpecialComHelperLegacy : public SystemObject, public ExecutableObjectIF {
+ public:
+ static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::PLOC_MPSOC_HELPER;
+
+ //! [EXPORT] : [COMMENT] Flash write fails
+ static const Event MPSOC_FLASH_WRITE_FAILED = MAKE_EVENT(0, severity::LOW);
+ //! [EXPORT] : [COMMENT] Flash write successful
+ static const Event MPSOC_FLASH_WRITE_SUCCESSFUL = MAKE_EVENT(1, severity::INFO);
+ //! [EXPORT] : [COMMENT] Communication interface returned failure when trying to send the command
+ //! to the MPSoC
+ //! P1: Return value returned by the communication interface sendMessage function
+ //! P2: Internal state of MPSoC helper
+ static const Event MPSOC_SENDING_COMMAND_FAILED = MAKE_EVENT(2, severity::LOW);
+ //! [EXPORT] : [COMMENT] Request receive message of communication interface failed
+ //! P1: Return value returned by the communication interface requestReceiveMessage function
+ //! P2: Internal state of MPSoC helper
+ static const Event MPSOC_HELPER_REQUESTING_REPLY_FAILED = MAKE_EVENT(3, severity::LOW);
+ //! [EXPORT] : [COMMENT] Reading receive message of communication interface failed
+ //! P1: Return value returned by the communication interface readingReceivedMessage function
+ //! P2: Internal state of MPSoC helper
+ static const Event MPSOC_HELPER_READING_REPLY_FAILED = MAKE_EVENT(4, severity::LOW);
+ //! [EXPORT] : [COMMENT] Did not receive acknowledgment report
+ //! P1: Number of bytes missing
+ //! P2: Internal state of MPSoC helper
+ static const Event MPSOC_MISSING_ACK = MAKE_EVENT(5, severity::LOW);
+ //! [EXPORT] : [COMMENT] Did not receive execution report
+ //! P1: Number of bytes missing
+ //! P2: Internal state of MPSoC helper
+ static const Event MPSOC_MISSING_EXE = MAKE_EVENT(6, severity::LOW);
+ //! [EXPORT] : [COMMENT] Received acknowledgment failure report
+ //! P1: Internal state of MPSoC
+ static const Event MPSOC_ACK_FAILURE_REPORT = MAKE_EVENT(7, severity::LOW);
+ //! [EXPORT] : [COMMENT] Received execution failure report
+ //! P1: Internal state of MPSoC
+ static const Event MPSOC_EXE_FAILURE_REPORT = MAKE_EVENT(8, severity::LOW);
+ //! [EXPORT] : [COMMENT] Expected acknowledgment report but received space packet with other apid
+ //! P1: Apid of received space packet
+ //! P2: Internal state of MPSoC
+ static const Event MPSOC_ACK_INVALID_APID = MAKE_EVENT(9, severity::LOW);
+ //! [EXPORT] : [COMMENT] Expected execution report but received space packet with other apid
+ //! P1: Apid of received space packet
+ //! P2: Internal state of MPSoC
+ static const Event MPSOC_EXE_INVALID_APID = MAKE_EVENT(10, severity::LOW);
+ //! [EXPORT] : [COMMENT] Received sequence count does not match expected sequence count
+ //! P1: Expected sequence count
+ //! P2: Received sequence count
+ static const Event MPSOC_HELPER_SEQ_CNT_MISMATCH = MAKE_EVENT(11, severity::LOW);
+ static const Event MPSOC_TM_SIZE_ERROR = MAKE_EVENT(12, severity::LOW);
+ static const Event MPSOC_TM_CRC_MISSMATCH = MAKE_EVENT(13, severity::LOW);
+ static const Event MPSOC_FLASH_READ_PACKET_ERROR = MAKE_EVENT(14, severity::LOW);
+ static const Event MPSOC_FLASH_READ_FAILED = MAKE_EVENT(15, severity::LOW);
+ static const Event MPSOC_FLASH_READ_SUCCESSFUL = MAKE_EVENT(16, severity::INFO);
+ static const Event MPSOC_READ_TIMEOUT = MAKE_EVENT(17, severity::LOW);
+
+ enum FlashReadErrorType : uint32_t {
+ FLASH_READ_APID_ERROR = 0,
+ FLASH_READ_FILENAME_ERROR = 1,
+ FLASH_READ_READLEN_ERROR = 2
+ };
+
+ PlocMpsocSpecialComHelperLegacy(object_id_t objectId);
+ virtual ~PlocMpsocSpecialComHelperLegacy();
+
+ ReturnValue_t initialize() override;
+ ReturnValue_t performOperation(uint8_t operationCode = 0) override;
+
+ ReturnValue_t setComIF(DeviceCommunicationIF* communicationInterface_);
+ void setComCookie(CookieIF* comCookie_);
+
+ /**
+ * @brief Starts flash write sequence
+ *
+ * @param obcFile File where to read from the data
+ * @param mpsocFile The file of the MPSoC where should be written to
+ *
+ * @return returnvalue::OK if successful, otherwise error return value
+ */
+ ReturnValue_t startFlashWrite(std::string obcFile, std::string mpsocFile);
+ /**
+ *
+ * @param obcFile Full target file name on OBC
+ * @param mpsocFile The file on the MPSoC which should be copied ot the OBC
+ * @param readFileSize The size of the file on the MPSoC.
+ * @return
+ */
+ ReturnValue_t startFlashRead(std::string obcFile, std::string mpsocFile, size_t readFileSize);
+
+ /**
+ * @brief Can be used to interrupt a running data transfer.
+ */
+ void stopProcess();
+
+ /**
+ * @brief Sets the sequence count object responsible for the sequence count handling
+ */
+ void setSequenceCount(SourceSequenceCounter* sequenceCount_);
+
+ private:
+ static const uint8_t INTERFACE_ID = CLASS_ID::PLOC_MPSOC_HELPER;
+
+ //! [EXPORT] : [COMMENT] File error occured for file transfers from OBC to the MPSoC.
+ static const ReturnValue_t FILE_WRITE_ERROR = MAKE_RETURN_CODE(0xA0);
+ //! [EXPORT] : [COMMENT] File error occured for file transfers from MPSoC to OBC.
+ static const ReturnValue_t FILE_READ_ERROR = MAKE_RETURN_CODE(0xA1);
+
+ // Maximum number of times the communication interface retries polling data from the reply
+ // buffer
+ static const int RETRIES = 10000;
+
+ struct FlashInfo {
+ std::string obcFile;
+ std::string mpsocFile;
+ };
+
+ struct FlashRead : public FlashInfo {
+ size_t totalReadSize = 0;
+ };
+
+ struct FlashRead flashReadAndWrite;
+#if OBSW_THREAD_TRACING == 1
+ uint32_t opCounter = 0;
+#endif
+
+ enum class InternalState { IDLE, FLASH_WRITE, FLASH_READ };
+
+ InternalState internalState = InternalState::IDLE;
+
+ BinarySemaphore semaphore;
+#ifdef XIPHOS_Q7S
+ SdCardManager* sdcMan = nullptr;
+#endif
+ uint8_t commandBuffer[mpsoc::MAX_COMMAND_SIZE];
+ SpacePacketCreator creator;
+ ploc::SpTcParams spParams = ploc::SpTcParams(creator);
+
+ Countdown tmCountdown = Countdown(5000);
+
+ std::array fileBuf{};
+ std::array tmBuf{};
+
+ bool terminate = false;
+
+ /**
+ * Communication interface of MPSoC responsible for low level access. Must be set by the
+ * MPSoC Handler.
+ */
+ SerialComIF* uartComIF = nullptr;
+ // Communication cookie. Must be set by the MPSoC Handler
+ CookieIF* comCookie = nullptr;
+ // Sequence count, must be set by Ploc MPSoC Handler
+ SourceSequenceCounter* sequenceCount = nullptr;
+ ploc::SpTmReader spReader;
+
+ void resetHelper();
+ ReturnValue_t performFlashWrite();
+ ReturnValue_t performFlashRead();
+ ReturnValue_t flashfopen(uint8_t accessMode);
+ ReturnValue_t flashfclose();
+ ReturnValue_t handlePacketTransmissionNoReply(ploc::SpTcBase& tc);
+ ReturnValue_t handlePacketTransmissionFlashRead(mpsoc::TcFlashRead& tc, std::ofstream& ofile,
+ size_t expectedReadLen);
+ ReturnValue_t handleFlashReadReply(std::ofstream& ofile, size_t expectedReadLen);
+ ReturnValue_t sendCommand(ploc::SpTcBase& tc);
+ ReturnValue_t receive(uint8_t* data, size_t requestBytes, size_t* readBytes);
+ ReturnValue_t handleAck();
+ ReturnValue_t handleExe();
+ ReturnValue_t startFlashReadOrWriteBase(std::string obcFile, std::string mpsocFile);
+ ReturnValue_t fileCheck(std::string obcFile);
+ void handleAckApidFailure(const ploc::SpTmReader& reader);
+ void handleExeFailure();
+ ReturnValue_t handleTmReception();
+ ReturnValue_t checkReceivedTm();
+};
+
+#endif /* BSP_Q7S_DEVICES_PLOCMPSOCHELPER_H_ */
diff --git a/archive/PlocSupervisorHandler.cpp b/archive/PlocSupervisorHandler.cpp
new file mode 100644
index 0000000..67ec707
--- /dev/null
+++ b/archive/PlocSupervisorHandler.cpp
@@ -0,0 +1,2027 @@
+#include "PlocSupervisorHandler.h"
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include "OBSWConfig.h"
+#include "eive/definitions.h"
+#include "fsfw/datapool/PoolReadGuard.h"
+#include "fsfw/globalfunctions/CRC.h"
+#include "fsfw/ipc/QueueFactory.h"
+#include "fsfw/timemanager/Clock.h"
+
+using namespace supv;
+using namespace returnvalue;
+
+PlocSupervisorHandler::PlocSupervisorHandler(object_id_t objectId, CookieIF* comCookie,
+ Gpio uartIsolatorSwitch, power::Switch_t powerSwitch,
+ PlocSupvUartManager& supvHelper)
+ : DeviceHandlerBase(objectId, supvHelper.getObjectId(), comCookie),
+ uartIsolatorSwitch(uartIsolatorSwitch),
+ hkset(this),
+ bootStatusReport(this),
+ latchupStatusReport(this),
+ countersReport(this),
+ adcReport(this),
+ powerSwitch(powerSwitch),
+ uartManager(supvHelper) {
+ if (comCookie == nullptr) {
+ sif::error << "PlocSupervisorHandler: Invalid com cookie" << std::endl;
+ }
+ spParams.buf = commandBuffer;
+ spParams.maxSize = sizeof(commandBuffer);
+ eventQueue = QueueFactory::instance()->createMessageQueue(EventMessage::EVENT_MESSAGE_SIZE * 5);
+}
+
+PlocSupervisorHandler::~PlocSupervisorHandler() {}
+
+ReturnValue_t PlocSupervisorHandler::initialize() {
+ ReturnValue_t result = returnvalue::OK;
+ result = DeviceHandlerBase::initialize();
+ if (result != returnvalue::OK) {
+ return result;
+ }
+#ifdef XIPHOS_Q7S
+ sdcMan = SdCardManager::instance();
+#endif /* TE0720_1CFA */
+
+ result = eventSubscription();
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ return result;
+}
+
+void PlocSupervisorHandler::performOperationHook() {
+ if (normalCommandIsPending and normalCmdCd.hasTimedOut()) {
+ // Event, FDIR, printout? Leads to spam though and normally should not happen..
+ normalCommandIsPending = false;
+ }
+ if (commandIsPending and cmdCd.hasTimedOut()) {
+ // Event, FDIR, printout? Leads to spam though and normally should not happen..
+ commandIsPending = false;
+
+ // if(iter->second.sendReplyTo != NO_COMMANDER) {
+ // actionHelper.finish(true, iter->second.sendReplyTo, iter->first, returnvalue::OK);
+ // }
+ disableAllReplies();
+ }
+ EventMessage event;
+ for (ReturnValue_t result = eventQueue->receiveMessage(&event); result == returnvalue::OK;
+ result = eventQueue->receiveMessage(&event)) {
+ switch (event.getMessageId()) {
+ case EventMessage::EVENT_MESSAGE:
+ handleEvent(&event);
+ break;
+ default:
+ sif::debug << "PlocSupervisorHandler::performOperationHook: Did not subscribe to this event"
+ << " message" << std::endl;
+ break;
+ }
+ }
+}
+
+ReturnValue_t PlocSupervisorHandler::executeAction(ActionId_t actionId,
+ MessageQueueId_t commandedBy,
+ const uint8_t* data, size_t size) {
+ using namespace supv;
+ ReturnValue_t result = returnvalue::OK;
+
+ switch (actionId) {
+ default:
+ break;
+ }
+
+ if (uartManager.longerRequestActive()) {
+ return result::SUPV_HELPER_EXECUTING;
+ }
+
+ result = acceptExternalDeviceCommands();
+ if (result != returnvalue::OK) {
+ return result;
+ }
+
+ switch (actionId) {
+ case PERFORM_UPDATE: {
+ if (size > config::MAX_PATH_SIZE + config::MAX_FILENAME_SIZE) {
+ return result::FILENAME_TOO_LONG;
+ }
+ shutdownCmdSent = false;
+ UpdateParams params;
+ result = extractUpdateCommand(data, size, params);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ result = uartManager.performUpdate(params);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ return EXECUTION_FINISHED;
+ }
+ case CONTINUE_UPDATE: {
+ shutdownCmdSent = false;
+ uartManager.initiateUpdateContinuation();
+ return EXECUTION_FINISHED;
+ }
+ case MEMORY_CHECK_WITH_FILE: {
+ shutdownCmdSent = false;
+ UpdateParams params;
+ result = extractBaseParams(&data, size, params);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ if (not std::filesystem::exists(params.file)) {
+ return HasFileSystemIF::FILE_DOES_NOT_EXIST;
+ }
+ uartManager.performMemCheck(params.file, params.memId, params.startAddr);
+ return EXECUTION_FINISHED;
+ }
+ default:
+ break;
+ }
+ return DeviceHandlerBase::executeAction(actionId, commandedBy, data, size);
+}
+
+void PlocSupervisorHandler::doStartUp() {
+ if (startupState == StartupState::OFF) {
+ bootTimeout.resetTimer();
+ startupState = StartupState::BOOTING;
+ }
+ if (startupState == StartupState::BOOTING) {
+ if (bootTimeout.hasTimedOut()) {
+ uartIsolatorSwitch.pullHigh();
+ uartManager.start();
+ if (SET_TIME_DURING_BOOT) {
+ startupState = StartupState::SET_TIME;
+ } else {
+ startupState = StartupState::ON;
+ }
+ }
+ }
+ if (startupState == StartupState::TIME_WAS_SET) {
+ startupState = StartupState::ON;
+ }
+ if (startupState == StartupState::ON) {
+ hkset.setReportingEnabled(true);
+ supv::SUPV_ON = true;
+ setMode(_MODE_TO_ON);
+ }
+}
+
+void PlocSupervisorHandler::doShutDown() {
+ setMode(_MODE_POWER_DOWN);
+ hkset.setReportingEnabled(false);
+ hkset.setValidity(false, true);
+ shutdownCmdSent = false;
+ packetInBuffer = false;
+ nextReplyId = supv::NONE;
+ uartManager.stop();
+ uartIsolatorSwitch.pullLow();
+ disableAllReplies();
+ supv::SUPV_ON = false;
+ startupState = StartupState::OFF;
+}
+
+ReturnValue_t PlocSupervisorHandler::buildNormalDeviceCommand(DeviceCommandId_t* id) {
+ if (not normalCommandIsPending) {
+ *id = GET_HK_REPORT;
+ normalCommandIsPending = true;
+ normalCmdCd.resetTimer();
+ return buildCommandFromCommand(*id, nullptr, 0);
+ }
+ return NOTHING_TO_SEND;
+}
+
+ReturnValue_t PlocSupervisorHandler::buildTransitionDeviceCommand(DeviceCommandId_t* id) {
+ if (startupState == StartupState::SET_TIME) {
+ *id = supv::SET_TIME_REF;
+ startupState = StartupState::WAIT_FOR_TIME_REPLY;
+ return buildCommandFromCommand(*id, nullptr, 0);
+ }
+ return NOTHING_TO_SEND;
+}
+
+ReturnValue_t PlocSupervisorHandler::buildCommandFromCommand(DeviceCommandId_t deviceCommand,
+ const uint8_t* commandData,
+ size_t commandDataLen) {
+ using namespace supv;
+ ReturnValue_t result = returnvalue::FAILED;
+ spParams.buf = commandBuffer;
+ switch (deviceCommand) {
+ case GET_HK_REPORT: {
+ prepareEmptyCmd(Apid::HK, static_cast(tc::HkId::GET_REPORT));
+ result = returnvalue::OK;
+ break;
+ }
+ case START_MPSOC: {
+ sif::info << "PLOC SUPV: Starting MPSoC" << std::endl;
+ prepareEmptyCmd(Apid::BOOT_MAN, static_cast(tc::BootManId::START_MPSOC));
+ result = returnvalue::OK;
+ break;
+ }
+ case SHUTDOWN_MPSOC: {
+ sif::info << "PLOC SUPV: Shutting down MPSoC" << std::endl;
+ prepareEmptyCmd(Apid::BOOT_MAN, static_cast(tc::BootManId::SHUTDOWN_MPSOC));
+ result = returnvalue::OK;
+ break;
+ }
+ case SEL_MPSOC_BOOT_IMAGE: {
+ prepareSelBootImageCmd(commandData);
+ result = returnvalue::OK;
+ break;
+ }
+ case RESET_MPSOC: {
+ sif::info << "PLOC SUPV: Resetting MPSoC" << std::endl;
+ prepareEmptyCmd(Apid::BOOT_MAN, static_cast(tc::BootManId::RESET_MPSOC));
+ result = returnvalue::OK;
+ break;
+ }
+ case SET_TIME_REF: {
+ result = prepareSetTimeRefCmd();
+ break;
+ }
+ case SET_BOOT_TIMEOUT: {
+ prepareSetBootTimeoutCmd(commandData);
+ result = returnvalue::OK;
+ break;
+ }
+ case SET_MAX_RESTART_TRIES: {
+ prepareRestartTriesCmd(commandData);
+ result = returnvalue::OK;
+ break;
+ }
+ case DISABLE_PERIOIC_HK_TRANSMISSION: {
+ prepareDisableHk();
+ result = returnvalue::OK;
+ break;
+ }
+ case GET_BOOT_STATUS_REPORT: {
+ prepareEmptyCmd(Apid::BOOT_MAN, static_cast(tc::BootManId::GET_BOOT_STATUS_REPORT));
+ result = returnvalue::OK;
+ break;
+ }
+ case ENABLE_LATCHUP_ALERT: {
+ result = prepareLatchupConfigCmd(commandData, deviceCommand);
+ break;
+ }
+ case DISABLE_LATCHUP_ALERT: {
+ result = prepareLatchupConfigCmd(commandData, deviceCommand);
+ break;
+ }
+ case SET_ALERT_LIMIT: {
+ result = prepareSetAlertLimitCmd(commandData);
+ break;
+ }
+ case GET_LATCHUP_STATUS_REPORT: {
+ prepareEmptyCmd(Apid::LATCHUP_MON, static_cast(tc::LatchupMonId::GET_STATUS_REPORT));
+ result = returnvalue::OK;
+ break;
+ }
+ case RUN_AUTO_EM_TESTS: {
+ result = prepareRunAutoEmTest(commandData);
+ break;
+ }
+ case SET_GPIO: {
+ result = prepareSetGpioCmd(commandData, commandDataLen);
+ break;
+ }
+ case FACTORY_RESET: {
+ result = prepareFactoryResetCmd(commandData, commandDataLen);
+ break;
+ }
+ case READ_GPIO: {
+ result = prepareReadGpioCmd(commandData, commandDataLen);
+ break;
+ }
+ case SET_SHUTDOWN_TIMEOUT: {
+ prepareSetShutdownTimeoutCmd(commandData);
+ result = returnvalue::OK;
+ break;
+ }
+ case FACTORY_FLASH: {
+ prepareEmptyCmd(Apid::BOOT_MAN, static_cast(tc::BootManId::FACTORY_FLASH));
+ result = returnvalue::OK;
+ break;
+ }
+ case RESET_PL: {
+ prepareEmptyCmd(Apid::BOOT_MAN, static_cast(tc::BootManId::RESET_PL));
+ result = returnvalue::OK;
+ break;
+ }
+ case SET_ADC_ENABLED_CHANNELS: {
+ prepareSetAdcEnabledChannelsCmd(commandData);
+ result = returnvalue::OK;
+ break;
+ }
+ case SET_ADC_WINDOW_AND_STRIDE: {
+ prepareSetAdcWindowAndStrideCmd(commandData);
+ result = returnvalue::OK;
+ break;
+ }
+ case SET_ADC_THRESHOLD: {
+ prepareSetAdcThresholdCmd(commandData);
+ result = returnvalue::OK;
+ break;
+ }
+ case WIPE_MRAM: {
+ result = prepareWipeMramCmd(commandData);
+ break;
+ }
+ case REQUEST_ADC_REPORT: {
+ prepareEmptyCmd(Apid::ADC_MON, static_cast(tc::AdcMonId::REQUEST_ADC_SAMPLE));
+ result = returnvalue::OK;
+ break;
+ }
+ case REQUEST_LOGGING_COUNTERS: {
+ prepareEmptyCmd(Apid::DATA_LOGGER,
+ static_cast(tc::DataLoggerServiceId::REQUEST_COUNTERS));
+ result = returnvalue::OK;
+ break;
+ }
+ default:
+ sif::debug << "PlocSupervisorHandler::buildCommandFromCommand: Command not implemented"
+ << std::endl;
+ result = DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED;
+ break;
+ }
+ commandIsPending = true;
+ cmdCd.resetTimer();
+ return result;
+}
+
+void PlocSupervisorHandler::fillCommandAndReplyMap() {
+ // Command only
+ insertInCommandMap(GET_HK_REPORT);
+ insertInCommandMap(START_MPSOC);
+ insertInCommandMap(SHUTDOWN_MPSOC);
+ insertInCommandMap(SEL_MPSOC_BOOT_IMAGE);
+ insertInCommandMap(SET_BOOT_TIMEOUT);
+ insertInCommandMap(SET_MAX_RESTART_TRIES);
+ insertInCommandMap(RESET_MPSOC);
+ insertInCommandMap(WIPE_MRAM);
+ insertInCommandMap(SET_TIME_REF);
+ insertInCommandMap(DISABLE_PERIOIC_HK_TRANSMISSION);
+ insertInCommandMap(GET_BOOT_STATUS_REPORT);
+ insertInCommandMap(ENABLE_LATCHUP_ALERT);
+ insertInCommandMap(DISABLE_LATCHUP_ALERT);
+ insertInCommandMap(SET_ALERT_LIMIT);
+ insertInCommandMap(GET_LATCHUP_STATUS_REPORT);
+ insertInCommandMap(RUN_AUTO_EM_TESTS);
+ insertInCommandMap(SET_GPIO);
+ insertInCommandMap(READ_GPIO);
+ insertInCommandMap(FACTORY_RESET);
+ insertInCommandMap(MEMORY_CHECK);
+ insertInCommandMap(SET_SHUTDOWN_TIMEOUT);
+ insertInCommandMap(FACTORY_FLASH);
+ insertInCommandMap(SET_ADC_ENABLED_CHANNELS);
+ insertInCommandMap(SET_ADC_THRESHOLD);
+ insertInCommandMap(SET_ADC_WINDOW_AND_STRIDE);
+ insertInCommandMap(RESET_PL);
+ insertInCommandMap(REQUEST_ADC_REPORT);
+ insertInCommandMap(REQUEST_LOGGING_COUNTERS);
+
+ // ACK replies, use countdown for them
+ insertInReplyMap(ACK_REPORT, 0, nullptr, SIZE_ACK_REPORT, false, &acknowledgementReportTimeout);
+ insertInReplyMap(EXE_REPORT, 0, nullptr, SIZE_EXE_REPORT, false, &executionReportTimeout);
+ insertInReplyMap(MEMORY_CHECK, 5, nullptr, 0, false);
+
+ // TM replies
+ insertInReplyMap(HK_REPORT, 3, &hkset);
+ insertInReplyMap(BOOT_STATUS_REPORT, 3, &bootStatusReport, SIZE_BOOT_STATUS_REPORT);
+ insertInReplyMap(LATCHUP_REPORT, 3, &latchupStatusReport, SIZE_LATCHUP_STATUS_REPORT);
+ insertInReplyMap(COUNTERS_REPORT, 3, &countersReport, SIZE_COUNTERS_REPORT);
+ insertInReplyMap(ADC_REPORT, 3, &adcReport, SIZE_ADC_REPORT);
+}
+
+ReturnValue_t PlocSupervisorHandler::enableReplyInReplyMap(DeviceCommandMap::iterator command,
+ uint8_t expectedReplies,
+ bool useAlternateId,
+ DeviceCommandId_t alternateReplyID) {
+ ReturnValue_t result = OK;
+
+ uint8_t enabledReplies = 0;
+
+ switch (command->first) {
+ case GET_HK_REPORT: {
+ enabledReplies = 3;
+ result = DeviceHandlerBase::enableReplyInReplyMap(command, enabledReplies, true, HK_REPORT);
+ if (result != returnvalue::OK) {
+ sif::debug << "PlocSupervisorHandler::enableReplyInReplyMap: Reply with id " << HK_REPORT
+ << " not in replyMap" << std::endl;
+ }
+ break;
+ }
+ case GET_BOOT_STATUS_REPORT: {
+ enabledReplies = 3;
+ result = DeviceHandlerBase::enableReplyInReplyMap(command, enabledReplies, true,
+ BOOT_STATUS_REPORT);
+ if (result != returnvalue::OK) {
+ sif::debug << "PlocSupervisorHandler::enableReplyInReplyMap: Reply with id "
+ << BOOT_STATUS_REPORT << " not in replyMap" << std::endl;
+ }
+ break;
+ }
+ case GET_LATCHUP_STATUS_REPORT: {
+ enabledReplies = 3;
+ result =
+ DeviceHandlerBase::enableReplyInReplyMap(command, enabledReplies, true, LATCHUP_REPORT);
+ if (result != returnvalue::OK) {
+ sif::debug << "PlocSupervisorHandler::enableReplyInReplyMap: Reply with id "
+ << LATCHUP_REPORT << " not in replyMap" << std::endl;
+ }
+ break;
+ }
+ case REQUEST_LOGGING_COUNTERS: {
+ enabledReplies = 3;
+ result =
+ DeviceHandlerBase::enableReplyInReplyMap(command, enabledReplies, true, COUNTERS_REPORT);
+ if (result != returnvalue::OK) {
+ sif::debug << "PlocSupervisorHandler::enableReplyInReplyMap: Reply with id "
+ << COUNTERS_REPORT << " not in replyMap" << std::endl;
+ }
+ break;
+ }
+ case REQUEST_ADC_REPORT: {
+ enabledReplies = 3;
+ result = DeviceHandlerBase::enableReplyInReplyMap(command, enabledReplies, true, ADC_REPORT);
+ if (result != returnvalue::OK) {
+ sif::debug << "PlocSupervisorHandler::enableReplyInReplyMap: Reply with id " << ADC_REPORT
+ << " not in replyMap" << std::endl;
+ }
+ break;
+ }
+ case FIRST_MRAM_DUMP: {
+ enabledReplies = 2; // expected replies will be increased in handleMramDumpPacket
+ result =
+ DeviceHandlerBase::enableReplyInReplyMap(command, enabledReplies, true, FIRST_MRAM_DUMP);
+ if (result != returnvalue::OK) {
+ sif::debug << "PlocSupervisorHandler::enableReplyInReplyMap: Reply with id "
+ << FIRST_MRAM_DUMP << " not in replyMap" << std::endl;
+ }
+ break;
+ }
+ case CONSECUTIVE_MRAM_DUMP: {
+ enabledReplies = 2; // expected replies will be increased in handleMramDumpPacket
+ result = DeviceHandlerBase::enableReplyInReplyMap(command, enabledReplies, true,
+ CONSECUTIVE_MRAM_DUMP);
+ if (result != returnvalue::OK) {
+ sif::debug << "PlocSupervisorHandler::enableReplyInReplyMap: Reply with id "
+ << CONSECUTIVE_MRAM_DUMP << " not in replyMap" << std::endl;
+ }
+ break;
+ }
+ case MEMORY_CHECK: {
+ enabledReplies = 3;
+ result =
+ DeviceHandlerBase::enableReplyInReplyMap(command, enabledReplies, true, MEMORY_CHECK);
+ if (result != returnvalue::OK) {
+ sif::debug << "PlocSupervisorHandler::enableReplyInReplyMap: Reply with id " << MEMORY_CHECK
+ << " not in replyMap" << std::endl;
+ }
+ break;
+ }
+ case START_MPSOC:
+ case SHUTDOWN_MPSOC:
+ case SEL_MPSOC_BOOT_IMAGE:
+ case SET_BOOT_TIMEOUT:
+ case SET_MAX_RESTART_TRIES:
+ case RESET_MPSOC:
+ case SET_TIME_REF:
+ case ENABLE_LATCHUP_ALERT:
+ case DISABLE_LATCHUP_ALERT:
+ case SET_ALERT_LIMIT:
+ case SET_ADC_ENABLED_CHANNELS:
+ case SET_ADC_WINDOW_AND_STRIDE:
+ case SET_ADC_THRESHOLD:
+ case RUN_AUTO_EM_TESTS:
+ case WIPE_MRAM:
+ case SET_GPIO:
+ case FACTORY_RESET:
+ case READ_GPIO:
+ case DISABLE_PERIOIC_HK_TRANSMISSION:
+ case SET_SHUTDOWN_TIMEOUT:
+ case FACTORY_FLASH:
+ case ENABLE_AUTO_TM:
+ case DISABLE_AUTO_TM:
+ case RESET_PL:
+ enabledReplies = 2;
+ break;
+ default:
+ sif::debug << "PlocSupervisorHandler::enableReplyInReplyMap: Unknown command id" << std::endl;
+ break;
+ }
+
+ /**
+ * Every command causes at least one acknowledgment and one execution report. Therefore both
+ * replies will be enabled here.
+ */
+ result = DeviceHandlerBase::enableReplyInReplyMap(command, enabledReplies, true, ACK_REPORT);
+ if (result != returnvalue::OK) {
+ sif::debug << "PlocSupervisorHandler::enableReplyInReplyMap: Reply with id " << ACK_REPORT
+ << " not in replyMap" << std::endl;
+ }
+
+ setExecutionTimeout(command->first);
+
+ result = DeviceHandlerBase::enableReplyInReplyMap(command, enabledReplies, true, EXE_REPORT);
+ if (result != returnvalue::OK) {
+ sif::debug << "PlocSupervisorHandler::enableReplyInReplyMap: Reply with id " << EXE_REPORT
+ << " not in replyMap" << std::endl;
+ }
+
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocSupervisorHandler::scanForReply(const uint8_t* start, size_t remainingSize,
+ DeviceCommandId_t* foundId, size_t* foundLen) {
+ using namespace supv;
+
+ tmReader.setData(start, remainingSize);
+ uint16_t apid = tmReader.getModuleApid();
+ if (DEBUG_PLOC_SUPV) {
+ handlePacketPrint();
+ }
+
+ switch (apid) {
+ case (Apid::TMTC_MAN): {
+ switch (tmReader.getServiceId()) {
+ case (static_cast(supv::tm::TmtcId::ACK)):
+ case (static_cast(supv::tm::TmtcId::NAK)): {
+ *foundLen = tmReader.getFullPacketLen();
+ *foundId = ReplyId::ACK_REPORT;
+ return OK;
+ }
+ case (static_cast(supv::tm::TmtcId::EXEC_ACK)):
+ case (static_cast(supv::tm::TmtcId::EXEC_NAK)): {
+ *foundLen = tmReader.getFullPacketLen();
+ *foundId = EXE_REPORT;
+ return OK;
+ }
+ }
+ break;
+ }
+ case (Apid::HK): {
+ if (tmReader.getServiceId() == static_cast(supv::tm::HkId::REPORT)) {
+ normalCommandIsPending = false;
+ // Yeah apparently this is needed??
+ disableCommand(GET_HK_REPORT);
+ *foundLen = tmReader.getFullPacketLen();
+ *foundId = ReplyId::HK_REPORT;
+ return OK;
+ } else if (tmReader.getServiceId() == static_cast(supv::tm::HkId::HARDFAULTS)) {
+ handleBadApidServiceCombination(SUPV_UNINIMPLEMENTED_TM, apid, tmReader.getServiceId());
+ return INVALID_DATA;
+ }
+ break;
+ }
+ case (Apid::BOOT_MAN): {
+ if (tmReader.getServiceId() ==
+ static_cast(supv::tm::BootManId::BOOT_STATUS_REPORT)) {
+ *foundLen = tmReader.getFullPacketLen();
+ *foundId = ReplyId::BOOT_STATUS_REPORT;
+ return OK;
+ }
+ break;
+ }
+ case (Apid::ADC_MON): {
+ if (tmReader.getServiceId() == static_cast(supv::tm::AdcMonId::ADC_REPORT)) {
+ *foundLen = tmReader.getFullPacketLen();
+ *foundId = ReplyId::ADC_REPORT;
+ return OK;
+ }
+ break;
+ }
+ case (Apid::MEM_MAN): {
+ if (tmReader.getServiceId() ==
+ static_cast(supv::tm::MemManId::UPDATE_STATUS_REPORT)) {
+ *foundLen = tmReader.getFullPacketLen();
+ *foundId = ReplyId::UPDATE_STATUS_REPORT;
+ return OK;
+ }
+ break;
+ }
+ case (Apid::DATA_LOGGER): {
+ if (tmReader.getServiceId() ==
+ static_cast(supv::tm::DataLoggerId::COUNTERS_REPORT)) {
+ *foundLen = tmReader.getFullPacketLen();
+ *foundId = ReplyId::COUNTERS_REPORT;
+ return OK;
+ }
+ }
+ }
+ handleBadApidServiceCombination(SUPV_UNKNOWN_TM, apid, tmReader.getServiceId());
+ *foundLen = remainingSize;
+ return INVALID_DATA;
+}
+
+void PlocSupervisorHandler::handlePacketPrint() {
+ if (tmReader.getModuleApid() == Apid::TMTC_MAN) {
+ if ((tmReader.getServiceId() == static_cast(supv::tm::TmtcId::ACK)) or
+ (tmReader.getServiceId() == static_cast(supv::tm::TmtcId::NAK))) {
+ AcknowledgmentReport ack(tmReader);
+ ReturnValue_t result = ack.parse();
+ if (result != returnvalue::OK) {
+ sif::warning << "PlocSupervisorHandler: Parsing ACK failed" << std::endl;
+ }
+ if (REDUCE_NORMAL_MODE_PRINTOUT and ack.getRefModuleApid() == (uint8_t)supv::Apid::HK and
+ ack.getRefServiceId() == (uint8_t)supv::tc::HkId::GET_REPORT) {
+ return;
+ }
+ const char* printStr = "???";
+ if (tmReader.getServiceId() == static_cast(supv::tm::TmtcId::ACK)) {
+ printStr = "ACK";
+
+ } else if (tmReader.getServiceId() == static_cast(supv::tm::TmtcId::NAK)) {
+ printStr = "NAK";
+ }
+ sif::debug << "PlocSupervisorHandler: RECV " << printStr << " for APID Module ID "
+ << (int)ack.getRefModuleApid() << " Service ID " << (int)ack.getRefServiceId()
+ << " Seq Count " << ack.getRefSequenceCount() << std::endl;
+ return;
+ } else if ((tmReader.getServiceId() == static_cast(supv::tm::TmtcId::EXEC_ACK)) or
+ (tmReader.getServiceId() == static_cast(supv::tm::TmtcId::EXEC_NAK))) {
+ ExecutionReport exe(tmReader);
+ ReturnValue_t result = exe.parse();
+ if (result != returnvalue::OK) {
+ sif::warning << "PlocSupervisorHandler: Parsing EXE failed" << std::endl;
+ }
+ const char* printStr = "???";
+ if (REDUCE_NORMAL_MODE_PRINTOUT and exe.getRefModuleApid() == (uint8_t)supv::Apid::HK and
+ exe.getRefServiceId() == (uint8_t)supv::tc::HkId::GET_REPORT) {
+ return;
+ }
+ if (tmReader.getServiceId() == static_cast(supv::tm::TmtcId::EXEC_ACK)) {
+ printStr = "ACK EXE";
+
+ } else if (tmReader.getServiceId() == static_cast(supv::tm::TmtcId::EXEC_NAK)) {
+ printStr = "NAK EXE";
+ }
+ sif::debug << "PlocSupervisorHandler: RECV " << printStr << " for APID Module ID "
+ << (int)exe.getRefModuleApid() << " Service ID " << (int)exe.getRefServiceId()
+ << " Seq Count " << exe.getRefSequenceCount() << std::endl;
+ return;
+ }
+ }
+ sif::debug << "PlocSupervisorHandler: RECV PACKET Size " << tmReader.getFullPacketLen()
+ << " Module APID " << (int)tmReader.getModuleApid() << " Service ID "
+ << (int)tmReader.getServiceId() << std::endl;
+}
+ReturnValue_t PlocSupervisorHandler::interpretDeviceReply(DeviceCommandId_t id,
+ const uint8_t* packet) {
+ using namespace supv;
+ ReturnValue_t result = returnvalue::OK;
+
+ switch (id) {
+ case ACK_REPORT: {
+ result = handleAckReport(packet);
+ break;
+ }
+ case (HK_REPORT): {
+ result = handleHkReport(packet);
+ break;
+ }
+ case (BOOT_STATUS_REPORT): {
+ result = handleBootStatusReport(packet);
+ break;
+ }
+ case (COUNTERS_REPORT): {
+ result = genericHandleTm("COUNTERS", packet, countersReport);
+#if OBSW_VERBOSE_LEVEL >= 1 && OBSW_DEBUG_PLOC_SUPERVISOR == 1
+ countersReport.printSet();
+#endif
+ break;
+ }
+ case (LATCHUP_REPORT): {
+ result = handleLatchupStatusReport(packet);
+ break;
+ }
+ case (ADC_REPORT): {
+ result = genericHandleTm("ADC", packet, adcReport);
+#if OBSW_VERBOSE_LEVEL >= 1 && OBSW_DEBUG_PLOC_SUPERVISOR == 1
+ adcReport.printSet();
+#endif
+ break;
+ }
+ case (EXE_REPORT): {
+ result = handleExecutionReport(packet);
+ break;
+ }
+ case (UPDATE_STATUS_REPORT): {
+ // TODO: handle status report here
+ break;
+ }
+ default: {
+ sif::debug << "PlocSupervisorHandler::interpretDeviceReply: Unknown device reply id"
+ << std::endl;
+ return DeviceHandlerIF::UNKNOWN_DEVICE_REPLY;
+ }
+ }
+
+ return result;
+}
+
+ReturnValue_t PlocSupervisorHandler::initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
+ LocalDataPoolManager& poolManager) {
+ localDataPoolMap.emplace(supv::NUM_TMS, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::TEMP_PS, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::TEMP_PL, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::HK_SOC_STATE, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::NVM0_1_STATE, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::NVM3_STATE, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::MISSION_IO_STATE, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::FMC_STATE, &fmcStateEntry);
+ localDataPoolMap.emplace(supv::NUM_TCS, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::TEMP_SUP, &tempSupEntry);
+ localDataPoolMap.emplace(supv::UPTIME, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::CPULOAD, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::AVAILABLEHEAP, new PoolEntry({0}));
+
+ localDataPoolMap.emplace(supv::BR_SOC_STATE, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::POWER_CYCLES, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::BOOT_AFTER_MS, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::BOOT_TIMEOUT_MS, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::ACTIVE_NVM, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::BP0_STATE, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::BP1_STATE, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::BP2_STATE, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::BOOT_STATE, &bootStateEntry);
+ localDataPoolMap.emplace(supv::BOOT_CYCLES, &bootCyclesEntry);
+
+ localDataPoolMap.emplace(supv::LATCHUP_ID, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::CNT0, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::CNT1, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::CNT2, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::CNT3, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::CNT4, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::CNT5, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::CNT6, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::LATCHUP_RPT_TIME_MSEC, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::LATCHUP_RPT_TIME_SEC, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::LATCHUP_RPT_TIME_MIN, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::LATCHUP_RPT_TIME_HOUR, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::LATCHUP_RPT_TIME_DAY, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::LATCHUP_RPT_TIME_MON, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::LATCHUP_RPT_TIME_YEAR, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::LATCHUP_RPT_IS_SET, new PoolEntry({0}));
+
+ localDataPoolMap.emplace(supv::SIGNATURE, new PoolEntry());
+ localDataPoolMap.emplace(supv::LATCHUP_HAPPENED_CNTS, &latchupCounters);
+ localDataPoolMap.emplace(supv::ADC_DEVIATION_TRIGGERS_CNT, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::TC_RECEIVED_CNT, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::TM_AVAILABLE_CNT, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::SUPERVISOR_BOOTS, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::MPSOC_BOOTS, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::MPSOC_BOOT_FAILED_ATTEMPTS, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::MPSOC_POWER_UP, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::MPSOC_UPDATES, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::MPSOC_HEARTBEAT_RESETS, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::CPU_WDT_RESETS, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::PS_HEARTBEATS_LOST, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::PL_HEARTBEATS_LOST, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::EB_TASK_LOST, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::BM_TASK_LOST, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::LM_TASK_LOST, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::AM_TASK_LOST, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::TCTMM_TASK_LOST, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::MM_TASK_LOST, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::HK_TASK_LOST, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::DL_TASK_LOST, new PoolEntry({0}));
+ localDataPoolMap.emplace(supv::RWS_TASKS_LOST, new PoolEntry(3));
+
+ localDataPoolMap.emplace(supv::ADC_RAW, &adcRawEntry);
+ localDataPoolMap.emplace(supv::ADC_ENG, &adcEngEntry);
+
+ poolManager.subscribeForRegularPeriodicPacket(
+ subdp::RegularHkPeriodicParams(hkset.getSid(), false, 10.0));
+ return returnvalue::OK;
+}
+
+void PlocSupervisorHandler::handleEvent(EventMessage* eventMessage) {
+ ReturnValue_t result = returnvalue::OK;
+ object_id_t objectId = eventMessage->getReporter();
+ Event event = eventMessage->getEvent();
+ switch (objectId) {
+ case objects::PLOC_SUPERVISOR_HELPER: {
+ // After execution of update procedure, PLOC is in a state where it draws approx. 700 mA of
+ // current. To leave this state the shutdown MPSoC command must be sent here.
+ if (event == PlocSupvUartManager::SUPV_UPDATE_FAILED ||
+ event == PlocSupvUartManager::SUPV_UPDATE_SUCCESSFUL ||
+ event == PlocSupvUartManager::SUPV_CONTINUE_UPDATE_FAILED ||
+ event == PlocSupvUartManager::SUPV_CONTINUE_UPDATE_SUCCESSFUL ||
+ event == PlocSupvUartManager::SUPV_MEM_CHECK_FAIL ||
+ event == PlocSupvUartManager::SUPV_MEM_CHECK_OK) {
+ // Wait for a short period for the uart state machine to adjust
+ // TaskFactory::delayTask(5);
+ if (not shutdownCmdSent) {
+ shutdownCmdSent = true;
+ result = this->executeAction(supv::SHUTDOWN_MPSOC, NO_COMMANDER, nullptr, 0);
+ if (result != returnvalue::OK) {
+ triggerEvent(SUPV_MPSOC_SHUTDOWN_BUILD_FAILED);
+ sif::warning << "PlocSupervisorHandler::handleEvent: Failed to build MPSoC shutdown "
+ "command"
+ << std::endl;
+ return;
+ }
+ }
+ }
+ break;
+ }
+ default:
+ sif::debug << "PlocMPSoCHandler::handleEvent: Did not subscribe to this event" << std::endl;
+ break;
+ }
+}
+
+void PlocSupervisorHandler::setExecutionTimeout(DeviceCommandId_t command) {
+ using namespace supv;
+ switch (command) {
+ case FIRST_MRAM_DUMP:
+ case CONSECUTIVE_MRAM_DUMP:
+ executionReportTimeout.setTimeout(MRAM_DUMP_EXECUTION_TIMEOUT);
+ break;
+ case COPY_ADC_DATA_TO_MRAM:
+ executionReportTimeout.setTimeout(COPY_ADC_TO_MRAM_TIMEOUT);
+ break;
+ default:
+ executionReportTimeout.setTimeout(EXECUTION_DEFAULT_TIMEOUT);
+ break;
+ }
+}
+
+ReturnValue_t PlocSupervisorHandler::verifyPacket(const uint8_t* start, size_t foundLen) {
+ if (CRC::crc16ccitt(start, foundLen) != 0) {
+ return result::CRC_FAILURE;
+ }
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocSupervisorHandler::handleAckReport(const uint8_t* data) {
+ using namespace supv;
+ ReturnValue_t result = returnvalue::OK;
+
+ if (not tmReader.verifyCrc()) {
+ sif::error << "PlocSupervisorHandler::handleAckReport: CRC failure" << std::endl;
+ nextReplyId = supv::NONE;
+ replyRawReplyIfnotWiretapped(data, supv::SIZE_ACK_REPORT);
+ triggerEvent(SUPV_CRC_FAILURE_EVENT);
+ sendFailureReport(supv::ACK_REPORT, result::CRC_FAILURE);
+ disableAllReplies();
+ return returnvalue::OK;
+ }
+ AcknowledgmentReport ack(tmReader);
+ result = ack.parse();
+ if (result != returnvalue::OK) {
+ nextReplyId = supv::NONE;
+ replyRawReplyIfnotWiretapped(data, supv::SIZE_ACK_REPORT);
+ triggerEvent(SUPV_CRC_FAILURE_EVENT);
+ sendFailureReport(supv::ACK_REPORT, result);
+ disableAllReplies();
+ return result;
+ }
+ if (tmReader.getServiceId() == static_cast(supv::tm::TmtcId::NAK)) {
+ DeviceCommandId_t commandId = getPendingCommand();
+ if (commandId != DeviceHandlerIF::NO_COMMAND_ID) {
+ triggerEvent(SUPV_ACK_FAILURE, commandId, static_cast(ack.getStatusCode()));
+ }
+ ack.printStatusInformation();
+ printAckFailureInfo(ack.getStatusCode(), commandId);
+ sendFailureReport(supv::ACK_REPORT, result::RECEIVED_ACK_FAILURE);
+ disableAllReplies();
+ nextReplyId = supv::NONE;
+ result = IGNORE_REPLY_DATA;
+ } else if (tmReader.getServiceId() == static_cast(supv::tm::TmtcId::ACK)) {
+ setNextReplyId();
+ }
+ return result;
+}
+
+ReturnValue_t PlocSupervisorHandler::handleExecutionReport(const uint8_t* data) {
+ using namespace supv;
+ ReturnValue_t result = returnvalue::OK;
+
+ if (not tmReader.verifyCrc()) {
+ nextReplyId = supv::NONE;
+ return result::CRC_FAILURE;
+ }
+ ExecutionReport report(tmReader);
+ result = report.parse();
+ if (result != OK) {
+ nextReplyId = supv::NONE;
+ return result;
+ }
+ if (tmReader.getServiceId() == static_cast(supv::tm::TmtcId::EXEC_ACK)) {
+ result = handleExecutionSuccessReport(report);
+ } else if (tmReader.getServiceId() == static_cast(supv::tm::TmtcId::EXEC_NAK)) {
+ handleExecutionFailureReport(report);
+ }
+ commandIsPending = false;
+ nextReplyId = supv::NONE;
+ return result;
+}
+
+ReturnValue_t PlocSupervisorHandler::handleHkReport(const uint8_t* data) {
+ ReturnValue_t result = returnvalue::OK;
+
+ result = verifyPacket(data, tmReader.getFullPacketLen());
+
+ if (result == result::CRC_FAILURE) {
+ sif::error << "PlocSupervisorHandler::handleHkReport: Hk report has invalid crc" << std::endl;
+ return result;
+ }
+
+ uint16_t offset = supv::PAYLOAD_OFFSET;
+ hkset.tempPs = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8 |
+ *(data + offset + 3);
+ offset += 4;
+ hkset.tempPl = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8 |
+ *(data + offset + 3);
+ offset += 4;
+ hkset.tempSup = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8 |
+ *(data + offset + 3);
+ offset += 4;
+ size_t size = sizeof(hkset.uptime.value);
+ result = SerializeAdapter::deSerialize(&hkset.uptime, data + offset, &size,
+ SerializeIF::Endianness::BIG);
+ offset += 8;
+ hkset.cpuLoad = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8 |
+ *(data + offset + 3);
+ offset += 4;
+ hkset.availableHeap = *(data + offset) << 24 | *(data + offset + 1) << 16 |
+ *(data + offset + 2) << 8 | *(data + offset + 3);
+ offset += 4;
+ hkset.numTcs = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8 |
+ *(data + offset + 3);
+ offset += 4;
+ hkset.numTms = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8 |
+ *(data + offset + 3);
+ offset += 4;
+ hkset.socState = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8 |
+ *(data + offset + 3);
+ offset += 4;
+ hkset.nvm0_1_state = *(data + offset);
+ offset += 1;
+ hkset.nvm3_state = *(data + offset);
+ offset += 1;
+ hkset.missionIoState = *(data + offset);
+ offset += 1;
+ hkset.fmcState = *(data + offset);
+ offset += 1;
+
+ nextReplyId = supv::EXE_REPORT;
+ hkset.setValidity(true, true);
+
+#if OBSW_VERBOSE_LEVEL >= 1 && OBSW_DEBUG_PLOC_SUPERVISOR == 1
+ sif::info << "PlocSupervisorHandler::handleHkReport: temp_ps: " << hkset.tempPs << std::endl;
+ sif::info << "PlocSupervisorHandler::handleHkReport: temp_pl: " << hkset.tempPl << std::endl;
+ sif::info << "PlocSupervisorHandler::handleHkReport: temp_sup: " << hkset.tempSup << std::endl;
+ sif::info << "PlocSupervisorHandler::handleHkReport: uptime: " << hkset.uptime << std::endl;
+ sif::info << "PlocSupervisorHandler::handleHkReport: cpu_load: " << hkset.cpuLoad << std::endl;
+ sif::info << "PlocSupervisorHandler::handleHkReport: available_heap: " << hkset.availableHeap
+ << std::endl;
+ sif::info << "PlocSupervisorHandler::handleHkReport: num_tcs: " << hkset.numTcs << std::endl;
+ sif::info << "PlocSupervisorHandler::handleHkReport: num_tms: " << hkset.numTms << std::endl;
+ sif::info << "PlocSupervisorHandler::handleHkReport: soc_state: " << hkset.socState << std::endl;
+ sif::info << "PlocSupervisorHandler::handleHkReport: nvm0_1_state: "
+ << static_cast(hkset.nvm0_1_state.value) << std::endl;
+ sif::info << "PlocSupervisorHandler::handleHkReport: nvm3_state: "
+ << static_cast(hkset.nvm3_state.value) << std::endl;
+ sif::info << "PlocSupervisorHandler::handleHkReport: mission_io_state: "
+ << static_cast(hkset.missionIoState.value) << std::endl;
+ sif::info << "PlocSupervisorHandler::handleHkReport: fmc_state: "
+ << static_cast(hkset.fmcState.value) << std::endl;
+
+#endif
+
+ return result;
+}
+
+ReturnValue_t PlocSupervisorHandler::handleBootStatusReport(const uint8_t* data) {
+ ReturnValue_t result = returnvalue::OK;
+
+ result = verifyPacket(data, tmReader.getFullPacketLen());
+
+ if (result == result::CRC_FAILURE) {
+ sif::error << "PlocSupervisorHandler::handleBootStatusReport: Boot status report has invalid"
+ " crc"
+ << std::endl;
+ return result;
+ }
+
+ const uint8_t* payloadStart = tmReader.getPayloadStart();
+ uint16_t offset = 0;
+ bootStatusReport.socState = payloadStart[0];
+ offset += 1;
+ bootStatusReport.powerCycles = payloadStart[1];
+ offset += 1;
+ bootStatusReport.bootAfterMs = *(payloadStart + offset) << 24 |
+ *(payloadStart + offset + 1) << 16 |
+ *(payloadStart + offset + 2) << 8 | *(payloadStart + offset + 3);
+ offset += 4;
+ bootStatusReport.bootTimeoutMs = *(payloadStart + offset) << 24 |
+ *(payloadStart + offset + 1) << 16 |
+ *(payloadStart + offset + 2) << 8 | *(payloadStart + offset + 3);
+ offset += 4;
+ bootStatusReport.activeNvm = *(payloadStart + offset);
+ offset += 1;
+ bootStatusReport.bp0State = *(payloadStart + offset);
+ offset += 1;
+ bootStatusReport.bp1State = *(payloadStart + offset);
+ offset += 1;
+ bootStatusReport.bp2State = *(payloadStart + offset);
+ offset += 1;
+ bootStatusReport.bootState = *(payloadStart + offset);
+ offset += 1;
+ bootStatusReport.bootCycles = *(payloadStart + offset);
+
+ nextReplyId = supv::EXE_REPORT;
+ bootStatusReport.setValidity(true, true);
+
+#if OBSW_VERBOSE_LEVEL >= 1 && OBSW_DEBUG_PLOC_SUPERVISOR == 1
+ sif::info << "PlocSupervisorHandler::handleBootStatusReport: SoC State (0 - off, 1 - booting, 2 "
+ "- Update, 3 "
+ "- operating, 4 - Shutdown, 5 - Reset): "
+ << static_cast(bootStatusReport.socState.value) << std::endl;
+ sif::info << "PlocSupervisorHandler::handleBootStatusReport: Power Cycles: "
+ << static_cast(bootStatusReport.powerCycles.value) << std::endl;
+ sif::info << "PlocSupervisorHandler::handleBootStatusReport: BootAfterMs: "
+ << bootStatusReport.bootAfterMs << " ms" << std::endl;
+ sif::info << "PlocSupervisorHandler::handleBootStatusReport: BootTimeoutMs: " << std::dec
+ << bootStatusReport.bootTimeoutMs << " ms" << std::endl;
+ sif::info << "PlocSupervisorHandler::handleBootStatusReport: Active NVM: "
+ << static_cast(bootStatusReport.activeNvm.value) << std::endl;
+ sif::info << "PlocSupervisorHandler::handleBootStatusReport: BP0: "
+ << static_cast(bootStatusReport.bp0State.value) << std::endl;
+ sif::info << "PlocSupervisorHandler::handleBootStatusReport: BP1: "
+ << static_cast(bootStatusReport.bp1State.value) << std::endl;
+ sif::info << "PlocSupervisorHandler::handleBootStatusReport: BP2: "
+ << static_cast(bootStatusReport.bp2State.value) << std::endl;
+ sif::info << "PlocSupervisorHandler::handleBootStatusReport: Boot state: "
+ << static_cast(bootStatusReport.bootState.value) << std::endl;
+ sif::info << "PlocSupervisorHandler::handleBootStatusReport: Boot cycles: "
+ << static_cast(bootStatusReport.bootCycles.value) << std::endl;
+#endif
+
+ return result;
+}
+
+ReturnValue_t PlocSupervisorHandler::handleLatchupStatusReport(const uint8_t* data) {
+ ReturnValue_t result = returnvalue::OK;
+
+ result = verifyPacket(data, tmReader.getFullPacketLen());
+
+ if (result == result::CRC_FAILURE) {
+ sif::error << "PlocSupervisorHandler::handleLatchupStatusReport: Latchup status report has "
+ << "invalid crc" << std::endl;
+ return result;
+ }
+
+ const uint8_t* payloadData = tmReader.getPayloadStart();
+ uint16_t offset = 0;
+ latchupStatusReport.id = *(payloadData + offset);
+ offset += 1;
+ latchupStatusReport.cnt0 = *(payloadData + offset) << 8 | *(payloadData + offset + 1);
+ offset += 2;
+ latchupStatusReport.cnt1 = *(payloadData + offset) << 8 | *(payloadData + offset + 1);
+ offset += 2;
+ latchupStatusReport.cnt2 = *(payloadData + offset) << 8 | *(payloadData + offset + 1);
+ offset += 2;
+ latchupStatusReport.cnt3 = *(payloadData + offset) << 8 | *(payloadData + offset + 1);
+ offset += 2;
+ latchupStatusReport.cnt4 = *(payloadData + offset) << 8 | *(payloadData + offset + 1);
+ offset += 2;
+ latchupStatusReport.cnt5 = *(payloadData + offset) << 8 | *(payloadData + offset + 1);
+ offset += 2;
+ latchupStatusReport.cnt6 = *(payloadData + offset) << 8 | *(data + offset + 1);
+ offset += 2;
+ uint16_t msec = *(payloadData + offset) << 8 | *(payloadData + offset + 1);
+ latchupStatusReport.isSet = msec >> supv::LatchupStatusReport::IS_SET_BIT_POS;
+ latchupStatusReport.timeMsec = msec & (~(1 << latchupStatusReport.IS_SET_BIT_POS));
+ offset += 2;
+ latchupStatusReport.timeSec = *(payloadData + offset);
+ offset += 1;
+ latchupStatusReport.timeMin = *(payloadData + offset);
+ offset += 1;
+ latchupStatusReport.timeHour = *(payloadData + offset);
+ offset += 1;
+ latchupStatusReport.timeDay = *(payloadData + offset);
+ offset += 1;
+ latchupStatusReport.timeMon = *(payloadData + offset);
+ offset += 1;
+ latchupStatusReport.timeYear = *(payloadData + offset);
+
+ nextReplyId = supv::EXE_REPORT;
+
+#if OBSW_VERBOSE_LEVEL >= 1 && OBSW_DEBUG_PLOC_SUPERVISOR == 1
+ sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: Latchup ID: "
+ << static_cast(latchupStatusReport.id.value) << std::endl;
+ sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: CNT0: "
+ << latchupStatusReport.cnt0 << std::endl;
+ sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: CNT1: "
+ << latchupStatusReport.cnt1 << std::endl;
+ sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: CNT2: "
+ << latchupStatusReport.cnt2 << std::endl;
+ sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: CNT3: "
+ << latchupStatusReport.cnt3 << std::endl;
+ sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: CNT4: "
+ << latchupStatusReport.cnt4 << std::endl;
+ sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: CNT5: "
+ << latchupStatusReport.cnt5 << std::endl;
+ sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: CNT6: "
+ << latchupStatusReport.cnt6 << std::endl;
+ sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: Sec: "
+ << static_cast(latchupStatusReport.timeSec.value) << std::endl;
+ sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: Min: "
+ << static_cast(latchupStatusReport.timeMin.value) << std::endl;
+ sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: Hour: "
+ << static_cast(latchupStatusReport.timeHour.value) << std::endl;
+ sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: Day: "
+ << static_cast(latchupStatusReport.timeDay.value) << std::endl;
+ sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: Mon: "
+ << static_cast(latchupStatusReport.timeMon.value) << std::endl;
+ sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: Year: "
+ << static_cast(latchupStatusReport.timeYear.value) << std::endl;
+ sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: Msec: "
+ << static_cast(latchupStatusReport.timeMsec.value) << std::endl;
+ sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: isSet: "
+ << static_cast(latchupStatusReport.isSet.value) << std::endl;
+#endif
+
+ return result;
+}
+
+ReturnValue_t PlocSupervisorHandler::genericHandleTm(const char* contextString, const uint8_t* data,
+ LocalPoolDataSetBase& set) {
+ ReturnValue_t result = returnvalue::OK;
+
+ result = verifyPacket(data, tmReader.getFullPacketLen());
+
+ if (result == result::CRC_FAILURE) {
+ sif::warning << "PlocSupervisorHandler: " << contextString << " report has "
+ << "invalid CRC" << std::endl;
+ return result;
+ }
+
+ const uint8_t* dataField = data + supv::PAYLOAD_OFFSET;
+ PoolReadGuard pg(&set);
+ if (pg.getReadResult() != returnvalue::OK) {
+ return result;
+ }
+ set.setValidityBufferGeneration(false);
+ size_t size = set.getSerializedSize();
+ result = set.deSerialize(&dataField, &size, SerializeIF::Endianness::BIG);
+ if (result != returnvalue::OK) {
+ sif::warning << "PlocSupervisorHandler: Deserialization failed" << std::endl;
+ }
+ set.setValidityBufferGeneration(true);
+ set.setValidity(true, true);
+ nextReplyId = supv::EXE_REPORT;
+ return result;
+}
+
+void PlocSupervisorHandler::setNextReplyId() {
+ switch (getPendingCommand()) {
+ case supv::GET_HK_REPORT:
+ nextReplyId = supv::HK_REPORT;
+ break;
+ case supv::GET_BOOT_STATUS_REPORT:
+ nextReplyId = supv::BOOT_STATUS_REPORT;
+ break;
+ case supv::GET_LATCHUP_STATUS_REPORT:
+ nextReplyId = supv::LATCHUP_REPORT;
+ break;
+ case supv::FIRST_MRAM_DUMP:
+ nextReplyId = supv::FIRST_MRAM_DUMP;
+ break;
+ case supv::CONSECUTIVE_MRAM_DUMP:
+ nextReplyId = supv::CONSECUTIVE_MRAM_DUMP;
+ break;
+ case supv::REQUEST_LOGGING_COUNTERS:
+ nextReplyId = supv::COUNTERS_REPORT;
+ break;
+ case supv::REQUEST_ADC_REPORT:
+ nextReplyId = supv::ADC_REPORT;
+ break;
+ default:
+ /* If no telemetry is expected the next reply is always the execution report */
+ nextReplyId = supv::EXE_REPORT;
+ break;
+ }
+}
+
+size_t PlocSupervisorHandler::getNextReplyLength(DeviceCommandId_t commandId) {
+ size_t replyLen = 0;
+
+ if (nextReplyId == supv::NONE) {
+ return replyLen;
+ }
+
+ if (nextReplyId == supv::FIRST_MRAM_DUMP || nextReplyId == supv::CONSECUTIVE_MRAM_DUMP) {
+ /**
+ * Try to read 20 MRAM packets. If reply is larger, the packets will be read with the
+ * next doSendRead call. The command will be as long active as the packet with the sequence
+ * count indicating the last packet has not been received.
+ */
+ replyLen = supv::MAX_PACKET_SIZE * 20;
+ return replyLen;
+ }
+
+ DeviceReplyIter iter = deviceReplyMap.find(nextReplyId);
+ if (iter != deviceReplyMap.end()) {
+ if ((iter->second.delayCycles == 0 && iter->second.countdown == nullptr) ||
+ (not iter->second.active && iter->second.countdown != nullptr)) {
+ /* Reply inactive */
+ return replyLen;
+ }
+ replyLen = iter->second.replyLen;
+ } else {
+ sif::debug << "PlocSupervisorHandler::getNextReplyLength: No entry for reply with reply id "
+ << std::hex << nextReplyId << " in deviceReplyMap" << std::endl;
+ }
+
+ return replyLen;
+}
+
+void PlocSupervisorHandler::doOffActivity() {}
+
+void PlocSupervisorHandler::handleDeviceTm(const uint8_t* data, size_t dataSize,
+ DeviceCommandId_t replyId) {
+ ReturnValue_t result = returnvalue::OK;
+
+ if (wiretappingMode == RAW) {
+ /* Data already sent in doGetRead() */
+ return;
+ }
+
+ DeviceReplyMap::iterator iter = deviceReplyMap.find(replyId);
+ if (iter == deviceReplyMap.end()) {
+ sif::debug << "PlocSupervisorHandler::handleDeviceTM: Unknown reply id" << std::endl;
+ return;
+ }
+ MessageQueueId_t queueId = iter->second.command->second.sendReplyTo;
+
+ if (queueId == NO_COMMANDER) {
+ return;
+ }
+
+ result = actionHelper.reportData(queueId, replyId, data, dataSize);
+ if (result != returnvalue::OK) {
+ sif::debug << "PlocSupervisorHandler::handleDeviceTM: Failed to report data" << std::endl;
+ }
+}
+
+ReturnValue_t PlocSupervisorHandler::prepareEmptyCmd(uint16_t apid, uint8_t serviceId) {
+ supv::NoPayloadPacket packet(spParams, apid, serviceId);
+ ReturnValue_t result = packet.buildPacket();
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(packet);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocSupervisorHandler::prepareSelBootImageCmd(const uint8_t* commandData) {
+ supv::MPSoCBootSelect packet(spParams);
+ ReturnValue_t result =
+ packet.buildPacket(commandData[0], commandData[1], commandData[2], commandData[3]);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(packet);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocSupervisorHandler::prepareSetTimeRefCmd() {
+ Clock::TimeOfDay_t time;
+ ReturnValue_t result = Clock::getDateAndTime(&time);
+ if (result != returnvalue::OK) {
+ sif::warning << "PlocSupervisorHandler::prepareSetTimeRefCmd: Failed to get current time"
+ << std::endl;
+ return result::GET_TIME_FAILURE;
+ }
+ supv::SetTimeRef packet(spParams);
+ result = packet.buildPacket(&time);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(packet);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocSupervisorHandler::prepareDisableHk() {
+ supv::DisablePeriodicHkTransmission packet(spParams);
+ ReturnValue_t result = packet.buildPacket();
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(packet);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocSupervisorHandler::prepareSetBootTimeoutCmd(const uint8_t* commandData) {
+ supv::SetBootTimeout packet(spParams);
+ uint32_t timeout = *(commandData) << 24 | *(commandData + 1) << 16 | *(commandData + 2) << 8 |
+ *(commandData + 3);
+ ReturnValue_t result = packet.buildPacket(timeout);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(packet);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocSupervisorHandler::prepareRestartTriesCmd(const uint8_t* commandData) {
+ uint8_t restartTries = *(commandData);
+ supv::SetRestartTries packet(spParams);
+ ReturnValue_t result = packet.buildPacket(restartTries);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(packet);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocSupervisorHandler::prepareLatchupConfigCmd(const uint8_t* commandData,
+ DeviceCommandId_t deviceCommand) {
+ ReturnValue_t result = returnvalue::OK;
+ uint8_t latchupId = *commandData;
+ if (latchupId > 6) {
+ return result::INVALID_LATCHUP_ID;
+ }
+ switch (deviceCommand) {
+ case (supv::ENABLE_LATCHUP_ALERT): {
+ supv::LatchupAlert packet(spParams);
+ result = packet.buildPacket(true, latchupId);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(packet);
+ break;
+ }
+ case (supv::DISABLE_LATCHUP_ALERT): {
+ supv::LatchupAlert packet(spParams);
+ result = packet.buildPacket(false, latchupId);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(packet);
+ break;
+ }
+ default: {
+ sif::debug << "PlocSupervisorHandler::prepareLatchupConfigCmd: Invalid command id"
+ << std::endl;
+ result = returnvalue::FAILED;
+ break;
+ }
+ }
+ return result;
+}
+
+ReturnValue_t PlocSupervisorHandler::prepareSetAlertLimitCmd(const uint8_t* commandData) {
+ uint8_t offset = 0;
+ uint8_t latchupId = *commandData;
+ offset += 1;
+ uint32_t dutycycle = *(commandData + offset) << 24 | *(commandData + offset + 1) << 16 |
+ *(commandData + offset + 2) << 8 | *(commandData + offset + 3);
+ if (latchupId > 6) {
+ return result::INVALID_LATCHUP_ID;
+ }
+ supv::SetAlertlimit packet(spParams);
+ ReturnValue_t result = packet.buildPacket(latchupId, dutycycle);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(packet);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocSupervisorHandler::prepareSetAdcEnabledChannelsCmd(const uint8_t* commandData) {
+ uint16_t ch = *(commandData) << 8 | *(commandData + 1);
+ supv::SetAdcEnabledChannels packet(spParams);
+ ReturnValue_t result = packet.buildPacket(ch);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(packet);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocSupervisorHandler::prepareSetAdcWindowAndStrideCmd(const uint8_t* commandData) {
+ uint8_t offset = 0;
+ uint16_t windowSize = *(commandData + offset) << 8 | *(commandData + offset + 1);
+ offset += 2;
+ uint16_t stridingStepSize = *(commandData + offset) << 8 | *(commandData + offset + 1);
+ supv::SetAdcWindowAndStride packet(spParams);
+ ReturnValue_t result = packet.buildPacket(windowSize, stridingStepSize);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(packet);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocSupervisorHandler::prepareSetAdcThresholdCmd(const uint8_t* commandData) {
+ uint32_t threshold = *(commandData) << 24 | *(commandData + 1) << 16 | *(commandData + 2) << 8 |
+ *(commandData + 3);
+ supv::SetAdcThreshold packet(spParams);
+ ReturnValue_t result = packet.buildPacket(threshold);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(packet);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocSupervisorHandler::prepareRunAutoEmTest(const uint8_t* commandData) {
+ uint8_t test = *commandData;
+ if (test != 1 && test != 2) {
+ return result::INVALID_TEST_PARAM;
+ }
+ supv::RunAutoEmTests packet(spParams);
+ ReturnValue_t result = packet.buildPacket(test);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(packet);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocSupervisorHandler::prepareSetGpioCmd(const uint8_t* commandData,
+ size_t commandDataLen) {
+ if (commandDataLen < 3) {
+ return HasActionsIF::INVALID_PARAMETERS;
+ }
+ uint8_t port = *commandData;
+ uint8_t pin = *(commandData + 1);
+ uint8_t val = *(commandData + 2);
+ supv::SetGpio packet(spParams);
+ ReturnValue_t result = packet.buildPacket(port, pin, val);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(packet);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocSupervisorHandler::prepareReadGpioCmd(const uint8_t* commandData,
+ size_t commandDataLen) {
+ if (commandDataLen < 2) {
+ return HasActionsIF::INVALID_PARAMETERS;
+ }
+ uint8_t port = *commandData;
+ uint8_t pin = *(commandData + 1);
+ supv::ReadGpio packet(spParams);
+ ReturnValue_t result = packet.buildPacket(port, pin);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(packet);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocSupervisorHandler::prepareFactoryResetCmd(const uint8_t* commandData,
+ size_t len) {
+ FactoryReset resetCmd(spParams);
+ if (len < 1) {
+ return HasActionsIF::INVALID_PARAMETERS;
+ }
+ ReturnValue_t result = resetCmd.buildPacket(commandData[0]);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(resetCmd);
+ return returnvalue::OK;
+}
+
+void PlocSupervisorHandler::finishTcPrep(TcBase& tc) {
+ nextReplyId = supv::ACK_REPORT;
+ rawPacket = commandBuffer;
+ rawPacketLen = tc.getFullPacketLen();
+ if (DEBUG_PLOC_SUPV) {
+ sif::debug << "PLOC SUPV: SEND PACKET Size " << tc.getFullPacketLen() << " Module APID "
+ << (int)tc.getModuleApid() << " Service ID " << (int)tc.getServiceId() << std::endl;
+ }
+}
+
+ReturnValue_t PlocSupervisorHandler::prepareSetShutdownTimeoutCmd(const uint8_t* commandData) {
+ uint32_t timeout = 0;
+ ReturnValue_t result = returnvalue::OK;
+ size_t size = sizeof(timeout);
+ result =
+ SerializeAdapter::deSerialize(&timeout, &commandData, &size, SerializeIF::Endianness::BIG);
+ if (result != returnvalue::OK) {
+ sif::warning
+ << "PlocSupervisorHandler::prepareSetShutdownTimeoutCmd: Failed to deserialize timeout"
+ << std::endl;
+ return result;
+ }
+ supv::SetShutdownTimeout packet(spParams);
+ result = packet.buildPacket(timeout);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(packet);
+ return returnvalue::OK;
+}
+
+void PlocSupervisorHandler::disableAllReplies() {
+ using namespace supv;
+ DeviceReplyMap::iterator iter;
+
+ /* Disable ack reply */
+ iter = deviceReplyMap.find(ACK_REPORT);
+ if (iter == deviceReplyMap.end()) {
+ return;
+ }
+ DeviceReplyInfo* info = &(iter->second);
+ if (info == nullptr) {
+ return;
+ }
+ info->delayCycles = 0;
+ info->command = deviceCommandMap.end();
+
+ DeviceCommandId_t commandId = getPendingCommand();
+
+ /* If the command expects a telemetry packet the appropriate tm reply will be disabled here */
+ switch (commandId) {
+ case GET_HK_REPORT: {
+ disableReply(GET_HK_REPORT);
+ break;
+ }
+ case FIRST_MRAM_DUMP:
+ case CONSECUTIVE_MRAM_DUMP: {
+ disableReply(commandId);
+ break;
+ }
+ case REQUEST_ADC_REPORT: {
+ disableReply(ADC_REPORT);
+ break;
+ }
+ case GET_BOOT_STATUS_REPORT: {
+ disableReply(BOOT_STATUS_REPORT);
+ break;
+ }
+ case GET_LATCHUP_STATUS_REPORT: {
+ disableReply(LATCHUP_REPORT);
+ break;
+ }
+ case REQUEST_LOGGING_COUNTERS: {
+ disableReply(COUNTERS_REPORT);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+ /* We must always disable the execution report reply here */
+ disableExeReportReply();
+}
+
+void PlocSupervisorHandler::disableReply(DeviceCommandId_t replyId) {
+ DeviceReplyMap::iterator iter = deviceReplyMap.find(replyId);
+ if (iter == deviceReplyMap.end()) {
+ return;
+ }
+ DeviceReplyInfo* info = &(iter->second);
+ info->delayCycles = 0;
+ info->active = false;
+ info->command = deviceCommandMap.end();
+}
+
+void PlocSupervisorHandler::sendFailureReport(DeviceCommandId_t replyId, ReturnValue_t status) {
+ DeviceReplyIter iter = deviceReplyMap.find(replyId);
+
+ if (iter == deviceReplyMap.end()) {
+ sif::debug << "PlocSupervisorHandler::sendFailureReport: Reply not in reply map" << std::endl;
+ return;
+ }
+
+ DeviceCommandInfo* info = &(iter->second.command->second);
+
+ if (info == nullptr) {
+ sif::debug << "PlocSupervisorHandler::sendFailureReport: Reply has no active command"
+ << std::endl;
+ return;
+ }
+
+ if (info->sendReplyTo != NO_COMMANDER) {
+ actionHelper.finish(false, info->sendReplyTo, iter->first, status);
+ }
+ info->isExecuting = false;
+}
+
+void PlocSupervisorHandler::disableExeReportReply() {
+ DeviceReplyIter iter = deviceReplyMap.find(supv::EXE_REPORT);
+ if (iter == deviceReplyMap.end()) {
+ return;
+ }
+ DeviceReplyInfo* info = &(iter->second);
+ info->delayCycles = 0;
+ info->command = deviceCommandMap.end();
+ info->active = false;
+ /* Expected replies is set to one here. The value will set to 0 in replyToReply() */
+ info->command->second.expectedReplies = 1;
+}
+
+ReturnValue_t PlocSupervisorHandler::handleMramDumpPacket(DeviceCommandId_t id) {
+ ReturnValue_t result = returnvalue::FAILED;
+
+ // Prepare packet for downlink
+ if (packetInBuffer) {
+ uint16_t packetLen = readSpacePacketLength(spacePacketBuffer);
+ result = verifyPacket(spacePacketBuffer, ccsds::HEADER_LEN + packetLen + 1);
+ if (result != returnvalue::OK) {
+ sif::warning << "PlocSupervisorHandler::handleMramDumpPacket: CRC failure" << std::endl;
+ return result;
+ }
+ result = handleMramDumpFile(id);
+ if (result != returnvalue::OK) {
+ DeviceCommandMap::iterator iter = deviceCommandMap.find(id);
+ if (iter != deviceCommandMap.end()) {
+ actionHelper.finish(false, iter->second.sendReplyTo, id, result);
+ }
+ disableAllReplies();
+ nextReplyId = supv::NONE;
+ return result;
+ }
+ packetInBuffer = false;
+ receivedMramDumpPackets++;
+ if (expectedMramDumpPackets == receivedMramDumpPackets) {
+ nextReplyId = supv::EXE_REPORT;
+ }
+ increaseExpectedMramReplies(id);
+ return returnvalue::OK;
+ }
+ return result;
+}
+
+void PlocSupervisorHandler::increaseExpectedMramReplies(DeviceCommandId_t id) {
+ DeviceReplyMap::iterator mramDumpIter = deviceReplyMap.find(id);
+ DeviceReplyMap::iterator exeReportIter = deviceReplyMap.find(supv::EXE_REPORT);
+ if (mramDumpIter == deviceReplyMap.end()) {
+ sif::debug << "PlocSupervisorHandler::increaseExpectedMramReplies: Dump MRAM reply not "
+ << "in reply map" << std::endl;
+ return;
+ }
+ if (exeReportIter == deviceReplyMap.end()) {
+ sif::debug << "PlocSupervisorHandler::increaseExpectedMramReplies: Execution report not "
+ << "in reply map" << std::endl;
+ return;
+ }
+ DeviceReplyInfo* mramReplyInfo = &(mramDumpIter->second);
+ if (mramReplyInfo == nullptr) {
+ sif::debug << "PlocSupervisorHandler::increaseExpectedReplies: MRAM reply info nullptr"
+ << std::endl;
+ return;
+ }
+ DeviceReplyInfo* exeReplyInfo = &(exeReportIter->second);
+ if (exeReplyInfo == nullptr) {
+ sif::debug << "PlocSupervisorHandler::increaseExpectedReplies: Execution reply info"
+ << " nullptr" << std::endl;
+ return;
+ }
+ DeviceCommandInfo* info = &(mramReplyInfo->command->second);
+ if (info == nullptr) {
+ sif::debug << "PlocSupervisorHandler::increaseExpectedReplies: Command info nullptr"
+ << std::endl;
+ return;
+ }
+ uint8_t sequenceFlags = spacePacketBuffer[2] >> 6;
+ if (sequenceFlags != static_cast(ccsds::SequenceFlags::LAST_SEGMENT) &&
+ (sequenceFlags != static_cast(ccsds::SequenceFlags::UNSEGMENTED))) {
+ // Command expects at least one MRAM packet more and the execution report
+ info->expectedReplies = 2;
+ mramReplyInfo->countdown->resetTimer();
+ } else {
+ // Command expects the execution report
+ info->expectedReplies = 1;
+ mramReplyInfo->active = false;
+ }
+ exeReplyInfo->countdown->resetTimer();
+ return;
+}
+
+ReturnValue_t PlocSupervisorHandler::handleMramDumpFile(DeviceCommandId_t id) {
+#ifdef XIPHOS_Q7S
+ if (not sdcMan->getActiveSdCard()) {
+ return HasFileSystemIF::FILESYSTEM_INACTIVE;
+ }
+#endif
+ ReturnValue_t result = returnvalue::OK;
+ uint16_t packetLen = readSpacePacketLength(spacePacketBuffer);
+ uint8_t sequenceFlags = readSequenceFlags(spacePacketBuffer);
+ if (id == supv::FIRST_MRAM_DUMP) {
+ if (sequenceFlags == static_cast(ccsds::SequenceFlags::FIRST_SEGMENT) ||
+ (sequenceFlags == static_cast(ccsds::SequenceFlags::UNSEGMENTED))) {
+ result = createMramDumpFile();
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ }
+ }
+ if (not std::filesystem::exists(activeMramFile)) {
+ sif::warning << "PlocSupervisorHandler::handleMramDumpFile: MRAM file does not exist"
+ << std::endl;
+ return result::MRAM_FILE_NOT_EXISTS;
+ }
+ std::ofstream file(activeMramFile, std::ios_base::app | std::ios_base::out);
+ file.write(reinterpret_cast(spacePacketBuffer + ccsds::HEADER_LEN), packetLen - 1);
+ file.close();
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocSupervisorHandler::prepareWipeMramCmd(const uint8_t* commandData) {
+ uint32_t start = 0;
+ uint32_t stop = 0;
+ size_t size = sizeof(start) + sizeof(stop);
+ SerializeAdapter::deSerialize(&start, &commandData, &size, SerializeIF::Endianness::BIG);
+ SerializeAdapter::deSerialize(&stop, &commandData, &size, SerializeIF::Endianness::BIG);
+ if ((stop - start) <= 0) {
+ return result::INVALID_MRAM_ADDRESSES;
+ }
+ supv::MramCmd packet(spParams);
+ ReturnValue_t result = packet.buildPacket(start, stop, supv::MramCmd::MramAction::WIPE);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+ finishTcPrep(packet);
+ return returnvalue::OK;
+}
+
+uint16_t PlocSupervisorHandler::readSpacePacketLength(uint8_t* spacePacket) {
+ return spacePacket[4] << 8 | spacePacket[5];
+}
+
+uint8_t PlocSupervisorHandler::readSequenceFlags(uint8_t* spacePacket) {
+ return spacePacketBuffer[2] >> 6;
+}
+
+ReturnValue_t PlocSupervisorHandler::createMramDumpFile() {
+ ReturnValue_t result = returnvalue::OK;
+ std::string timeStamp;
+ result = getTimeStampString(timeStamp);
+ if (result != returnvalue::OK) {
+ return result;
+ }
+
+ std::string filename = "mram-dump--" + timeStamp + ".bin";
+
+#ifdef XIPHOS_Q7S
+ const char* currentMountPrefix = sdcMan->getCurrentMountPrefix();
+#else
+ const char* currentMountPrefix = "/mnt/sd0";
+#endif /* BOARD_TE0720 == 0 */
+ if (currentMountPrefix == nullptr) {
+ return returnvalue::FAILED;
+ }
+
+ // Check if path to PLOC directory exists
+ if (not std::filesystem::exists(std::string(currentMountPrefix) + "/" + supervisorFilePath)) {
+ sif::warning << "PlocSupervisorHandler::createMramDumpFile: Supervisor path does not exist"
+ << std::endl;
+ return result::PATH_DOES_NOT_EXIST;
+ }
+ activeMramFile = std::string(currentMountPrefix) + "/" + supervisorFilePath + "/" + filename;
+ // Create new file
+ std::ofstream file(activeMramFile, std::ios_base::out);
+ file.close();
+
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocSupervisorHandler::getTimeStampString(std::string& timeStamp) {
+ Clock::TimeOfDay_t time;
+ ReturnValue_t result = Clock::getDateAndTime(&time);
+ if (result != returnvalue::OK) {
+ sif::warning << "PlocSupervisorHandler::getTimeStampString: Failed to get current time"
+ << std::endl;
+ return result::GET_TIME_FAILURE;
+ }
+ timeStamp = std::to_string(time.year) + "-" + std::to_string(time.month) + "-" +
+ std::to_string(time.day) + "--" + std::to_string(time.hour) + "-" +
+ std::to_string(time.minute) + "-" + std::to_string(time.second);
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocSupervisorHandler::extractUpdateCommand(const uint8_t* commandData, size_t size,
+ supv::UpdateParams& params) {
+ size_t remSize = size;
+ if (size > (config::MAX_FILENAME_SIZE + config::MAX_PATH_SIZE + sizeof(params.memId)) +
+ sizeof(params.startAddr) + sizeof(params.bytesWritten) + sizeof(params.seqCount) +
+ sizeof(uint8_t)) {
+ sif::warning << "PlocSupervisorHandler::extractUpdateCommand: Data size too big" << std::endl;
+ return result::INVALID_LENGTH;
+ }
+ ReturnValue_t result = returnvalue::OK;
+ result = extractBaseParams(&commandData, size, params);
+ result = SerializeAdapter::deSerialize(¶ms.bytesWritten, &commandData, &remSize,
+ SerializeIF::Endianness::BIG);
+ if (result != returnvalue::OK) {
+ sif::warning << "PlocSupervisorHandler::extractUpdateCommand: Failed to deserialize bytes "
+ "already written"
+ << std::endl;
+ return result;
+ }
+ result = SerializeAdapter::deSerialize(¶ms.seqCount, &commandData, &remSize,
+ SerializeIF::Endianness::BIG);
+ if (result != returnvalue::OK) {
+ sif::warning
+ << "PlocSupervisorHandler::extractUpdateCommand: Failed to deserialize start sequence count"
+ << std::endl;
+ return result;
+ }
+ uint8_t delMemRaw = 0;
+ result = SerializeAdapter::deSerialize(&delMemRaw, &commandData, &remSize,
+ SerializeIF::Endianness::BIG);
+ if (result != returnvalue::OK) {
+ sif::warning
+ << "PlocSupervisorHandler::extractUpdateCommand: Failed to deserialize whether to delete "
+ "memory"
+ << std::endl;
+ return result;
+ }
+ params.deleteMemory = delMemRaw;
+ return returnvalue::OK;
+}
+
+ReturnValue_t PlocSupervisorHandler::extractBaseParams(const uint8_t** commandData, size_t& remSize,
+ supv::UpdateParams& params) {
+ bool nullTermFound = false;
+ for (size_t idx = 0; idx < remSize; idx++) {
+ if ((*commandData)[idx] == '\0') {
+ nullTermFound = true;
+ break;
+ }
+ }
+ if (not nullTermFound) {
+ return returnvalue::FAILED;
+ }
+ params.file = std::string(reinterpret_cast