diff --git a/CMakeLists.txt b/CMakeLists.txt
index b5abe9ab..5b1b94f8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -52,6 +52,7 @@ set(LIB_FSFW_HAL_NAME fsfw_hal)
set(LIB_LWGPS_NAME lwgps)
set(LIB_ARCSEC wire)
set(THIRD_PARTY_FOLDER thirdparty)
+set(LIB_CXX_FS -lstdc++fs)
# Set path names
set(FSFW_PATH fsfw)
@@ -168,6 +169,7 @@ if(NOT Q7S_SIMPLE_MODE)
${LIB_LWGPS_NAME}
${LIB_FSFW_HAL_NAME}
${LIB_ARCSEC}
+ ${LIB_CXX_FS}
)
endif()
diff --git a/README.md b/README.md
index 787d40f2..68a5df30 100644
--- a/README.md
+++ b/README.md
@@ -1,19 +1,26 @@
-# EIVE On-Board Software
+ EIVE On-Board Software
+======
-## General information
+# 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 informatin about the Q7S can be found on the xiphos trac platform: https://trac.xiphos.com/trac/eive-q7/wiki/Q7RevB
- * Linux OS built with Yocto 2.5
- * Linux Kernel https://github.com/XiphosSystemsCorp/linux-xlnx.git
+ * 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.
+ * Linux OS built with Yocto 2.5
+ * 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 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
@@ -28,30 +35,56 @@ The CMake build system can be used to generate build systems as well (see helper
- 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.
-## Setting up development environment
+# Setting up development environment
-### Installing Vivado the the Xilinx development tools
+## Installing Vivado the 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.
+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 from 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 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
-* Add path of linux cross-compiler to permanent environment variables (`.profile` file in Linux):
+* 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 toolchain without Vivado
+### 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. ```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)
+
+## 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&fileid=831898).
@@ -69,7 +102,7 @@ or the following command for Linux (could be useful for CI/CD)
wget https://eive-cloud.irs.uni-stuttgart.de/index.php/s/2Fp2ag6NGnbtAsK/download/gcc-arm-linux-gnueabi.tar.gz
```
-### Installing CMake and MSYS2 on Windows
+## Installing CMake and MSYS2 on Windows
1. Install [MSYS2](https://www.msys2.org/) and [CMake](https://cmake.org/download/) first.
@@ -94,7 +127,7 @@ wget https://eive-cloud.irs.uni-stuttgart.de/index.php/s/2Fp2ag6NGnbtAsK/downloa
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
+## Installing CMake on Linux
1. Run the following command
@@ -117,7 +150,7 @@ wget https://eive-cloud.irs.uni-stuttgart.de/index.php/s/agnJGYeRf6fw2ci/downloa
Then, create a new environmental variables `Q7S_SYSROOT` and set it to the local system root path.
-## Building the software with CMake
+# Building the software with CMake
When using Windows, run theses steps in MSYS2.
@@ -153,7 +186,7 @@ When using Windows, run theses steps in MSYS2.
```sh
cd cmake/scripts/Q7S
- ./create_cmake_debug.sh
+ ./make_debug_cfg.sh
cd ../../..
```
@@ -230,7 +263,7 @@ IP address and path settings differ from machine to machine.
You can run it manually there. To perform auto-start on boot, have a look at the start-up
application section.
-## Debugging the software via Flatsat PC
+# Debugging the software via Flatsat PC
Open SSH connection to flatsat PC:
@@ -269,7 +302,8 @@ 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.
+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
@@ -289,12 +323,14 @@ To launch application from Xilinx SDK setup port fowarding on the development ma
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 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 to do this quickly.
-Note: When now setting up a debug session in the Xilinx SDK, the host must be set to localhost instead of the IP address of the Q7S.
+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.
-## Transfering files via SCP
+# Transfering files via SCP
To transfer files from the local machine to the Q7S, use port forwarding
@@ -318,11 +354,11 @@ From a windows machine files can be copied with putty tools (note: use IPv4 addr
pscp -scp -P 22 eive@192.168.199.227:/example-file
````
-## Launching an application at start-up
+# Launching an application at start-up
-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.
+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
@@ -340,11 +376,13 @@ the parition needs to be mounted.
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 /lib/systemd/system. The following shows an example service.
+
```sh
cat > example.service
[Unit]
@@ -361,8 +399,8 @@ the parition needs to be mounted.
[Install]
WantedBy=multi-user.target
```
-6. Enable the service. This is normally done with systemctl enable. However, this is not possible when the service is
- created for a mounted root partition. Therefore create a symlink as follows.
+6. Enable the service. This is normally done with systemctl enable. However, this is not possible
+ when the service is created for a mounted root partition. Therefore create a symlink as follows.
```sh
ln -s '/tmp/the-mounted-xdi-image/lib/systemd/system/example.service' '/tmp/the-mounted-xdi-image/etc/systemd/system/multi-user.target.wants/example.service'
```
@@ -380,37 +418,14 @@ the parition needs to be mounted.
```sh
systemctl status example
```
+
More detailed information about the used q7s commands can be found in the Q7S user manual.
-### Bringing up 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
-````
-
## 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.
+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:
@@ -435,58 +450,27 @@ required for some framework components. The recommended values for the new messa
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
-```
-or running the `unlockRealtime` script.
+ ```sh
+ sudo nano /etc/sysctl.conf
+ ```
-3. Run the shell script inside the linux folder
-```sh
-./unlockRealtime
-```
-This script executes the `sudo setcap 'cap_sys_nice=eip' \`
-command on the binaries, increases the soft real time limit of the current
-session and increases the maximum number of message queues by setting
-`/proc/sys/fs/mqueue/msg_max`.
-All changes are only applied for the current session (read 2. and 3. for
-a permanent solution). If running the script before executing the binary does
-not help or an warning is issue that the soft real time value is invalid,
-the hard real-time limit of the system might not be high enough (see step 1).
+ Append at end:
+ ```sh
+ fs/mqueue/msg_max =
+ ```
+
+ Apply changes with:
+ ```sh
+ sudo sysctl -p
+ ```
-## Flight Software Framework (FSFW)
+ A possible solution which only persists for the current session is
+ ```sh
+ echo | sudo tee /proc/sys/fs/mqueue/msg_max
+ ```
-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.
-
-## PCDU
+# PCDU
Connect to serial console of P60 Dock
````
@@ -511,10 +495,11 @@ p60-dock # param get out_en[0]
GET out_en[0] = 1
````
-## Debugging the software (when workstation is directly conncected to Q7S)
+# Debugging the software (when workstation is directly conncected to Q7S)
1. Assign static IP address to Q7S
- * Open serial console of Q7S (Accessible via the micro-USB of the PIM, see also Q7S user maunal chapter 10.3)
+ * 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
@@ -562,10 +547,10 @@ GET out_en[0] = 1
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 eiveobsw-linux.elf (in _bin\linux\devel)
- * Remote File Path: /tmp/eive_obsw.elf
+ * Local File Path: Path to eiveobsw-linux.elf (in `_bin\linux\devel`)
+ * Remote File Path: `/tmp/eive_obsw.elf`
-## Running cppcheck on the Software
+# 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.
@@ -597,6 +582,47 @@ Finally, you can convert the generated `.xml` file to HTML with the following co
cppcheck-htmlreport --file=report.xml --report-dir=cppcheck --source-dir=..
```
+# Special notes on 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.
+
+# Q7S Utilities and Troubleshooting
+
+## 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 `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:
@@ -626,10 +652,84 @@ 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).
+Both the MIOs and EMIOs can be accessed via the zynq_gpio instance which
+comprises 118 pins (54 MIOs and 64 EMIOs).
-## Running the EIVE OBSW on a Raspberry Pi
+## Xilinx UARTLIE
+
+Get info about ttyUL* devices
+````
+cat /proc/tty/driver
+````
+
+## I2C
+
+Getting information about I2C device
+````
+ls /sys/class/i2c-dev/i2c-0/device/device/driver
+````
+This shows the memory mapping of /dev/i2c-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
+````
+
+## Useful Q7S Linux Commands
+
+Display currently running image:
+
+```sh
+xsc_boot_copy
+```
+
+Rebooting currently running image:
+
+```sh
+xsc_boot_copy -r
+```
+
+## Preparation of a fresh rootfs and SD card
+
+This section summarizes important changes between a fresh rootfs and the current
+EIVE implementation
+
+### rootfs
+
+- Mount point `/mnt/sd0` created for SD card 0. Created with `mkdir`
+- Mount point `/mnt/sd1` created for SD card 1. Created with `mkdir`
+- Folder `scripts` in `/home/root` folder.
+- `scripts` folder currently contains `update_main_components.sh` script
+
+### SD Cards
+
+- Folder `bin` for binaries, for example the OBSW
+- Folder `misc` for miscellaneous files
+- Folder `tc` for telecommands
+- Folder `tm` for telemetry
+- Folder `xdi` for XDI components (e.g. for firmware or device tree updates)
+
+# 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.
@@ -645,26 +745,20 @@ sudo apt-get install gpiod libgpiod-dev
to install the required GPIO libraries before cloning the system root folder.
-## Special notes on Eclipse
+# Flight Software Framework (FSFW)
-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.
+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:
-## Xilinx UARTLIE
-Get info about ttyUL* devices
-````
-cat /proc/tty/driver
-````
+```sh
+git remote add upstream https://egit.irs.uni-stuttgart.de/fsfw/fsfw.git
+git remote update --prune
+```
-## I2C
-Getting information about I2C device
-````
-ls /sys/class/i2c-dev/i2c-0/device/device/driver
-````
-This shows the memory mapping of /dev/i2c-0
+After that, an update can be merged by running
+<<<<<<< HEAD
## Useful Q7S Linux Commands
Rebooting currently running image:
````
@@ -688,4 +782,11 @@ hwclock -w
Reading the real time clock
````
hwclock --show
-````
\ No newline at end of file
+````
+=======
+```sh
+git merge upstream/master
+```
+
+Alternatively, changes from other upstreams (forks) and branches can be merged like that in the same way.
+>>>>>>> develop
diff --git a/bsp_q7s/CMakeLists.txt b/bsp_q7s/CMakeLists.txt
index abf785e6..3bc9aa4e 100644
--- a/bsp_q7s/CMakeLists.txt
+++ b/bsp_q7s/CMakeLists.txt
@@ -2,13 +2,15 @@ target_sources(${TARGET_NAME} PUBLIC
main.cpp
)
+add_subdirectory(boardtest)
+
if(Q7S_SIMPLE_MODE)
add_subdirectory(simple)
else()
add_subdirectory(boardconfig)
add_subdirectory(comIF)
- add_subdirectory(boardtest)
add_subdirectory(gpio)
add_subdirectory(core)
- add_subdirectory(spiCallbacks)
+ add_subdirectory(memory)
+ add_subdirectory(spiCallbacks)
endif()
diff --git a/bsp_q7s/boardconfig/q7sConfig.h.in b/bsp_q7s/boardconfig/q7sConfig.h.in
index 6ee84821..11dec25c 100644
--- a/bsp_q7s/boardconfig/q7sConfig.h.in
+++ b/bsp_q7s/boardconfig/q7sConfig.h.in
@@ -1,7 +1,20 @@
#ifndef BSP_Q7S_BOARDCONFIG_Q7S_CONFIG_H_
#define BSP_Q7S_BOARDCONFIG_Q7S_CONFIG_H_
+#include
+
#cmakedefine01 Q7S_SIMPLE_MODE
+
+#define Q7S_SD_NONE 0
+#define Q7S_SD_COLD_REDUNDANT 1
+#define Q7S_SD_HOT_REDUNDANT 2
+// The OBSW will perform different actions to set up the SD cards depending on the flag set here
+// Set to Q7S_SD_NONE: Don't do anything
+// Set to Q7S_COLD_REDUNDANT: On startup, get the prefered SD card, turn it on and mount it, and
+// turn off the second SD card if it is on
+// Set to Q7S_HOT_REDUNDANT: On startup, turn on both SD cards and mount them
+#define Q7S_SD_CARD_CONFIG Q7S_SD_COLD_REDUNDANT
+
#define Q7S_ADD_RTD_DEVICES 0
/* Only one of those 2 should be enabled! */
@@ -11,4 +24,12 @@
#define Q7S_ADD_SPI_TEST 0
#endif
+#define Q7S_SIMPLE_ADD_FILE_SYSTEM_TEST 0
+
+namespace config {
+
+static const uint32_t SD_CARD_ACCESS_MUTEX_TIMEOUT = 50;
+
+}
+
#endif /* BSP_Q7S_BOARDCONFIG_Q7S_CONFIG_H_ */
diff --git a/bsp_q7s/boardtest/CMakeLists.txt b/bsp_q7s/boardtest/CMakeLists.txt
index 0599b73f..1cda38ca 100644
--- a/bsp_q7s/boardtest/CMakeLists.txt
+++ b/bsp_q7s/boardtest/CMakeLists.txt
@@ -1,4 +1,6 @@
target_sources(${TARGET_NAME} PRIVATE
+ FileSystemTest.cpp
+ Q7STestTask.cpp
)
diff --git a/bsp_q7s/boardtest/FileSystemTest.cpp b/bsp_q7s/boardtest/FileSystemTest.cpp
new file mode 100644
index 00000000..1de5bb7a
--- /dev/null
+++ b/bsp_q7s/boardtest/FileSystemTest.cpp
@@ -0,0 +1,21 @@
+#include "FileSystemTest.h"
+#include "fsfw/timemanager/Stopwatch.h"
+
+#include
+#include
+
+FileSystemTest::FileSystemTest() {
+ using namespace std;
+ SdCard sdCard = SdCard::SDC0;
+ cout << "SD Card Test for SD card " << static_cast(sdCard) << std::endl;
+ //Stopwatch stopwatch;
+ std::system("q7hw sd info all > /tmp/sd_status.txt");
+ //stopwatch.stop(true);
+ std::system("q7hw sd set 0 on > /tmp/sd_set.txt");
+ //stopwatch.stop(true);
+ std::system("q7hw sd set 0 off > /tmp/sd_set.txt");
+ //stopwatch.stop(true);
+}
+
+FileSystemTest::~FileSystemTest() {
+}
diff --git a/bsp_q7s/boardtest/FileSystemTest.h b/bsp_q7s/boardtest/FileSystemTest.h
new file mode 100644
index 00000000..907c86ca
--- /dev/null
+++ b/bsp_q7s/boardtest/FileSystemTest.h
@@ -0,0 +1,18 @@
+#ifndef BSP_Q7S_BOARDTEST_FILESYSTEMTEST_H_
+#define BSP_Q7S_BOARDTEST_FILESYSTEMTEST_H_
+
+enum SdCard {
+ SDC0,
+ SDC1
+};
+
+class FileSystemTest {
+public:
+ FileSystemTest();
+ virtual~ FileSystemTest();
+private:
+};
+
+
+
+#endif /* BSP_Q7S_BOARDTEST_FILESYSTEMTEST_H_ */
diff --git a/bsp_q7s/boardtest/Q7STestTask.cpp b/bsp_q7s/boardtest/Q7STestTask.cpp
new file mode 100644
index 00000000..a1470b79
--- /dev/null
+++ b/bsp_q7s/boardtest/Q7STestTask.cpp
@@ -0,0 +1,67 @@
+#include "Q7STestTask.h"
+
+#include "fsfw/timemanager/Stopwatch.h"
+#include "fsfw/tasks/TaskFactory.h"
+
+#include "bsp_q7s/memory/scratchApi.h"
+
+#include
+#include
+#include
+
+Q7STestTask::Q7STestTask(object_id_t objectId): TestTask(objectId) {
+}
+
+ReturnValue_t Q7STestTask::performOneShotAction() {
+ //sdCardTests();
+ testScratchApi();
+ return TestTask::performOneShotAction();
+}
+
+void Q7STestTask::sdCardTests() {
+ using namespace std;
+ Stopwatch stopwatch;
+ int result = std::system("q7hw sd info all > /tmp/sd_status.txt");
+ if(result != 0) {
+ sif::debug << "system call failed with " << result << endl;
+ }
+ ifstream sdStatus("/tmp/sd_status.txt");
+ string line;
+ uint8_t idx = 0;
+ while (std::getline(sdStatus, line)) {
+ std::istringstream iss(line);
+ string word;
+ while(iss >> word) {
+ if(word == "on") {
+ sif::info << "SD card " << static_cast(idx) << " is on" << endl;
+ }
+ else if(word == "off") {
+ sif::info << "SD card " << static_cast(idx) << " is off" << endl;
+ }
+ }
+ idx++;
+ }
+ std::remove("/tmp/sd_status.txt");
+}
+
+void Q7STestTask::fileTests() {
+ using namespace std;
+ ofstream testFile("/tmp/test.txt");
+ testFile << "Hallo Welt" << endl;
+ testFile.close();
+
+ system("echo \"Hallo Welt\" > /tmp/test2.txt");
+ system("echo \"Hallo Welt\"");
+}
+
+void Q7STestTask::testScratchApi() {
+ ReturnValue_t result = scratch::writeNumber("TEST", 1);
+ if(result != HasReturnvaluesIF::RETURN_OK) {
+ sif::debug << "Q7STestTask::scratchApiTest: Writing number failed" << std::endl;
+ }
+ int number = 0;
+ result = scratch::readNumber("TEST", number);
+ if(result != HasReturnvaluesIF::RETURN_OK) {
+ sif::debug << "Q7STestTask::scratchApiTest: Reading number failed" << std::endl;
+ }
+}
diff --git a/bsp_q7s/boardtest/Q7STestTask.h b/bsp_q7s/boardtest/Q7STestTask.h
new file mode 100644
index 00000000..664a8fa2
--- /dev/null
+++ b/bsp_q7s/boardtest/Q7STestTask.h
@@ -0,0 +1,20 @@
+#ifndef BSP_Q7S_BOARDTEST_Q7STESTTASK_H_
+#define BSP_Q7S_BOARDTEST_Q7STESTTASK_H_
+
+#include "test/testtasks/TestTask.h"
+
+class Q7STestTask: public TestTask {
+public:
+ Q7STestTask(object_id_t objectId);
+private:
+ ReturnValue_t performOneShotAction() override;
+
+ void sdCardTests();
+ void fileTests();
+
+ void testScratchApi();
+
+};
+
+
+#endif /* BSP_Q7S_BOARDTEST_Q7STESTTASK_H_ */
diff --git a/bsp_q7s/core/CoreController.cpp b/bsp_q7s/core/CoreController.cpp
index 253efd91..c4863260 100644
--- a/bsp_q7s/core/CoreController.cpp
+++ b/bsp_q7s/core/CoreController.cpp
@@ -1,4 +1,7 @@
#include "CoreController.h"
+#include "q7sConfig.h"
+
+#include "../memory/SdCardManager.h"
CoreController::CoreController(object_id_t objectId):
ExtendedControllerBase(objectId, objects::NO_OBJECT, 5) {
@@ -20,7 +23,126 @@ LocalPoolDataSetBase* CoreController::getDataSetHandle(sid_t sid) {
return nullptr;
}
+ReturnValue_t CoreController::initialize() {
+ return sdCardInit();
+}
+
ReturnValue_t CoreController::checkModeCommand(Mode_t mode, Submode_t submode,
uint32_t *msToReachTheMode) {
return HasReturnvaluesIF::RETURN_OK;
}
+
+ReturnValue_t CoreController::sdCardInit() {
+#if Q7S_SD_CARD_CONFIG == Q7S_SD_NONE
+ sif::info << "No SD card initialization will be performed" << std::endl;
+ return HasReturnvaluesIF::RETURN_OK;
+#else
+ SdCardManager* sdcMan = SdCardManager::instance();
+
+ // Create update status file
+ ReturnValue_t result = sdcMan->updateSdCardStateFile();
+ if(result != HasReturnvaluesIF::RETURN_OK) {
+ sif::warning << "CoreController::initialize: Updating SD card state file failed"
+ << std::endl;
+ }
+
+ auto sdStatus = std::pair(sd::SdStatus::OFF, sd::SdStatus::OFF);
+ result = sdcMan->getSdCardActiveStatus(sdStatus);
+ if(result != HasReturnvaluesIF::RETURN_OK) {
+ sif::warning << "Getting SD card activity status failed" << std::endl;
+ }
+
+ // Use a lambda to avoid duplicate code
+ auto setUpSdCard = [&](sd::SdCard sdCard, sd::SdStatus status, std::string sdString) {
+ std::string mountString;
+ if(sdCard == sd::SdCard::SLOT_0) {
+ mountString = SdCardManager::SD_0_MOUNT_POINT;
+ }
+ else {
+ mountString = SdCardManager::SD_1_MOUNT_POINT;
+ }
+
+ if(status == sd::SdStatus::OFF) {
+ sif::info << "Switching on and mounting SD card " << sdString << " at " <<
+ mountString << std::endl;
+ return sdcMan->switchOnSdCard(sdCard, true, &sdStatus);
+ }
+ else if(status == sd::SdStatus::ON) {
+ sif::info << "Mounting SD card " << sdString << " at " << mountString << std::endl;
+ return sdcMan->mountSdCard(sdCard);
+ }
+ else {
+ sif::info << "SD card " << sdString << " already on and mounted at " <<
+ mountString << std::endl;
+ return SdCardManager::ALREADY_MOUNTED;
+ }
+ };
+
+#if Q7S_SD_CARD_CONFIG == Q7S_SD_COLD_REDUNDANT
+ sd::SdCard preferredSdCard = sd::SdCard::SLOT_0;
+ result = sdcMan->getPreferredSdCard(preferredSdCard);
+ if(result != HasReturnvaluesIF::RETURN_OK) {
+ sif::warning << "Could not get preferred SD card information from the scratch buffer"
+ << std::endl;
+ }
+ std::string preferredString;
+ sd::SdStatus preferredStatus = sd::SdStatus::OFF;
+
+ sd::SdStatus otherStatus = sd::SdStatus::OFF;
+ std::string otherString;
+ sd::SdCard otherSdc = sd::SdCard::SLOT_0;
+
+ if(preferredSdCard == sd::SdCard::SLOT_0) {
+ preferredStatus = sdStatus.first;
+ preferredString = "0";
+ otherSdc = sd::SdCard::SLOT_1;
+ otherStatus = sdStatus.second;
+ otherString = "1";
+ }
+ else {
+ preferredString = "1";
+ preferredStatus = sdStatus.second;
+ otherStatus = sdStatus.first;
+ otherSdc = sd::SdCard::SLOT_0;
+ otherString = "0";
+ }
+
+ sif::info << "Cold redundant SD card configuration, preferred SD card " <<
+ preferredString << std::endl;
+
+ result = setUpSdCard(preferredSdCard, preferredStatus, preferredString);
+ if(result != SdCardManager::ALREADY_MOUNTED and result != HasReturnvaluesIF::RETURN_OK) {
+ sif::warning << "Setting up preferred card " << otherString <<
+ " in cold redundant mode failed" << std::endl;
+ // Try other SD card and mark set up operation as failed
+ setUpSdCard(otherSdc, otherStatus, otherString);
+ result = HasReturnvaluesIF::RETURN_FAILED;
+ }
+
+ if(result != HasReturnvaluesIF::RETURN_FAILED and otherStatus != sd::SdStatus::OFF) {
+ sif::info << "Switching off secondary SD card " << otherString << std::endl;
+ // Switch off other SD card in cold redundant mode if setting up preferred one walked
+ // without issues
+ result = sdcMan->switchOffSdCard(otherSdc, otherStatus, &sdStatus);
+ if(result != HasReturnvaluesIF::RETURN_OK and result != SdCardManager::ALREADY_OFF) {
+ sif::warning << "Switching off secondary SD card " << otherString <<
+ " in cold redundant mode failed" << std::endl;
+ }
+ }
+
+ // Update status file
+ sdcMan->updateSdCardStateFile();
+ return HasReturnvaluesIF::RETURN_OK;
+#elif Q7S_SD_CARD_CONFIG == Q7S_SD_HOT_REDUNDANT
+ sif::info << "Hot redundant SD card configuration" << std::endl;
+
+ setUpSdCard(sd::SdCard::SLOT_0, sdStatus.first, "0");
+ setUpSdCard(sd::SdCard::SLOT_1, sdStatus.second, "1");
+ // Update status file
+ sdcMan->updateSdCardStateFile();
+ return HasReturnvaluesIF::RETURN_OK;
+#endif
+
+#endif /* Q7S_SD_CARD_CONFIG != Q7S_SD_NONE */
+
+}
diff --git a/bsp_q7s/core/CoreController.h b/bsp_q7s/core/CoreController.h
index bc8a1581..940a9097 100644
--- a/bsp_q7s/core/CoreController.h
+++ b/bsp_q7s/core/CoreController.h
@@ -7,6 +7,8 @@ class CoreController: public ExtendedControllerBase {
public:
CoreController(object_id_t objectId);
+ ReturnValue_t initialize() override;
+
ReturnValue_t handleCommandMessage(CommandMessage *message) override;
void performControlOperation() override;
private:
@@ -15,6 +17,8 @@ private:
LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override;
ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode,
uint32_t *msToReachTheMode);
+
+ ReturnValue_t sdCardInit();
};
diff --git a/bsp_q7s/core/InitMission.cpp b/bsp_q7s/core/InitMission.cpp
index 3e655255..2bc334b9 100644
--- a/bsp_q7s/core/InitMission.cpp
+++ b/bsp_q7s/core/InitMission.cpp
@@ -44,6 +44,7 @@ void initmission::initMission() {
void initmission::initTasks() {
TaskFactory* factory = TaskFactory::instance();
+ ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
if(factory == nullptr) {
/* Should never happen ! */
return;
@@ -54,11 +55,18 @@ void initmission::initTasks() {
void (*missedDeadlineFunc) (void) = nullptr;
#endif
+ PeriodicTaskIF* coreController = factory->createPeriodicTask(
+ "CORE_CTRL", 60, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.4, missedDeadlineFunc);
+ result = coreController->addComponent(objects::CORE_CONTROLLER);
+ if(result != HasReturnvaluesIF::RETURN_OK) {
+ initmission::printAddObjectError("CORE_CTRL", objects::CORE_CONTROLLER);
+
+ }
+
/* TMTC Distribution */
PeriodicTaskIF* tmTcDistributor = factory->createPeriodicTask(
"DIST", 40, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.2, missedDeadlineFunc);
- ReturnValue_t result = tmTcDistributor->addComponent(
- objects::CCSDS_PACKET_DISTRIBUTOR);
+ result = tmTcDistributor->addComponent(objects::CCSDS_PACKET_DISTRIBUTOR);
if(result != HasReturnvaluesIF::RETURN_OK) {
initmission::printAddObjectError("CCSDS_DISTRIB", objects::CCSDS_PACKET_DISTRIBUTOR);
}
@@ -229,6 +237,7 @@ void initmission::initTasks() {
tmTcDistributor->startTask();
udpBridgeTask->startTask();
udpPollingTask->startTask();
+ coreController->startTask();
#if TE0720 == 0
uartPst->startTask();
diff --git a/bsp_q7s/core/ObjectFactory.cpp b/bsp_q7s/core/ObjectFactory.cpp
index 91a26d8a..7389d5ff 100644
--- a/bsp_q7s/core/ObjectFactory.cpp
+++ b/bsp_q7s/core/ObjectFactory.cpp
@@ -1,3 +1,4 @@
+#include
#include "ObjectFactory.h"
#include "OBSWConfig.h"
#include "tmtc/apid.h"
@@ -643,6 +644,11 @@ void ObjectFactory::produce(void* args){
new UdpTmTcBridge(objects::UDP_BRIDGE, objects::CCSDS_PACKET_DISTRIBUTOR);
new UdpTcPollingTask(objects::UDP_POLLING_TASK, objects::UDP_BRIDGE);
+ /* Test Task */
+#if OBSW_ADD_TEST_CODE == 1
+ new Q7STestTask(objects::TEST_TASK);
+#endif
+
#if TE0720 == 1 && TEST_LIBGPIOD == 1
/* Configure MIO0 as input */
GpiodRegular gpioConfigMio0(std::string("gpiochip0"), 0,
@@ -728,4 +734,5 @@ void ObjectFactory::produce(void* args){
#if Q7S_ADD_SPI_TEST == 1
new SpiTestClass(objects::SPI_TEST, gpioComIF);
#endif
+
}
diff --git a/bsp_q7s/main.cpp b/bsp_q7s/main.cpp
index 93968b76..9ce0dca2 100644
--- a/bsp_q7s/main.cpp
+++ b/bsp_q7s/main.cpp
@@ -6,12 +6,15 @@
#include "simple/simple.h"
#endif
+#include
+
/**
* @brief This is the main program for the target hardware.
* @return
*/
int main(void)
{
+ using namespace std;
#if Q7S_SIMPLE_MODE == 0
return obsw::obsw();
#else
diff --git a/bsp_q7s/memory/CMakeLists.txt b/bsp_q7s/memory/CMakeLists.txt
index 2ccdc7e2..6c7d0a94 100644
--- a/bsp_q7s/memory/CMakeLists.txt
+++ b/bsp_q7s/memory/CMakeLists.txt
@@ -1,4 +1,5 @@
target_sources(${TARGET_NAME} PRIVATE
- FileSystemManager.cpp
- SdCardAccess.cpp
+ FileSystemHandler.cpp
+ SdCardManager.cpp
+ scratchApi.cpp
)
\ No newline at end of file
diff --git a/bsp_q7s/memory/FileSystemHandler.cpp b/bsp_q7s/memory/FileSystemHandler.cpp
new file mode 100644
index 00000000..1519b92b
--- /dev/null
+++ b/bsp_q7s/memory/FileSystemHandler.cpp
@@ -0,0 +1,184 @@
+#include "FileSystemHandler.h"
+
+#include "fsfw/tasks/TaskFactory.h"
+#include "fsfw/memory/GenericFileSystemMessage.h"
+#include "fsfw/ipc/QueueFactory.h"
+
+#include
+
+FileSystemHandler::FileSystemHandler(object_id_t fileSystemHandler):
+ SystemObject(fileSystemHandler) {
+ mq = QueueFactory::instance()->createMessageQueue(FS_MAX_QUEUE_SIZE);
+}
+
+FileSystemHandler::~FileSystemHandler() {
+ QueueFactory::instance()->deleteMessageQueue(mq);
+}
+
+ReturnValue_t FileSystemHandler::performOperation(uint8_t unsignedChar) {
+ while(true) {
+ try {
+ fileSystemHandlerLoop();
+ }
+ catch(std::bad_alloc& e) {
+ // Restart OBSW, hints at a memory leak
+ sif::error << "Allocation error in FileSystemHandler::performOperation"
+ << e.what() << std::endl;
+ // TODO: If we trigger an event, it might not get sent because were restarting
+ // Set up an error file or a special flag in the scratch buffer.
+ // TODO: CoreController: Implement function to restart OBC
+ }
+ }
+}
+
+
+void FileSystemHandler::fileSystemHandlerLoop() {
+ CommandMessage filemsg;
+ ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
+ while(true) {
+ if(opCounter % 5 == 0) {
+ fileSystemCheckup();
+ }
+ result = mq->receiveMessage(&filemsg);
+ if(result == MessageQueueIF::EMPTY) {
+ break;
+ }
+ else if(result != HasReturnvaluesIF::RETURN_FAILED) {
+ sif::warning << "FileSystemHandler::performOperation: Message reception failed!"
+ << std::endl;
+ break;
+ }
+ Command_t command = filemsg.getCommand();
+ switch(command) {
+ case(GenericFileSystemMessage::CMD_CREATE_DIRECTORY): {
+ break;
+ }
+ case(GenericFileSystemMessage::CMD_CREATE_FILE): {
+ break;
+ }
+ }
+ opCounter++;
+ }
+
+ // This task will have a low priority and will run permanently in the background
+ // so we will just run in a permanent loop here and check file system
+ // messages permanently
+ TaskFactory::instance()->delayTask(1000);
+}
+
+void FileSystemHandler::fileSystemCheckup() {
+ SdCardManager::SdStatusPair statusPair;
+ sdcMan->getSdCardActiveStatus(statusPair);
+ sd::SdCard preferredSdCard;
+ sdcMan->getPreferredSdCard(preferredSdCard);
+ if((preferredSdCard == sd::SdCard::SLOT_0) and
+ (statusPair.first == sd::SdStatus::MOUNTED)) {
+ currentMountPrefix = SdCardManager::SD_0_MOUNT_POINT;
+ }
+ if((preferredSdCard == sd::SdCard::SLOT_1) and
+ (statusPair.second == sd::SdStatus::MOUNTED)) {
+ currentMountPrefix = SdCardManager::SD_1_MOUNT_POINT;
+ }
+ else {
+ std::string sdString;
+ if(preferredSdCard == sd::SdCard::SLOT_0) {
+ sdString = "0";
+ }
+ else {
+ sdString = "1";
+ }
+ sif::warning << "FileSystemHandler::performOperation: Inconsistent" <<
+ " state detected. Preferred SD card is " << sdString <<
+ " but does not appear to be mounted. Attempting fix.." << std::endl;
+ // This function will appear to fix the inconsistent state
+ ReturnValue_t result = sdcMan->sanitizeState(&preferredSdCard, &statusPair);
+ if(result != HasReturnvaluesIF::RETURN_OK) {
+ // Oh no.
+ // TODO: Trigger medium severity event
+ sif::error << "Fix failed" << std::endl;
+ }
+ }
+}
+
+MessageQueueId_t FileSystemHandler::getCommandQueue() const {
+ return mq->getId();
+}
+
+ReturnValue_t FileSystemHandler::initialize() {
+ sdcMan = SdCardManager::instance();
+ sd::SdCard preferredSdCard;
+ ReturnValue_t result = sdcMan->getPreferredSdCard(preferredSdCard);
+ if(result != HasReturnvaluesIF::RETURN_OK) {
+ return result;
+ }
+ if(preferredSdCard == sd::SdCard::SLOT_0) {
+ currentMountPrefix = SdCardManager::SD_0_MOUNT_POINT;
+ }
+ else if(preferredSdCard == sd::SdCard::SLOT_1) {
+ currentMountPrefix = SdCardManager::SD_1_MOUNT_POINT;
+ }
+ return HasReturnvaluesIF::RETURN_OK;
+}
+
+ReturnValue_t FileSystemHandler::appendToFile(const char *repositoryPath, const char *filename,
+ const uint8_t *data, size_t size, uint16_t packetNumber, void *args) {
+ // A double slash between repo and filename should not be an issue, so add it in any case
+ std::string fullPath = currentMountPrefix + std::string(repositoryPath) + "/" +
+ std::string(filename);
+ return HasReturnvaluesIF::RETURN_OK;
+}
+
+ReturnValue_t FileSystemHandler::createFile(const char *repositoryPath, const char *filename,
+ const uint8_t *data, size_t size, void *args) {
+ // A double slash between repo and filename should not be an issue, so add it in any case
+ std::string fullPath = currentMountPrefix + std::string(repositoryPath) + "/" +
+ std::string(filename);
+ return HasReturnvaluesIF::RETURN_OK;
+}
+
+ReturnValue_t FileSystemHandler::deleteFile(const char *repositoryPath, const char *filename,
+ void *args) {
+ // A double slash between repo and filename should not be an issue, so add it in any case
+ std::string fullPath = currentMountPrefix + std::string(repositoryPath) + "/" +
+ std::string(filename);
+ return HasReturnvaluesIF::RETURN_OK;
+}
+
+ReturnValue_t FileSystemHandler::createDirectory(const char *repositoryPath, void *args) {
+ std::string fullPath = currentMountPrefix + std::string(repositoryPath);
+ if(std::filesystem::exists(fullPath)) {
+ return DIRECTORY_ALREADY_EXISTS;
+ }
+ if(std::filesystem::create_directory(fullPath)) {
+ return HasReturnvaluesIF::RETURN_OK;
+ }
+ sif::warning << "Creating directory " << fullPath << " failed" << std::endl;
+ return GENERIC_FILE_ERROR;
+}
+
+ReturnValue_t FileSystemHandler::removeDirectory(const char *repositoryPath,
+ bool deleteRecurively, void *args) {
+ std::string fullPath = currentMountPrefix + std::string(repositoryPath);
+ if(not std::filesystem::exists(fullPath)) {
+ return DIRECTORY_DOES_NOT_EXIST;
+ }
+ std::error_code err;
+ if(not deleteRecurively) {
+ if(std::filesystem::remove(fullPath, err)) {
+ return HasReturnvaluesIF::RETURN_OK;
+ }
+ else {
+ // Check error code. Most probably denied permissions because folder is not empty
+ }
+ }
+ else {
+ if(std::filesystem::remove_all(fullPath, err)) {
+ return HasReturnvaluesIF::RETURN_OK;
+ }
+ else {
+ // Check error code
+ }
+ }
+
+ return HasReturnvaluesIF::RETURN_OK;
+}
diff --git a/bsp_q7s/memory/FileSystemHandler.h b/bsp_q7s/memory/FileSystemHandler.h
new file mode 100644
index 00000000..886fa574
--- /dev/null
+++ b/bsp_q7s/memory/FileSystemHandler.h
@@ -0,0 +1,56 @@
+#ifndef BSP_Q7S_MEMORY_FILESYSTEMHANDLER_H_
+#define BSP_Q7S_MEMORY_FILESYSTEMHANDLER_H_
+
+#include "SdCardManager.h"
+#include "OBSWConfig.h"
+
+#include "fsfw/ipc/MessageQueueIF.h"
+#include "fsfw/tasks/ExecutableObjectIF.h"
+#include "fsfw/objectmanager/SystemObject.h"
+#include "fsfw/memory/HasFileSystemIF.h"
+
+#include
+
+class FileSystemHandler: public SystemObject,
+ public ExecutableObjectIF,
+ public HasFileSystemIF {
+public:
+ FileSystemHandler(object_id_t fileSystemHandler);
+ virtual~ FileSystemHandler();
+
+ ReturnValue_t performOperation(uint8_t) override;
+
+ ReturnValue_t initialize() override;
+
+ /**
+ * Function to get the MessageQueueId_t of the implementing object
+ * @return MessageQueueId_t of the object
+ */
+ MessageQueueId_t getCommandQueue() const override;
+
+private:
+ MessageQueueIF* mq = nullptr;
+ std::string currentMountPrefix = SdCardManager::SD_0_MOUNT_POINT;
+ static constexpr uint32_t FS_MAX_QUEUE_SIZE = config::OBSW_FILESYSTEM_HANDLER_QUEUE_SIZE;
+ SdCardManager* sdcMan = nullptr;
+ uint8_t opCounter = 0;
+
+ void fileSystemHandlerLoop();
+ void fileSystemCheckup();
+
+ ReturnValue_t appendToFile(const char* repositoryPath,
+ const char* filename, const uint8_t* data, size_t size,
+ uint16_t packetNumber, void* args = nullptr) override;
+ ReturnValue_t createFile(const char* repositoryPath,
+ const char* filename, const uint8_t* data = nullptr,
+ size_t size = 0, void* args = nullptr) override;
+ ReturnValue_t deleteFile(const char* repositoryPath,
+ const char* filename, void* args = nullptr) override;
+ ReturnValue_t createDirectory(const char* repositoryPath, void* args = nullptr) override;
+ ReturnValue_t removeDirectory(const char* repositoryPath, bool deleteRecurively = false,
+ void* args = nullptr) override;
+};
+
+
+
+#endif /* BSP_Q7S_MEMORY_FILESYSTEMMANAGER_H_ */
diff --git a/bsp_q7s/memory/FileSystemManager.cpp b/bsp_q7s/memory/FileSystemManager.cpp
deleted file mode 100644
index 40c8e58a..00000000
--- a/bsp_q7s/memory/FileSystemManager.cpp
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "FileSystemManager.h"
-
-class FileSystemManager {
-public:
-
-private:
-};
diff --git a/bsp_q7s/memory/FileSystemManager.h b/bsp_q7s/memory/FileSystemManager.h
deleted file mode 100644
index 4fc8dc52..00000000
--- a/bsp_q7s/memory/FileSystemManager.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef BSP_Q7S_MEMORY_FILESYSTEMMANAGER_H_
-#define BSP_Q7S_MEMORY_FILESYSTEMMANAGER_H_
-
-
-
-
-
-#endif /* BSP_Q7S_MEMORY_FILESYSTEMMANAGER_H_ */
diff --git a/bsp_q7s/memory/SdCardAccess.cpp b/bsp_q7s/memory/SdCardAccess.cpp
deleted file mode 100644
index 3d5f12b9..00000000
--- a/bsp_q7s/memory/SdCardAccess.cpp
+++ /dev/null
@@ -1,4 +0,0 @@
-#include "SdCardAccess.h"
-
-SdCardAccess::SdCardAccess() {
-}
diff --git a/bsp_q7s/memory/SdCardAccess.h b/bsp_q7s/memory/SdCardAccess.h
deleted file mode 100644
index ba273d09..00000000
--- a/bsp_q7s/memory/SdCardAccess.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef BSP_Q7S_MEMORY_SDCARDACCESS_H_
-#define BSP_Q7S_MEMORY_SDCARDACCESS_H_
-
-class SdCardAccess {
-public:
- SdCardAccess();
-private:
-};
-
-
-#endif /* BSP_Q7S_MEMORY_SDCARDACCESS_H_ */
diff --git a/bsp_q7s/memory/SdCardManager.cpp b/bsp_q7s/memory/SdCardManager.cpp
new file mode 100644
index 00000000..5a50665b
--- /dev/null
+++ b/bsp_q7s/memory/SdCardManager.cpp
@@ -0,0 +1,333 @@
+#include "SdCardManager.h"
+#include "scratchApi.h"
+
+#include "linux/utility/utility.h"
+
+#include "fsfw/ipc/MutexFactory.h"
+#include "fsfw/serviceinterface/ServiceInterface.h"
+
+#include
+#include
+#include
+
+SdCardManager* SdCardManager::factoryInstance = nullptr;
+
+SdCardManager::SdCardManager() {
+}
+
+SdCardManager::~SdCardManager() {
+}
+
+void SdCardManager::create() {
+ if(factoryInstance == nullptr) {
+ factoryInstance = new SdCardManager();
+ }
+}
+
+SdCardManager* SdCardManager::instance() {
+ SdCardManager::create();
+ return SdCardManager::factoryInstance;
+}
+
+ReturnValue_t SdCardManager::switchOnSdCard(sd::SdCard sdCard, bool doMountSdCard,
+ SdStatusPair* statusPair) {
+ ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
+ if(statusPair == nullptr) {
+ statusPair = std::make_unique().get();
+ result = getSdCardActiveStatus(*statusPair);
+ if(result != HasReturnvaluesIF::RETURN_OK) {
+ return result;
+ }
+ }
+
+ // Not allowed, this function turns on one SD card
+ if(sdCard == sd::SdCard::BOTH) {
+ sif::warning << "SdCardManager::switchOffSdCard: API does not allow sd::SdStatus::BOTH"
+ << std::endl;
+ return HasReturnvaluesIF::RETURN_FAILED;
+ }
+
+ sd::SdStatus targetStatus;
+ if(sdCard == sd::SdCard::SLOT_0) {
+ targetStatus = statusPair->first;
+ }
+ else if(sdCard == sd::SdCard::SLOT_1) {
+ targetStatus = statusPair->second;
+ }
+
+ auto switchCall = [&]() {
+ if(targetStatus == sd::SdStatus::ON) {
+ if(not doMountSdCard) {
+ return ALREADY_ON;
+ }
+ else {
+ return mountSdCard(sdCard);
+ }
+ }
+ else if(targetStatus == sd::SdStatus::MOUNTED) {
+ return ALREADY_MOUNTED;
+ }
+ else if(targetStatus == sd::SdStatus::OFF) {
+ return setSdCardState(sdCard, true);
+ }
+ else {
+ return HasReturnvaluesIF::RETURN_FAILED;
+ }
+ };
+
+ result = switchCall();
+
+ if(result != HasReturnvaluesIF::RETURN_OK or not doMountSdCard) {
+ return result;
+ }
+
+ return mountSdCard(sdCard);
+}
+
+ReturnValue_t SdCardManager::switchOffSdCard(sd::SdCard sdCard, bool doUnmountSdCard,
+ SdStatusPair* statusPair) {
+ std::pair active;
+ ReturnValue_t result = getSdCardActiveStatus(active);
+ if(result != HasReturnvaluesIF::RETURN_OK) {
+ return result;
+ }
+ // Not allowed, this function turns off one SD card
+ if(sdCard == sd::SdCard::BOTH) {
+ sif::warning << "SdCardManager::switchOffSdCard: API does not allow sd::SdStatus::BOTH"
+ << std::endl;
+ return HasReturnvaluesIF::RETURN_FAILED;
+ }
+ if(sdCard == sd::SdCard::SLOT_0) {
+ if(active.first == sd::SdStatus::OFF) {
+ return ALREADY_OFF;
+ }
+ }
+ else if(sdCard == sd::SdCard::SLOT_1) {
+ if(active.second == sd::SdStatus::OFF) {
+ return ALREADY_OFF;
+ }
+ }
+
+ if(doUnmountSdCard) {
+ result = unmountSdCard(sdCard);
+ if(result != HasReturnvaluesIF::RETURN_OK) {
+ return result;
+ }
+ }
+
+ return setSdCardState(sdCard, false);
+}
+
+ReturnValue_t SdCardManager::setSdCardState(sd::SdCard sdCard, bool on) {
+ using namespace std;
+ string sdstring = "";
+ string statestring = "";
+ if(sdCard == sd::SdCard::SLOT_0) {
+ sdstring = "0";
+ }
+ else if(sdCard == sd::SdCard::SLOT_1) {
+ sdstring = "1";
+ }
+ if(on) {
+ statestring = "on";
+ }
+ else {
+ statestring = "off";
+ }
+ ostringstream command;
+ command << "q7hw sd set " << sdstring << " " << statestring;
+ int result = system(command.str().c_str());
+ if(result == 0) {
+ return HasReturnvaluesIF::RETURN_OK;
+ }
+ sif::warning << "SdCardManager::setSdCardState: system call failed with code " <<
+ result << std::endl;
+ return SYSTEM_CALL_ERROR;
+}
+
+ReturnValue_t SdCardManager::getSdCardActiveStatus(SdStatusPair& active) {
+ using namespace std;
+ if(not filesystem::exists(SD_STATE_FILE)) {
+ return STATUS_FILE_NEXISTS;
+ }
+
+ // Now the file should exist in any case. Still check whether it exists.
+ fstream sdStatus(SD_STATE_FILE);
+ if (not sdStatus.good()) {
+ return STATUS_FILE_NEXISTS;
+ }
+ string line;
+ uint8_t idx = 0;
+ sd::SdCard currentSd = sd::SdCard::SLOT_0;
+ // Process status file line by line
+ while (std::getline(sdStatus, line)) {
+ processSdStatusLine(active, line, idx, currentSd);
+ }
+ return HasReturnvaluesIF::RETURN_OK;
+}
+
+ReturnValue_t SdCardManager::mountSdCard(sd::SdCard sdCard) {
+ using namespace std;
+ if(sdCard == sd::SdCard::BOTH) {
+ sif::warning << "SdCardManager::mountSdCard: API does not allow sd::SdStatus::BOTH"
+ << std::endl;
+ return HasReturnvaluesIF::RETURN_FAILED;
+ }
+ string mountDev;
+ string mountPoint;
+ if(sdCard == sd::SdCard::SLOT_0) {
+ mountDev = SD_0_DEV_NAME;
+ mountPoint = SD_0_MOUNT_POINT;
+ }
+ else if(sdCard == sd::SdCard::SLOT_1) {
+ mountDev = SD_1_DEV_NAME;
+ mountPoint = SD_1_MOUNT_POINT;
+ }
+ if(not filesystem::exists(mountDev)) {
+ sif::warning << "SdCardManager::mountSdCard: Device file does not exists. Make sure to"
+ " turn on the SD card" << std::endl;
+ return MOUNT_ERROR;
+ }
+
+ string sdMountCommand = "mount " + mountDev + " " + mountPoint;
+ int result = system(sdMountCommand.c_str());
+ if (result != 0) {
+ utility::handleSystemError(result, "SdCardManager::mountSdCard");
+ return SYSTEM_CALL_ERROR;
+ }
+ return HasReturnvaluesIF::RETURN_OK;
+}
+
+ReturnValue_t SdCardManager::unmountSdCard(sd::SdCard sdCard) {
+ using namespace std;
+ if(sdCard == sd::SdCard::BOTH) {
+ sif::warning << "SdCardManager::unmountSdCard: API does not allow sd::SdStatus::BOTH"
+ << std::endl;
+ return HasReturnvaluesIF::RETURN_FAILED;
+ }
+ string mountPoint;
+ if(sdCard == sd::SdCard::SLOT_0) {
+ mountPoint = SD_0_MOUNT_POINT;
+ }
+ else if(sdCard == sd::SdCard::SLOT_1) {
+ mountPoint = SD_1_MOUNT_POINT;
+ }
+ if(filesystem::is_empty(mountPoint)) {
+ // The mount point will always exist, but if it is empty, that is strong hint that
+ // the SD card was not mounted properly. Still proceed with operation.
+ sif::warning << "SdCardManager::unmountSdCard: Mount point is empty!" << std::endl;
+ }
+ string sdUnmountCommand = "umount " + mountPoint;
+ int result = system(sdUnmountCommand.c_str());
+ if (result != 0) {
+ utility::handleSystemError(result, "SdCardManager::unmountSdCard");
+ return SYSTEM_CALL_ERROR;
+ }
+ return HasReturnvaluesIF::RETURN_OK;
+}
+
+ReturnValue_t SdCardManager::sanitizeState(sd::SdCard* prefSdCard, SdStatusPair* statusPair) {
+ if(prefSdCard == nullptr) {
+ prefSdCard = std::make_unique(sd::SdCard::SLOT_0).get();
+ getPreferredSdCard(*prefSdCard);
+ }
+ if(statusPair == nullptr) {
+ statusPair = std::make_unique().get();
+ getSdCardActiveStatus(*statusPair);
+ }
+
+ auto sanitizerFunc = [&](sd::SdCard prefSdCard) {
+ if(statusPair->first == sd::SdStatus::ON) {
+ return mountSdCard(prefSdCard);
+ }
+ else {
+ return switchOnSdCard(prefSdCard, true, statusPair);
+ }
+ };
+
+ return sanitizerFunc(*prefSdCard);
+}
+
+void SdCardManager::processSdStatusLine(std::pair &active,
+ std::string& line, uint8_t& idx, sd::SdCard& currentSd) {
+ using namespace std;
+ istringstream iss(line);
+ string word;
+ bool slotLine = false;
+ bool mountLine = false;
+ while(iss >> word) {
+ if (word == "Slot") {
+ slotLine = true;
+ }
+ if(word == "Mounted") {
+ mountLine = true;
+ }
+
+ if(slotLine) {
+ if (word == "1:") {
+ currentSd = sd::SdCard::SLOT_1;
+ }
+
+ if(word == "on") {
+ if(currentSd == sd::SdCard::SLOT_0) {
+ active.first = sd::SdStatus::ON;
+ }
+ else {
+ active.second = sd::SdStatus::ON;
+ }
+ }
+ else if (word == "off") {
+ if(currentSd == sd::SdCard::SLOT_0) {
+ active.first = sd::SdStatus::OFF;
+ }
+ else {
+ active.second = sd::SdStatus::OFF;
+ }
+ }
+ }
+
+ if(mountLine) {
+ if(currentSd == sd::SdCard::SLOT_0) {
+ active.first = sd::SdStatus::MOUNTED;
+ }
+ else {
+ active.second = sd::SdStatus::MOUNTED;
+ }
+ }
+
+ if(idx > 5) {
+ sif::warning << "SdCardManager::sdCardActive: /tmp/sd_status.txt has more than 6 "
+ "lines and might be invalid!" << std::endl;
+ }
+ }
+ idx++;
+}
+
+ReturnValue_t SdCardManager::getPreferredSdCard(sd::SdCard& sdCard) const {
+ uint8_t prefSdCard = 0;
+ ReturnValue_t result = scratch::readNumber(scratch::PREFERED_SDC_KEY, prefSdCard);
+ if(result != HasReturnvaluesIF::RETURN_OK) {
+ return result;
+ }
+ sdCard = static_cast(prefSdCard);
+ return HasReturnvaluesIF::RETURN_OK;
+}
+
+ReturnValue_t SdCardManager::setPreferredSdCard(sd::SdCard sdCard) {
+ if(sdCard == sd::SdCard::BOTH) {
+ return HasReturnvaluesIF::RETURN_FAILED;
+ }
+ return scratch::writeNumber(scratch::PREFERED_SDC_KEY, static_cast(sdCard));
+}
+
+ReturnValue_t SdCardManager::updateSdCardStateFile() {
+ // Use q7hw utility and pipe the command output into the state file
+ std::string updateCmd = "q7hw sd info all > " + std::string(SD_STATE_FILE);
+ int result = std::system(updateCmd.c_str());
+ if(result == 0) {
+ return HasReturnvaluesIF::RETURN_OK;
+ }
+ sif::warning << "SdCardManager::updateSdCardStateFile: system call failed with code " <<
+ result << std::endl;
+ return HasReturnvaluesIF::RETURN_FAILED;
+}
diff --git a/bsp_q7s/memory/SdCardManager.h b/bsp_q7s/memory/SdCardManager.h
new file mode 100644
index 00000000..966e6e2a
--- /dev/null
+++ b/bsp_q7s/memory/SdCardManager.h
@@ -0,0 +1,159 @@
+#ifndef BSP_Q7S_MEMORY_SDCARDACCESSMANAGER_H_
+#define BSP_Q7S_MEMORY_SDCARDACCESSMANAGER_H_
+
+#include "definitions.h"
+#include "returnvalues/classIds.h"
+
+#include "fsfw/returnvalues/HasReturnvaluesIF.h"
+
+#include
+#include
+#include
+
+class MutexIF;
+
+/**
+ * @brief Manages handling of SD cards like switching them on or off or getting the current
+ * state
+ */
+class SdCardManager {
+ friend class SdCardAccess;
+public:
+ using SdStatusPair = std::pair;
+
+ static constexpr uint8_t INTERFACE_ID = CLASS_ID::SD_CARD_MANAGER;
+
+ static constexpr ReturnValue_t ALREADY_ON =
+ HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 0);
+ static constexpr ReturnValue_t ALREADY_MOUNTED =
+ HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 1);
+ static constexpr ReturnValue_t ALREADY_OFF =
+ HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 2);
+ static constexpr ReturnValue_t STATUS_FILE_NEXISTS =
+ HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 10);
+ static constexpr ReturnValue_t STATUS_FILE_FORMAT_INVALID =
+ HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 11);
+ static constexpr ReturnValue_t MOUNT_ERROR =
+ HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 12);
+ static constexpr ReturnValue_t UNMOUNT_ERROR =
+ HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 13);
+ static constexpr ReturnValue_t SYSTEM_CALL_ERROR =
+ HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 14);
+
+ // C++17 does not support constexpr std::string yet
+ static constexpr char SD_0_DEV_NAME[] = "/dev/mmcblk0p1";
+ static constexpr char SD_1_DEV_NAME[] = "/dev/mmcblk1p1";
+ static constexpr char SD_0_MOUNT_POINT[] = "/mnt/sd0";
+ static constexpr char SD_1_MOUNT_POINT[] = "/mnt/sd1";
+ static constexpr char SD_STATE_FILE[] = "/tmp/sd_status.txt";
+
+ virtual ~SdCardManager();
+
+ static void create();
+
+ /**
+ * Returns the single instance of the SD card manager.
+ */
+ static SdCardManager* instance();
+
+ /**
+ * Set the preferred SD card which will determine which SD card will be used as the primary
+ * SD card in hot redundant and cold redundant mode. This function will not switch the
+ * SD cards which are currently on and mounted, this needs to be implemented by
+ * an upper layer by using #switchOffSdCard , #switchOnSdCard and #updateSdCardStateFile
+ * @param sdCard
+ * @return
+ */
+ ReturnValue_t setPreferredSdCard(sd::SdCard sdCard);
+
+ /**
+ * Get the currently configured preferred SD card
+ * @param sdCard
+ * @return
+ */
+ ReturnValue_t getPreferredSdCard(sd::SdCard& sdCard) const;
+
+ /**
+ * Switch on the specified SD card.
+ * @param sdCard
+ * @param doMountSdCard Mount the SD card after switching it on, which is necessary
+ * to use it
+ * @param statusPair If the status pair is already available, it can be passed here
+ * @return - RETURN_OK on success, ALREADY_ON if it is already on,
+ * SYSTEM_CALL_ERROR on system error
+ */
+ ReturnValue_t switchOnSdCard(sd::SdCard sdCard, bool doMountSdCard = true,
+ SdStatusPair* statusPair = nullptr);
+
+ /**
+ * Switch off the specified SD card.
+ * @param sdCard
+ * @param doUnmountSdCard Unmount the SD card before switching the card off, which makes
+ * the operation safer
+ * @param statusPair If the status pair is already available, it can be passed here
+ * @return - RETURN_OK on success, ALREADY_ON if it is already on,
+ * SYSTEM_CALL_ERROR on system error
+ */
+ ReturnValue_t switchOffSdCard(sd::SdCard sdCard, bool doUnmountSdCard = true,
+ SdStatusPair* statusPair = nullptr);
+
+ /**
+ * Update the state file or creates one if it does not exist. You need to call this
+ * function before calling #sdCardActive
+ * @return - RETURN_OK if the state file was updated successfully
+ * - SYSTEM_CALL_ERROR if the call to create the status file failed
+ */
+ ReturnValue_t updateSdCardStateFile();
+
+ /**
+ * Get the state of the SD cards. If the state file does not exist, this function will
+ * take care of updating it. If it does not, the function will use the state file to get
+ * the status of the SD cards and set the field of the provided boolean pair.
+ * @param active Pair of booleans, where the first entry is the state of the first SD card
+ * and the second one the state of the second SD card
+ * @return - RETURN_OK if the state was read successfully
+ * - STATUS_FILE_FORMAT_INVALID if there was an issue with the state file. The user
+ * should call #updateSdCardStateFile again in that case
+ * - STATUS_FILE_NEXISTS if the status file does not exist
+ */
+ ReturnValue_t getSdCardActiveStatus(SdStatusPair& active);
+
+ /**
+ * Mount the specified SD card. This is necessary to use it.
+ * @param sdCard
+ * @return
+ */
+ ReturnValue_t mountSdCard(sd::SdCard sdCard);
+ /**
+ * Unmount the specified SD card. This is recommended before switching it off. The SD card
+ * can't be used after it has been unmounted.
+ * @param sdCard
+ * @return
+ */
+ ReturnValue_t unmountSdCard(sd::SdCard sdCard);
+
+ /**
+ * In case that there is a discrepancy between the preferred SD card and the currently
+ * mounted one, this function will sanitize the state by attempting to mount the
+ * currently preferred SD card. If the caller already has state information, it can be
+ * passed into the function.
+ * @param prefSdCard Preferred SD card captured with #getPreferredSdCard
+ * @param statusPair Current SD card status capture with #getSdCardActiveStatus
+ * @throws std::bad_alloc if one of the two arguments was a nullptr and an allocation failed
+ * @return
+ */
+ ReturnValue_t sanitizeState(sd::SdCard* prefSdCard = nullptr,
+ SdStatusPair* statusPair = nullptr);
+
+private:
+ SdCardManager();
+
+ ReturnValue_t setSdCardState(sd::SdCard sdCard, bool on);
+
+ void processSdStatusLine(SdStatusPair& active, std::string& line, uint8_t& idx,
+ sd::SdCard& currentSd);
+
+ static SdCardManager* factoryInstance;
+};
+
+#endif /* BSP_Q7S_MEMORY_SDCARDACCESSMANAGER_H_ */
diff --git a/bsp_q7s/memory/definitions.h b/bsp_q7s/memory/definitions.h
new file mode 100644
index 00000000..546a6585
--- /dev/null
+++ b/bsp_q7s/memory/definitions.h
@@ -0,0 +1,25 @@
+#ifndef BSP_Q7S_MEMORY_DEFINITIONS_H_
+#define BSP_Q7S_MEMORY_DEFINITIONS_H_
+
+#include
+
+namespace sd {
+
+enum SdStatus: uint8_t {
+ OFF = 0,
+ ON = 1,
+ // A mounted SD card is on as well
+ MOUNTED = 2
+};
+
+enum SdCard: uint8_t {
+ SLOT_0,
+ SLOT_1,
+ BOTH
+};
+
+}
+
+
+
+#endif /* BSP_Q7S_MEMORY_DEFINITIONS_H_ */
diff --git a/bsp_q7s/memory/scratchApi.cpp b/bsp_q7s/memory/scratchApi.cpp
new file mode 100644
index 00000000..0b159775
--- /dev/null
+++ b/bsp_q7s/memory/scratchApi.cpp
@@ -0,0 +1,12 @@
+#include "scratchApi.h"
+
+ReturnValue_t scratch::writeString(std::string name, std::string string) {
+ std::ostringstream oss;
+ oss << "xsc_scratch write " << name << " \"" << string << "\"";
+ int result = std::system(oss.str().c_str());
+ if(result != 0) {
+ utility::handleSystemError(result, "scratch::String");
+ return HasReturnvaluesIF::RETURN_FAILED;
+ }
+ return HasReturnvaluesIF::RETURN_OK;
+}
diff --git a/bsp_q7s/memory/scratchApi.h b/bsp_q7s/memory/scratchApi.h
new file mode 100644
index 00000000..eceda11a
--- /dev/null
+++ b/bsp_q7s/memory/scratchApi.h
@@ -0,0 +1,72 @@
+#ifndef BSP_Q7S_MEMORY_SCRATCHAPI_H_
+#define BSP_Q7S_MEMORY_SCRATCHAPI_H_
+
+#include "fsfw/returnvalues/HasReturnvaluesIF.h"
+#include "fsfw/serviceinterface/ServiceInterface.h"
+#include "linux/utility/utility.h"
+
+#include
+#include
+#include
+#include
+#include
+
+/**
+ * @brief API for the scratch buffer
+ */
+namespace scratch {
+
+static constexpr char PREFERED_SDC_KEY[] = "PREFSD";
+
+namespace {
+static uint8_t counter = 0;
+}
+
+template::value>::type>
+inline ReturnValue_t writeNumber(std::string name, T num) noexcept {
+ std::ostringstream oss;
+ oss << "xsc_scratch write " << name << " " << num;
+ int result = std::system(oss.str().c_str());
+ if(result != 0) {
+ utility::handleSystemError(result, "scratch::writeNumber");
+ return HasReturnvaluesIF::RETURN_FAILED;
+ }
+ return HasReturnvaluesIF::RETURN_OK;
+}
+
+template::value>::type>
+inline ReturnValue_t readNumber(std::string name, T& num) noexcept {
+ using namespace std;
+ string filename = "/tmp/sro" + std::to_string(counter++);
+ ostringstream oss;
+ oss << "xsc_scratch read " << name << " > " << filename;
+
+ int result = std::system(oss.str().c_str());
+ if(result != 0) {
+ utility::handleSystemError(result, "scratch::writeNumber");
+ return HasReturnvaluesIF::RETURN_FAILED;
+ }
+ ifstream file(filename);
+ string line;
+ if (not std::getline(file, line)) {
+ return HasReturnvaluesIF::RETURN_FAILED;
+ }
+
+ size_t pos = line.find("=");
+ std::string valueAsString = line.substr(pos + 1);
+ try {
+ num = std::stoi(valueAsString);
+ }
+ catch(std::invalid_argument& e) {
+ sif::warning << "scratch::readNumber: stoi call failed with " << e.what() << std::endl;
+ }
+
+ std::remove(filename.c_str());
+ return HasReturnvaluesIF::RETURN_OK;
+}
+
+ReturnValue_t writeString(std::string name, std::string string);
+
+}
+
+#endif /* BSP_Q7S_MEMORY_SCRATCHAPI_H_ */
diff --git a/bsp_q7s/simple/simple.cpp b/bsp_q7s/simple/simple.cpp
index 6261bfe3..a6bb9fdf 100644
--- a/bsp_q7s/simple/simple.cpp
+++ b/bsp_q7s/simple/simple.cpp
@@ -1,5 +1,17 @@
#include "simple.h"
+#include "q7sConfig.h"
+
+#if Q7S_SIMPLE_ADD_FILE_SYSTEM_TEST == 1
+#include "../boardtest/FileSystemTest.h"
+#endif
int simple::simple() {
+ cout << "-- Q7S Simple Application --" << endl;
+#if Q7S_SIMPLE_ADD_FILE_SYSTEM_TEST == 1
+ {
+ FileSystemTest fileSystemTest;
+ }
+#endif
return 0;
}
+
diff --git a/cmake/scripts/Linux/create_cmake_debug_cfg.sh b/cmake/scripts/Linux/make_debug_cfg.sh
similarity index 99%
rename from cmake/scripts/Linux/create_cmake_debug_cfg.sh
rename to cmake/scripts/Linux/make_debug_cfg.sh
index 8c626bf4..dc21896d 100755
--- a/cmake/scripts/Linux/create_cmake_debug_cfg.sh
+++ b/cmake/scripts/Linux/make_debug_cfg.sh
@@ -29,3 +29,4 @@ set -x # Print command
${python} cmake_build_config.py -o "${os_fsfw}" -g "${build_generator}" -b "debug" -l "${builddir}"
# Use this if commands are added which should not be printed
# set +x
+
diff --git a/cmake/scripts/Linux/create_cmake_release_cfg.sh b/cmake/scripts/Linux/make_release_cfg.sh
similarity index 100%
rename from cmake/scripts/Linux/create_cmake_release_cfg.sh
rename to cmake/scripts/Linux/make_release_cfg.sh
diff --git a/cmake/scripts/Linux/ninja_debug_cfg.sh b/cmake/scripts/Linux/ninja_debug_cfg.sh
new file mode 100755
index 00000000..9627ccf6
--- /dev/null
+++ b/cmake/scripts/Linux/ninja_debug_cfg.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+counter=0
+while [ ${counter} -lt 5 ]
+do
+ cd ..
+ if [ -f "cmake_build_config.py" ];then
+ break
+ fi
+ counter=$((counter=counter + 1))
+done
+
+if [ "${counter}" -ge 5 ];then
+ echo "create_cmake_cfg.sh not found in upper directories!"
+ exit 1
+fi
+
+build_generator="Ninja"
+os_fsfw="linux"
+builddir="build-Debug-Host"
+if [ "${OS}" = "Windows_NT" ]; then
+ python="py"
+# Could be other OS but this works for now.
+else
+ python="python3"
+fi
+
+echo "Running command (without the leading +):"
+set -x # Print command
+${python} cmake_build_config.py -o "${os_fsfw}" -g "${build_generator}" -b "debug" -l "${builddir}"
+# Use this if commands are added which should not be printed
+# set +x
+
diff --git a/cmake/scripts/Q7S/simple/simple_make_debug_cfg.sh b/cmake/scripts/Q7S/simple/simple_make_debug_cfg.sh
index b692184a..8a6c7b3f 100755
--- a/cmake/scripts/Q7S/simple/simple_make_debug_cfg.sh
+++ b/cmake/scripts/Q7S/simple/simple_make_debug_cfg.sh
@@ -31,5 +31,5 @@ fi
echo "Running command (without the leading +):"
set -x # Print command
${python} cmake_build_config.py -o "${os_fsfw}" -g "${build_generator}" -b "debug" -t "${tgt_bsp}" \
- -l"${build_dir}" -d "${definitions}"
+ -l "${build_dir}" -d "${definitions}"
# set +x
diff --git a/cmake/scripts/Q7S/simple/simple_ninja_debug_cfg.sh b/cmake/scripts/Q7S/simple/simple_ninja_debug_cfg.sh
index a3a55521..965aae45 100755
--- a/cmake/scripts/Q7S/simple/simple_ninja_debug_cfg.sh
+++ b/cmake/scripts/Q7S/simple/simple_ninja_debug_cfg.sh
@@ -29,6 +29,6 @@ fi
echo "Running command (without the leading +):"
set -x # Print command
${python} cmake_build_config.py -o "${os_fsfw}" -g "${build_generator}" -b "debug" -t "${tgt_bsp}" \
- -l"${build_dir}" -d "${definitions}"
+ -l "${build_dir}" -d "${definitions}"
# set +x
diff --git a/cmake/scripts/cmake_build_config.py b/cmake/scripts/cmake_build_config.py
old mode 100644
new mode 100755
diff --git a/common/config/OBSWVersion.h b/common/config/OBSWVersion.h
index 7ac812e6..b796f146 100644
--- a/common/config/OBSWVersion.h
+++ b/common/config/OBSWVersion.h
@@ -4,7 +4,7 @@
const char* const SW_NAME = "eive";
#define SW_VERSION 1
-#define SW_SUBVERSION 2
+#define SW_SUBVERSION 3
#define SW_SUBSUBVERSION 0
#endif /* COMMON_CONFIG_OBSWVERSION_H_ */
diff --git a/doc/img/ProcessSettings.png b/doc/img/ProcessSettings.png
new file mode 100644
index 00000000..5a8c3c99
Binary files /dev/null and b/doc/img/ProcessSettings.png differ
diff --git a/fsfw b/fsfw
index 38f2f69c..da8a4470 160000
--- a/fsfw
+++ b/fsfw
@@ -1 +1 @@
-Subproject commit 38f2f69c784c74cd87a10dce6c968325cf1cb472
+Subproject commit da8a4470734808bed4d872e47c192af694382c41
diff --git a/linux/csp/CspComIF.cpp b/linux/csp/CspComIF.cpp
index a8f13963..07fe63d3 100644
--- a/linux/csp/CspComIF.cpp
+++ b/linux/csp/CspComIF.cpp
@@ -24,6 +24,8 @@ ReturnValue_t CspComIF::initializeInterface(CookieIF *cookie) {
/* Perform CAN and CSP initialization only once */
if(cspDeviceMap.empty()){
+ sif::info << "Performing " << canInterface << " initialization.." << std::endl;
+
/* Define the memory to allocate for the CSP stack */
int buf_count = 10;
int buf_size = 300;
@@ -57,6 +59,7 @@ ReturnValue_t CspComIF::initializeInterface(CookieIF *cookie) {
sif::error << "Failed to start csp route task" << std::endl;
return HasReturnvaluesIF::RETURN_FAILED;
}
+ sif::info << canInterface << " initialized successfully" << std::endl;
}
uint8_t cspAddress = cspCookie->getCspAddress();
diff --git a/linux/fsfwconfig/OBSWConfig.h.in b/linux/fsfwconfig/OBSWConfig.h.in
index 657b4025..294f1d91 100644
--- a/linux/fsfwconfig/OBSWConfig.h.in
+++ b/linux/fsfwconfig/OBSWConfig.h.in
@@ -70,6 +70,7 @@ namespace config {
#endif
/* Add mission configuration flags here */
+static constexpr uint32_t OBSW_FILESYSTEM_HANDLER_QUEUE_SIZE = 50;
#ifdef __cplusplus
}
diff --git a/linux/fsfwconfig/pollingsequence/pollingSequenceFactory.cpp b/linux/fsfwconfig/pollingsequence/pollingSequenceFactory.cpp
index 3771c784..3c5f570c 100644
--- a/linux/fsfwconfig/pollingsequence/pollingSequenceFactory.cpp
+++ b/linux/fsfwconfig/pollingsequence/pollingSequenceFactory.cpp
@@ -477,7 +477,7 @@ ReturnValue_t pst::pstUart(FixedTimeslotTaskIF *thisSequence) {
thisSequence->addSlot(objects::SYRLINKS_HK_HANDLER, length * 0,
DeviceHandlerIF::PERFORM_OPERATION);
-#if OBSW_ADD_GPS == 1
+#if OBSW_ADD_ACS_BOARD == 1
thisSequence->addSlot(objects::GPS0_HANDLER, length * 0,
DeviceHandlerIF::PERFORM_OPERATION);
thisSequence->addSlot(objects::GPS1_HANDLER, length * 0,
@@ -486,7 +486,7 @@ ReturnValue_t pst::pstUart(FixedTimeslotTaskIF *thisSequence) {
thisSequence->addSlot(objects::SYRLINKS_HK_HANDLER, length * 0.2,
DeviceHandlerIF::SEND_WRITE);
-#if OBSW_ADD_GPS == 1
+#if OBSW_ADD_ACS_BOARD == 1
thisSequence->addSlot(objects::GPS0_HANDLER, length * 0.2,
DeviceHandlerIF::SEND_WRITE);
thisSequence->addSlot(objects::GPS1_HANDLER, length * 0.2,
@@ -495,7 +495,7 @@ ReturnValue_t pst::pstUart(FixedTimeslotTaskIF *thisSequence) {
thisSequence->addSlot(objects::SYRLINKS_HK_HANDLER, length * 0.4,
DeviceHandlerIF::GET_WRITE);
-#if OBSW_ADD_GPS == 1
+#if OBSW_ADD_ACS_BOARD == 1
thisSequence->addSlot(objects::GPS0_HANDLER, length * 0.4,
DeviceHandlerIF::GET_WRITE);
thisSequence->addSlot(objects::GPS1_HANDLER, length * 0.4,
@@ -504,7 +504,7 @@ ReturnValue_t pst::pstUart(FixedTimeslotTaskIF *thisSequence) {
thisSequence->addSlot(objects::SYRLINKS_HK_HANDLER, length * 0.6,
DeviceHandlerIF::SEND_READ);
-#if OBSW_ADD_GPS == 1
+#if OBSW_ADD_ACS_BOARD == 1
thisSequence->addSlot(objects::GPS0_HANDLER, length * 0.6,
DeviceHandlerIF::SEND_READ);
thisSequence->addSlot(objects::GPS1_HANDLER, length * 0.6,
@@ -513,7 +513,7 @@ ReturnValue_t pst::pstUart(FixedTimeslotTaskIF *thisSequence) {
thisSequence->addSlot(objects::SYRLINKS_HK_HANDLER, length * 0.8,
DeviceHandlerIF::GET_READ);
-#if OBSW_ADD_GPS == 1
+#if OBSW_ADD_ACS_BOARD == 1
thisSequence->addSlot(objects::GPS0_HANDLER, length * 0.8,
DeviceHandlerIF::GET_READ);
thisSequence->addSlot(objects::GPS1_HANDLER, length * 0.8,
diff --git a/linux/fsfwconfig/returnvalues/classIds.h b/linux/fsfwconfig/returnvalues/classIds.h
index d1bfe7e2..79f1a175 100644
--- a/linux/fsfwconfig/returnvalues/classIds.h
+++ b/linux/fsfwconfig/returnvalues/classIds.h
@@ -13,6 +13,7 @@ namespace CLASS_ID {
enum {
CLASS_ID_START = COMMON_CLASS_ID_END,
SA_DEPL_HANDLER, //SADPL
+ SD_CARD_MANAGER, //SDMA
CLASS_ID_END // [EXPORT] : [END]
};
}
diff --git a/linux/utility/CMakeLists.txt b/linux/utility/CMakeLists.txt
index 45a7edcc..a3387531 100644
--- a/linux/utility/CMakeLists.txt
+++ b/linux/utility/CMakeLists.txt
@@ -1,4 +1,5 @@
target_sources(${TARGET_NAME} PUBLIC
+ utility.cpp
)
diff --git a/linux/utility/utility.cpp b/linux/utility/utility.cpp
new file mode 100644
index 00000000..69a3d08f
--- /dev/null
+++ b/linux/utility/utility.cpp
@@ -0,0 +1,11 @@
+#include "OBSWConfig.h"
+#include "FSFWConfig.h"
+#include "utility.h"
+
+#include "fsfw/serviceinterface/ServiceInterface.h"
+
+void utility::handleSystemError(int retcode, std::string function) {
+#if OBSW_VERBOSE_LEVEL >= 1
+ sif::warning << function << ": System call failed with code " << retcode;
+#endif
+}
diff --git a/linux/utility/utility.h b/linux/utility/utility.h
new file mode 100644
index 00000000..3eb17a9b
--- /dev/null
+++ b/linux/utility/utility.h
@@ -0,0 +1,13 @@
+#ifndef LINUX_UTILITY_UTILITY_H_
+#define LINUX_UTILITY_UTILITY_H_
+
+#include "fsfw/returnvalues/HasReturnvaluesIF.h"
+#include
+
+namespace utility {
+
+void handleSystemError(int retcode, std::string function);
+
+}
+
+#endif /* LINUX_UTILITY_UTILITY_H_ */
diff --git a/misc/eclipse/.cproject b/misc/eclipse/.cproject
index a562e7b2..16bd992b 100644
--- a/misc/eclipse/.cproject
+++ b/misc/eclipse/.cproject
@@ -19,25 +19,25 @@
-
+
-
+
@@ -77,19 +77,23 @@
-
+
@@ -127,7 +131,7 @@
-
+