<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>http://217.79.180.177/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Uwe</id>
	<title>Aquarium-Control - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="http://217.79.180.177/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Uwe"/>
	<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php/Special:Contributions/Uwe"/>
	<updated>2026-08-03T20:21:14Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.40.0</generator>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=566</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=566"/>
		<updated>2026-08-01T09:42:44Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Using llvm-cov */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Requirements =&lt;br /&gt;
&lt;br /&gt;
The requirement are structured in two levels:&lt;br /&gt;
* system requirements&lt;br /&gt;
* software requirements&lt;br /&gt;
&lt;br /&gt;
The requirements coverage report is generated with the following command: &amp;lt;code&amp;gt;oft trace doc src tests -o Html &amp;gt; doc/req/coverage.html&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The linkage of the software requirements to the implementation and related test cases has been reverse engineered using the following two prompts for each function group:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Check all software requirements of thermal.md if you can find the matching implementations and tests.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Now do the reverse: Go through the implementation and test cases in src/sensors and the integration tests in tests/sensors and label every test case and at least every function linking to a matching software requirement from sensors.md.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;br /&gt;
&lt;br /&gt;
= Test strategy =&lt;br /&gt;
== Unit tests ==&lt;br /&gt;
Located within the implementation code files within &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Integration tests in test ==&lt;br /&gt;
Separated from implementation code to the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
== Code coverage measurement ==&lt;br /&gt;
=== Using tarpaulin ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo tarpaulin --features &amp;quot;test-utils&amp;quot; --out Html&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurement takes app. 22 minutes.&lt;br /&gt;
&lt;br /&gt;
=== Using llvm-cov ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo +nightly llvm-cov --features &amp;quot;test-utils&amp;quot; --html --ignore-filename-regex '(main\.rs|.*_channels\.rs|mock.*\.rs|pingable\.rs|privilege_check\.rs|recorder_data_frame\.rs)'&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The excluded files contain primarily debug functionality or wrappers without intrinsic logic which distorts the coverage measurement and where adding test cases would be meaningless.&lt;br /&gt;
&lt;br /&gt;
The nightly compiler build needs to be used because the application uses gating to exclude the unit tests from the coverage measurement: &amp;lt;code&amp;gt;#[cfg_attr(coverage_nightly, coverage(off))]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurements takes app. 8 minutes.&lt;br /&gt;
&lt;br /&gt;
=== Comparing the results from llvm-cov and tarpaulin ===&lt;br /&gt;
Based on commit &amp;lt;code&amp;gt;8f557814f679c30ebd70e181f2a3f996ce0e295c&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Module Summary Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! Subsystem / Module !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                        &lt;br /&gt;
    |- style=&amp;quot;font-weight:bold; background-color:#f0f0f0;&amp;quot;                                                                                                                                                 &lt;br /&gt;
    | '''TOTAL (All Application Source Files)''' || 71.53% (11216/15681) || 54.48% (4929/9047) || +17.04%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/&amp;lt;/code&amp;gt; || 0.00% (0/597) || 0.00% (0/457) || +0.00%                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/&amp;lt;/code&amp;gt; || 69.73% (1283/1840) || 52.72% (631/1197) || +17.01%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/&amp;lt;/code&amp;gt; || 46.32% (44/95) || 33.80% (24/71) || +12.51%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/&amp;lt;/code&amp;gt; || 79.06% (721/912) || 58.80% (244/415) || +20.26%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/&amp;lt;/code&amp;gt; || 84.06% (944/1123) || 59.48% (411/691) || +24.58%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/&amp;lt;/code&amp;gt; || 88.11% (467/530) || 60.33% (181/300) || +27.78%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/&amp;lt;/code&amp;gt; || 58.82% (610/1037) || 55.57% (334/601) || +3.25%                                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/&amp;lt;/code&amp;gt; || 77.46% (701/905) || 47.50% (304/640) || +29.96%                                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/&amp;lt;/code&amp;gt; || 60.22% (448/744) || 47.58% (216/454) || +12.64%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/&amp;lt;/code&amp;gt; || 75.70% (1748/2309) || 54.76% (656/1198) || +20.95%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/simulator/&amp;lt;/code&amp;gt; || 3.07% (10/326) || 2.05% (4/195) || +1.02%                                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/test_server/&amp;lt;/code&amp;gt; || 67.93% (822/1210) || 59.67% (429/719) || +8.27%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/&amp;lt;/code&amp;gt; || 88.45% (1019/1152) || 67.96% (437/643) || +20.49%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/&amp;lt;/code&amp;gt; || 80.91% (1187/1467) || 75.10% (564/751) || +5.81%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/&amp;lt;/code&amp;gt; || 86.10% (700/813) || 71.18% (331/465) || +14.92%                                                                                                                           &lt;br /&gt;
    |}  &lt;br /&gt;
&lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Detailed File Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! File Path !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_client.rs&amp;lt;/code&amp;gt; || 0.00% (0/117) || 0.00% (0/98) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_commissioning.rs&amp;lt;/code&amp;gt; || 0.00% (0/426) || 0.00% (0/315) || +0.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_test_server.rs&amp;lt;/code&amp;gt; || 0.00% (0/54) || 0.00% (0/44) || +0.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/database_interface_feed_trait.rs&amp;lt;/code&amp;gt; || 77.86% (102/131) || 58.06% (54/93) || +19.80%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/pingable.rs&amp;lt;/code&amp;gt; || 44.44% (4/9) || 33.33% (2/6) || +11.11%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface.rs&amp;lt;/code&amp;gt; || 65.79% (250/380) || 48.57% (119/245) || +17.22%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_balling.rs&amp;lt;/code&amp;gt; || 69.31% (140/202) || 53.91% (69/128) || +15.40%                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_data.rs&amp;lt;/code&amp;gt; || 60.76% (48/79) || 59.62% (31/52) || +1.14%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_feed.rs&amp;lt;/code&amp;gt; || 63.70% (186/292) || 45.00% (90/200) || +18.70%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_set_vals.rs&amp;lt;/code&amp;gt; || 66.12% (80/121) || 37.97% (30/79) || +28.15%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats.rs&amp;lt;/code&amp;gt; || 77.65% (139/179) || 67.59% (73/108) || +10.06%                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats_data_transfer.rs&amp;lt;/code&amp;gt; || 100.00% (7/7) || 100.00% (2/2) || +0.00%                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_midnight.rs&amp;lt;/code&amp;gt; || 100.00% (8/8) || 100.00% (4/4) || +0.00%                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_refill.rs&amp;lt;/code&amp;gt; || 66.94% (83/124) || 57.14% (48/84) || +9.80%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_schedule.rs&amp;lt;/code&amp;gt; || 85.54% (142/166) || 69.16% (74/107) || +16.38%                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_test_utils.rs&amp;lt;/code&amp;gt; || 100.00% (10/10) || 100.00% (5/5) || +0.00%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 63.64% (84/132) || 35.71% (30/84) || +27.92%                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/ipc_message.rs&amp;lt;/code&amp;gt; || 0.00% (0/51) || 0.00% (0/41) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/messaging.rs&amp;lt;/code&amp;gt; || 100.00% (44/44) || 80.00% (24/30) || +20.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed.rs&amp;lt;/code&amp;gt; || 86.40% (305/353) || 62.65% (104/166) || +23.75%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_profile.rs&amp;lt;/code&amp;gt; || 100.00% (100/100) || 100.00% (4/4) || +0.00%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_schedule_entry.rs&amp;lt;/code&amp;gt; || 97.62% (82/84) || 100.00% (10/10) || -2.38%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection.rs&amp;lt;/code&amp;gt; || 62.43% (113/181) || 56.64% (64/113) || +5.79%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_actuator_state.rs&amp;lt;/code&amp;gt; || 61.11% (11/18) || 54.55% (6/11) || +6.57%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_signals.rs&amp;lt;/code&amp;gt; || 62.50% (100/160) || 48.08% (50/104) || +14.42%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/config_app.rs&amp;lt;/code&amp;gt; || 82.35% (98/119) || 57.14% (44/77) || +25.21%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/execution_config.rs&amp;lt;/code&amp;gt; || 86.30% (63/73) || 72.34% (34/47) || +13.96%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/pid_file.rs&amp;lt;/code&amp;gt; || 96.67% (58/60) || 77.14% (27/35) || +19.52%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/run.rs&amp;lt;/code&amp;gt; || 83.40% (725/869) || 58.74% (306/521) || +24.66%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling.rs&amp;lt;/code&amp;gt; || 88.08% (340/386) || 61.08% (135/221) || +27.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_profile.rs&amp;lt;/code&amp;gt; || 100.00% (71/71) || 100.00% (18/18) || +0.00%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_set_val.rs&amp;lt;/code&amp;gt; || 77.97% (46/59) || 44.44% (24/54) || +33.53%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_dht.rs&amp;lt;/code&amp;gt; || 74.07% (40/54) || 64.71% (22/34) || +9.36%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_food_injection.rs&amp;lt;/code&amp;gt; || 83.33% (20/24) || 75.00% (12/16) || +8.33%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_i2c_interface.rs&amp;lt;/code&amp;gt; || 32.50% (13/40) || 28.00% (7/25) || +4.50%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_peristaltic_pump.rs&amp;lt;/code&amp;gt; || 85.00% (17/20) || 72.73% (8/11) || +12.27%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_relay_manager.rs&amp;lt;/code&amp;gt; || 82.50% (33/40) || 75.86% (22/29) || +6.64%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_sql_interface.rs&amp;lt;/code&amp;gt; || 66.21% (241/364) || 63.81% (134/210) || +2.40%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_water_injection.rs&amp;lt;/code&amp;gt; || 82.61% (19/23) || 75.00% (12/16) || +7.61%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/process_privileges.rs&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder.rs&amp;lt;/code&amp;gt; || 84.77% (590/696) || 54.12% (269/497) || +30.65%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_channels.rs&amp;lt;/code&amp;gt; || 58.82% (10/17) || 45.45% (5/11) || +13.37%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_data_frame.rs&amp;lt;/code&amp;gt; || 52.66% (101/192) || 22.31% (27/121) || +30.35%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/actuator_state.rs&amp;lt;/code&amp;gt; || 74.07% (20/27) || 75.00% (15/20) || -0.93%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/aquarium_device.rs&amp;lt;/code&amp;gt; || 66.67% (14/21) || 68.75% (11/16) || -2.08%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager.rs&amp;lt;/code&amp;gt; || 58.46% (394/674) || 45.43% (179/394) || +13.03%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager_channels.rs&amp;lt;/code&amp;gt; || 90.91% (20/22) || 45.83% (11/24) || +45.08%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific.rs&amp;lt;/code&amp;gt; || 74.77% (329/440) || 55.42% (92/166) || +19.35%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht.rs&amp;lt;/code&amp;gt; || 75.00% (105/140) || 53.64% (59/110) || +21.36%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20.rs&amp;lt;/code&amp;gt; || 74.55% (123/165) || 51.09% (47/92) || +23.46%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface.rs&amp;lt;/code&amp;gt; || 76.54% (362/473) || 57.51% (153/266) || +19.03%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager.rs&amp;lt;/code&amp;gt; || 76.10% (761/1000) || 54.34% (282/519) || +21.76%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager_channels.rs&amp;lt;/code&amp;gt; || 84.62% (33/39) || 75.00% (9/12) || +9.62%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating.rs&amp;lt;/code&amp;gt; || 90.96% (332/365) || 68.60% (148/216) || +22.36%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_actuation.rs&amp;lt;/code&amp;gt; || 84.47% (87/103) || 67.24% (39/58) || +17.23%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_set_vals.rs&amp;lt;/code&amp;gt; || 87.50% (35/40) || 64.29% (18/28) || +23.21%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_stats_entry.rs&amp;lt;/code&amp;gt; || 94.67% (142/150) || 82.22% (74/90) || +12.44%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (23/23) || +0.00%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile_config.rs&amp;lt;/code&amp;gt; || 70.83% (17/24) || 50.00% (7/14) || +20.83%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/temperature_gradient.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (32/32) || +0.00%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/thermal_control_status.rs&amp;lt;/code&amp;gt; || 81.25% (26/32) || 58.82% (10/17) || +22.43%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation.rs&amp;lt;/code&amp;gt; || 87.67% (192/219) || 61.16% (74/121) || +26.51%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 58.82% (30/51) || 58.82% (20/34) || +0.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/config.rs&amp;lt;/code&amp;gt; || 76.99% (830/1078) || 72.84% (437/600) || +4.15%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/spin_sleeper.rs&amp;lt;/code&amp;gt; || 79.17% (19/24) || 69.23% (9/13) || +9.94%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/thread_builder.rs&amp;lt;/code&amp;gt; || 92.59% (25/27) || 78.57% (11/14) || +14.02%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill.rs&amp;lt;/code&amp;gt; || 86.81% (447/515) || 69.45% (216/311) || +17.36%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch.rs&amp;lt;/code&amp;gt; || 84.14% (191/227) || 74.47% (105/141) || +9.67%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch_signals.rs&amp;lt;/code&amp;gt; || 90.36% (75/83) || 86.96% (40/46) || +3.40%                                                                                                    &lt;br /&gt;
    |}&lt;br /&gt;
&lt;br /&gt;
== System tests using the simulator ==&lt;br /&gt;
Running the application in simulator mode.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=565</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=565"/>
		<updated>2026-08-01T06:39:45Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Using llvm-cov */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Requirements =&lt;br /&gt;
&lt;br /&gt;
The requirement are structured in two levels:&lt;br /&gt;
* system requirements&lt;br /&gt;
* software requirements&lt;br /&gt;
&lt;br /&gt;
The requirements coverage report is generated with the following command: &amp;lt;code&amp;gt;oft trace doc src tests -o Html &amp;gt; doc/req/coverage.html&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The linkage of the software requirements to the implementation and related test cases has been reverse engineered using the following two prompts for each function group:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Check all software requirements of thermal.md if you can find the matching implementations and tests.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Now do the reverse: Go through the implementation and test cases in src/sensors and the integration tests in tests/sensors and label every test case and at least every function linking to a matching software requirement from sensors.md.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;br /&gt;
&lt;br /&gt;
= Test strategy =&lt;br /&gt;
== Unit tests ==&lt;br /&gt;
Located within the implementation code files within &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Integration tests in test ==&lt;br /&gt;
Separated from implementation code to the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
== Code coverage measurement ==&lt;br /&gt;
=== Using tarpaulin ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo tarpaulin --features &amp;quot;test-utils&amp;quot; --out Html&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurement takes app. 22 minutes.&lt;br /&gt;
&lt;br /&gt;
=== Using llvm-cov ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo +nightly llvm-cov --features &amp;quot;test-utils&amp;quot; --html --ignore-filename-regex '(main\.rs|.*_channels\.rs|mock.*\.rs|pingable\.rs|privilege_check\.rs)'&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The excluded files contain primarily debug functionality or wrappers without intrinsic logic which distorts the coverage measurement and where adding test cases would be meaningless.&lt;br /&gt;
&lt;br /&gt;
The nightly compiler build needs to be used because the application uses gating to exclude the unit tests from the coverage measurement: &amp;lt;code&amp;gt;#[cfg_attr(coverage_nightly, coverage(off))]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurements takes app. 8 minutes.&lt;br /&gt;
&lt;br /&gt;
=== Comparing the results from llvm-cov and tarpaulin ===&lt;br /&gt;
Based on commit &amp;lt;code&amp;gt;8f557814f679c30ebd70e181f2a3f996ce0e295c&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Module Summary Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! Subsystem / Module !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                        &lt;br /&gt;
    |- style=&amp;quot;font-weight:bold; background-color:#f0f0f0;&amp;quot;                                                                                                                                                 &lt;br /&gt;
    | '''TOTAL (All Application Source Files)''' || 71.53% (11216/15681) || 54.48% (4929/9047) || +17.04%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/&amp;lt;/code&amp;gt; || 0.00% (0/597) || 0.00% (0/457) || +0.00%                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/&amp;lt;/code&amp;gt; || 69.73% (1283/1840) || 52.72% (631/1197) || +17.01%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/&amp;lt;/code&amp;gt; || 46.32% (44/95) || 33.80% (24/71) || +12.51%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/&amp;lt;/code&amp;gt; || 79.06% (721/912) || 58.80% (244/415) || +20.26%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/&amp;lt;/code&amp;gt; || 84.06% (944/1123) || 59.48% (411/691) || +24.58%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/&amp;lt;/code&amp;gt; || 88.11% (467/530) || 60.33% (181/300) || +27.78%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/&amp;lt;/code&amp;gt; || 58.82% (610/1037) || 55.57% (334/601) || +3.25%                                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/&amp;lt;/code&amp;gt; || 77.46% (701/905) || 47.50% (304/640) || +29.96%                                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/&amp;lt;/code&amp;gt; || 60.22% (448/744) || 47.58% (216/454) || +12.64%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/&amp;lt;/code&amp;gt; || 75.70% (1748/2309) || 54.76% (656/1198) || +20.95%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/simulator/&amp;lt;/code&amp;gt; || 3.07% (10/326) || 2.05% (4/195) || +1.02%                                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/test_server/&amp;lt;/code&amp;gt; || 67.93% (822/1210) || 59.67% (429/719) || +8.27%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/&amp;lt;/code&amp;gt; || 88.45% (1019/1152) || 67.96% (437/643) || +20.49%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/&amp;lt;/code&amp;gt; || 80.91% (1187/1467) || 75.10% (564/751) || +5.81%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/&amp;lt;/code&amp;gt; || 86.10% (700/813) || 71.18% (331/465) || +14.92%                                                                                                                           &lt;br /&gt;
    |}  &lt;br /&gt;
&lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Detailed File Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! File Path !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_client.rs&amp;lt;/code&amp;gt; || 0.00% (0/117) || 0.00% (0/98) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_commissioning.rs&amp;lt;/code&amp;gt; || 0.00% (0/426) || 0.00% (0/315) || +0.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_test_server.rs&amp;lt;/code&amp;gt; || 0.00% (0/54) || 0.00% (0/44) || +0.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/database_interface_feed_trait.rs&amp;lt;/code&amp;gt; || 77.86% (102/131) || 58.06% (54/93) || +19.80%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/pingable.rs&amp;lt;/code&amp;gt; || 44.44% (4/9) || 33.33% (2/6) || +11.11%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface.rs&amp;lt;/code&amp;gt; || 65.79% (250/380) || 48.57% (119/245) || +17.22%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_balling.rs&amp;lt;/code&amp;gt; || 69.31% (140/202) || 53.91% (69/128) || +15.40%                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_data.rs&amp;lt;/code&amp;gt; || 60.76% (48/79) || 59.62% (31/52) || +1.14%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_feed.rs&amp;lt;/code&amp;gt; || 63.70% (186/292) || 45.00% (90/200) || +18.70%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_set_vals.rs&amp;lt;/code&amp;gt; || 66.12% (80/121) || 37.97% (30/79) || +28.15%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats.rs&amp;lt;/code&amp;gt; || 77.65% (139/179) || 67.59% (73/108) || +10.06%                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats_data_transfer.rs&amp;lt;/code&amp;gt; || 100.00% (7/7) || 100.00% (2/2) || +0.00%                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_midnight.rs&amp;lt;/code&amp;gt; || 100.00% (8/8) || 100.00% (4/4) || +0.00%                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_refill.rs&amp;lt;/code&amp;gt; || 66.94% (83/124) || 57.14% (48/84) || +9.80%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_schedule.rs&amp;lt;/code&amp;gt; || 85.54% (142/166) || 69.16% (74/107) || +16.38%                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_test_utils.rs&amp;lt;/code&amp;gt; || 100.00% (10/10) || 100.00% (5/5) || +0.00%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 63.64% (84/132) || 35.71% (30/84) || +27.92%                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/ipc_message.rs&amp;lt;/code&amp;gt; || 0.00% (0/51) || 0.00% (0/41) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/messaging.rs&amp;lt;/code&amp;gt; || 100.00% (44/44) || 80.00% (24/30) || +20.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed.rs&amp;lt;/code&amp;gt; || 86.40% (305/353) || 62.65% (104/166) || +23.75%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_profile.rs&amp;lt;/code&amp;gt; || 100.00% (100/100) || 100.00% (4/4) || +0.00%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_schedule_entry.rs&amp;lt;/code&amp;gt; || 97.62% (82/84) || 100.00% (10/10) || -2.38%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection.rs&amp;lt;/code&amp;gt; || 62.43% (113/181) || 56.64% (64/113) || +5.79%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_actuator_state.rs&amp;lt;/code&amp;gt; || 61.11% (11/18) || 54.55% (6/11) || +6.57%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_signals.rs&amp;lt;/code&amp;gt; || 62.50% (100/160) || 48.08% (50/104) || +14.42%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/config_app.rs&amp;lt;/code&amp;gt; || 82.35% (98/119) || 57.14% (44/77) || +25.21%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/execution_config.rs&amp;lt;/code&amp;gt; || 86.30% (63/73) || 72.34% (34/47) || +13.96%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/pid_file.rs&amp;lt;/code&amp;gt; || 96.67% (58/60) || 77.14% (27/35) || +19.52%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/run.rs&amp;lt;/code&amp;gt; || 83.40% (725/869) || 58.74% (306/521) || +24.66%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling.rs&amp;lt;/code&amp;gt; || 88.08% (340/386) || 61.08% (135/221) || +27.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_profile.rs&amp;lt;/code&amp;gt; || 100.00% (71/71) || 100.00% (18/18) || +0.00%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_set_val.rs&amp;lt;/code&amp;gt; || 77.97% (46/59) || 44.44% (24/54) || +33.53%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_dht.rs&amp;lt;/code&amp;gt; || 74.07% (40/54) || 64.71% (22/34) || +9.36%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_food_injection.rs&amp;lt;/code&amp;gt; || 83.33% (20/24) || 75.00% (12/16) || +8.33%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_i2c_interface.rs&amp;lt;/code&amp;gt; || 32.50% (13/40) || 28.00% (7/25) || +4.50%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_peristaltic_pump.rs&amp;lt;/code&amp;gt; || 85.00% (17/20) || 72.73% (8/11) || +12.27%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_relay_manager.rs&amp;lt;/code&amp;gt; || 82.50% (33/40) || 75.86% (22/29) || +6.64%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_sql_interface.rs&amp;lt;/code&amp;gt; || 66.21% (241/364) || 63.81% (134/210) || +2.40%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_water_injection.rs&amp;lt;/code&amp;gt; || 82.61% (19/23) || 75.00% (12/16) || +7.61%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/process_privileges.rs&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder.rs&amp;lt;/code&amp;gt; || 84.77% (590/696) || 54.12% (269/497) || +30.65%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_channels.rs&amp;lt;/code&amp;gt; || 58.82% (10/17) || 45.45% (5/11) || +13.37%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_data_frame.rs&amp;lt;/code&amp;gt; || 52.66% (101/192) || 22.31% (27/121) || +30.35%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/actuator_state.rs&amp;lt;/code&amp;gt; || 74.07% (20/27) || 75.00% (15/20) || -0.93%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/aquarium_device.rs&amp;lt;/code&amp;gt; || 66.67% (14/21) || 68.75% (11/16) || -2.08%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager.rs&amp;lt;/code&amp;gt; || 58.46% (394/674) || 45.43% (179/394) || +13.03%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager_channels.rs&amp;lt;/code&amp;gt; || 90.91% (20/22) || 45.83% (11/24) || +45.08%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific.rs&amp;lt;/code&amp;gt; || 74.77% (329/440) || 55.42% (92/166) || +19.35%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht.rs&amp;lt;/code&amp;gt; || 75.00% (105/140) || 53.64% (59/110) || +21.36%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20.rs&amp;lt;/code&amp;gt; || 74.55% (123/165) || 51.09% (47/92) || +23.46%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface.rs&amp;lt;/code&amp;gt; || 76.54% (362/473) || 57.51% (153/266) || +19.03%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager.rs&amp;lt;/code&amp;gt; || 76.10% (761/1000) || 54.34% (282/519) || +21.76%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager_channels.rs&amp;lt;/code&amp;gt; || 84.62% (33/39) || 75.00% (9/12) || +9.62%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating.rs&amp;lt;/code&amp;gt; || 90.96% (332/365) || 68.60% (148/216) || +22.36%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_actuation.rs&amp;lt;/code&amp;gt; || 84.47% (87/103) || 67.24% (39/58) || +17.23%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_set_vals.rs&amp;lt;/code&amp;gt; || 87.50% (35/40) || 64.29% (18/28) || +23.21%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_stats_entry.rs&amp;lt;/code&amp;gt; || 94.67% (142/150) || 82.22% (74/90) || +12.44%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (23/23) || +0.00%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile_config.rs&amp;lt;/code&amp;gt; || 70.83% (17/24) || 50.00% (7/14) || +20.83%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/temperature_gradient.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (32/32) || +0.00%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/thermal_control_status.rs&amp;lt;/code&amp;gt; || 81.25% (26/32) || 58.82% (10/17) || +22.43%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation.rs&amp;lt;/code&amp;gt; || 87.67% (192/219) || 61.16% (74/121) || +26.51%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 58.82% (30/51) || 58.82% (20/34) || +0.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/config.rs&amp;lt;/code&amp;gt; || 76.99% (830/1078) || 72.84% (437/600) || +4.15%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/spin_sleeper.rs&amp;lt;/code&amp;gt; || 79.17% (19/24) || 69.23% (9/13) || +9.94%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/thread_builder.rs&amp;lt;/code&amp;gt; || 92.59% (25/27) || 78.57% (11/14) || +14.02%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill.rs&amp;lt;/code&amp;gt; || 86.81% (447/515) || 69.45% (216/311) || +17.36%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch.rs&amp;lt;/code&amp;gt; || 84.14% (191/227) || 74.47% (105/141) || +9.67%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch_signals.rs&amp;lt;/code&amp;gt; || 90.36% (75/83) || 86.96% (40/46) || +3.40%                                                                                                    &lt;br /&gt;
    |}&lt;br /&gt;
&lt;br /&gt;
== System tests using the simulator ==&lt;br /&gt;
Running the application in simulator mode.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=564</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=564"/>
		<updated>2026-07-31T17:15:58Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Using llvm-cov */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Requirements =&lt;br /&gt;
&lt;br /&gt;
The requirement are structured in two levels:&lt;br /&gt;
* system requirements&lt;br /&gt;
* software requirements&lt;br /&gt;
&lt;br /&gt;
The requirements coverage report is generated with the following command: &amp;lt;code&amp;gt;oft trace doc src tests -o Html &amp;gt; doc/req/coverage.html&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The linkage of the software requirements to the implementation and related test cases has been reverse engineered using the following two prompts for each function group:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Check all software requirements of thermal.md if you can find the matching implementations and tests.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Now do the reverse: Go through the implementation and test cases in src/sensors and the integration tests in tests/sensors and label every test case and at least every function linking to a matching software requirement from sensors.md.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;br /&gt;
&lt;br /&gt;
= Test strategy =&lt;br /&gt;
== Unit tests ==&lt;br /&gt;
Located within the implementation code files within &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Integration tests in test ==&lt;br /&gt;
Separated from implementation code to the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
== Code coverage measurement ==&lt;br /&gt;
=== Using tarpaulin ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo tarpaulin --features &amp;quot;test-utils&amp;quot; --out Html&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurement takes app. 22 minutes.&lt;br /&gt;
&lt;br /&gt;
=== Using llvm-cov ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo llvm-cov --features &amp;quot;test-utils&amp;quot; --html --ignore-filename-regex '(main\.rs|.*_channels\.rs|mock.*\.rs|pingable\.rs|privilege_check\.rs)'&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The excluded files contain primarily debug functionality or wrappers without intrinsic logic which distorts the coverage measurement and where adding test cases would be meaningless.&lt;br /&gt;
&lt;br /&gt;
The measurements takes app. 8 minutes.&lt;br /&gt;
&lt;br /&gt;
=== Comparing the results from llvm-cov and tarpaulin ===&lt;br /&gt;
Based on commit &amp;lt;code&amp;gt;8f557814f679c30ebd70e181f2a3f996ce0e295c&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Module Summary Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! Subsystem / Module !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                        &lt;br /&gt;
    |- style=&amp;quot;font-weight:bold; background-color:#f0f0f0;&amp;quot;                                                                                                                                                 &lt;br /&gt;
    | '''TOTAL (All Application Source Files)''' || 71.53% (11216/15681) || 54.48% (4929/9047) || +17.04%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/&amp;lt;/code&amp;gt; || 0.00% (0/597) || 0.00% (0/457) || +0.00%                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/&amp;lt;/code&amp;gt; || 69.73% (1283/1840) || 52.72% (631/1197) || +17.01%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/&amp;lt;/code&amp;gt; || 46.32% (44/95) || 33.80% (24/71) || +12.51%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/&amp;lt;/code&amp;gt; || 79.06% (721/912) || 58.80% (244/415) || +20.26%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/&amp;lt;/code&amp;gt; || 84.06% (944/1123) || 59.48% (411/691) || +24.58%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/&amp;lt;/code&amp;gt; || 88.11% (467/530) || 60.33% (181/300) || +27.78%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/&amp;lt;/code&amp;gt; || 58.82% (610/1037) || 55.57% (334/601) || +3.25%                                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/&amp;lt;/code&amp;gt; || 77.46% (701/905) || 47.50% (304/640) || +29.96%                                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/&amp;lt;/code&amp;gt; || 60.22% (448/744) || 47.58% (216/454) || +12.64%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/&amp;lt;/code&amp;gt; || 75.70% (1748/2309) || 54.76% (656/1198) || +20.95%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/simulator/&amp;lt;/code&amp;gt; || 3.07% (10/326) || 2.05% (4/195) || +1.02%                                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/test_server/&amp;lt;/code&amp;gt; || 67.93% (822/1210) || 59.67% (429/719) || +8.27%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/&amp;lt;/code&amp;gt; || 88.45% (1019/1152) || 67.96% (437/643) || +20.49%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/&amp;lt;/code&amp;gt; || 80.91% (1187/1467) || 75.10% (564/751) || +5.81%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/&amp;lt;/code&amp;gt; || 86.10% (700/813) || 71.18% (331/465) || +14.92%                                                                                                                           &lt;br /&gt;
    |}  &lt;br /&gt;
&lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Detailed File Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! File Path !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_client.rs&amp;lt;/code&amp;gt; || 0.00% (0/117) || 0.00% (0/98) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_commissioning.rs&amp;lt;/code&amp;gt; || 0.00% (0/426) || 0.00% (0/315) || +0.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_test_server.rs&amp;lt;/code&amp;gt; || 0.00% (0/54) || 0.00% (0/44) || +0.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/database_interface_feed_trait.rs&amp;lt;/code&amp;gt; || 77.86% (102/131) || 58.06% (54/93) || +19.80%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/pingable.rs&amp;lt;/code&amp;gt; || 44.44% (4/9) || 33.33% (2/6) || +11.11%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface.rs&amp;lt;/code&amp;gt; || 65.79% (250/380) || 48.57% (119/245) || +17.22%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_balling.rs&amp;lt;/code&amp;gt; || 69.31% (140/202) || 53.91% (69/128) || +15.40%                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_data.rs&amp;lt;/code&amp;gt; || 60.76% (48/79) || 59.62% (31/52) || +1.14%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_feed.rs&amp;lt;/code&amp;gt; || 63.70% (186/292) || 45.00% (90/200) || +18.70%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_set_vals.rs&amp;lt;/code&amp;gt; || 66.12% (80/121) || 37.97% (30/79) || +28.15%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats.rs&amp;lt;/code&amp;gt; || 77.65% (139/179) || 67.59% (73/108) || +10.06%                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats_data_transfer.rs&amp;lt;/code&amp;gt; || 100.00% (7/7) || 100.00% (2/2) || +0.00%                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_midnight.rs&amp;lt;/code&amp;gt; || 100.00% (8/8) || 100.00% (4/4) || +0.00%                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_refill.rs&amp;lt;/code&amp;gt; || 66.94% (83/124) || 57.14% (48/84) || +9.80%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_schedule.rs&amp;lt;/code&amp;gt; || 85.54% (142/166) || 69.16% (74/107) || +16.38%                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_test_utils.rs&amp;lt;/code&amp;gt; || 100.00% (10/10) || 100.00% (5/5) || +0.00%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 63.64% (84/132) || 35.71% (30/84) || +27.92%                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/ipc_message.rs&amp;lt;/code&amp;gt; || 0.00% (0/51) || 0.00% (0/41) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/messaging.rs&amp;lt;/code&amp;gt; || 100.00% (44/44) || 80.00% (24/30) || +20.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed.rs&amp;lt;/code&amp;gt; || 86.40% (305/353) || 62.65% (104/166) || +23.75%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_profile.rs&amp;lt;/code&amp;gt; || 100.00% (100/100) || 100.00% (4/4) || +0.00%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_schedule_entry.rs&amp;lt;/code&amp;gt; || 97.62% (82/84) || 100.00% (10/10) || -2.38%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection.rs&amp;lt;/code&amp;gt; || 62.43% (113/181) || 56.64% (64/113) || +5.79%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_actuator_state.rs&amp;lt;/code&amp;gt; || 61.11% (11/18) || 54.55% (6/11) || +6.57%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_signals.rs&amp;lt;/code&amp;gt; || 62.50% (100/160) || 48.08% (50/104) || +14.42%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/config_app.rs&amp;lt;/code&amp;gt; || 82.35% (98/119) || 57.14% (44/77) || +25.21%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/execution_config.rs&amp;lt;/code&amp;gt; || 86.30% (63/73) || 72.34% (34/47) || +13.96%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/pid_file.rs&amp;lt;/code&amp;gt; || 96.67% (58/60) || 77.14% (27/35) || +19.52%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/run.rs&amp;lt;/code&amp;gt; || 83.40% (725/869) || 58.74% (306/521) || +24.66%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling.rs&amp;lt;/code&amp;gt; || 88.08% (340/386) || 61.08% (135/221) || +27.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_profile.rs&amp;lt;/code&amp;gt; || 100.00% (71/71) || 100.00% (18/18) || +0.00%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_set_val.rs&amp;lt;/code&amp;gt; || 77.97% (46/59) || 44.44% (24/54) || +33.53%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_dht.rs&amp;lt;/code&amp;gt; || 74.07% (40/54) || 64.71% (22/34) || +9.36%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_food_injection.rs&amp;lt;/code&amp;gt; || 83.33% (20/24) || 75.00% (12/16) || +8.33%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_i2c_interface.rs&amp;lt;/code&amp;gt; || 32.50% (13/40) || 28.00% (7/25) || +4.50%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_peristaltic_pump.rs&amp;lt;/code&amp;gt; || 85.00% (17/20) || 72.73% (8/11) || +12.27%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_relay_manager.rs&amp;lt;/code&amp;gt; || 82.50% (33/40) || 75.86% (22/29) || +6.64%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_sql_interface.rs&amp;lt;/code&amp;gt; || 66.21% (241/364) || 63.81% (134/210) || +2.40%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_water_injection.rs&amp;lt;/code&amp;gt; || 82.61% (19/23) || 75.00% (12/16) || +7.61%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/process_privileges.rs&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder.rs&amp;lt;/code&amp;gt; || 84.77% (590/696) || 54.12% (269/497) || +30.65%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_channels.rs&amp;lt;/code&amp;gt; || 58.82% (10/17) || 45.45% (5/11) || +13.37%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_data_frame.rs&amp;lt;/code&amp;gt; || 52.66% (101/192) || 22.31% (27/121) || +30.35%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/actuator_state.rs&amp;lt;/code&amp;gt; || 74.07% (20/27) || 75.00% (15/20) || -0.93%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/aquarium_device.rs&amp;lt;/code&amp;gt; || 66.67% (14/21) || 68.75% (11/16) || -2.08%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager.rs&amp;lt;/code&amp;gt; || 58.46% (394/674) || 45.43% (179/394) || +13.03%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager_channels.rs&amp;lt;/code&amp;gt; || 90.91% (20/22) || 45.83% (11/24) || +45.08%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific.rs&amp;lt;/code&amp;gt; || 74.77% (329/440) || 55.42% (92/166) || +19.35%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht.rs&amp;lt;/code&amp;gt; || 75.00% (105/140) || 53.64% (59/110) || +21.36%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20.rs&amp;lt;/code&amp;gt; || 74.55% (123/165) || 51.09% (47/92) || +23.46%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface.rs&amp;lt;/code&amp;gt; || 76.54% (362/473) || 57.51% (153/266) || +19.03%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager.rs&amp;lt;/code&amp;gt; || 76.10% (761/1000) || 54.34% (282/519) || +21.76%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager_channels.rs&amp;lt;/code&amp;gt; || 84.62% (33/39) || 75.00% (9/12) || +9.62%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating.rs&amp;lt;/code&amp;gt; || 90.96% (332/365) || 68.60% (148/216) || +22.36%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_actuation.rs&amp;lt;/code&amp;gt; || 84.47% (87/103) || 67.24% (39/58) || +17.23%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_set_vals.rs&amp;lt;/code&amp;gt; || 87.50% (35/40) || 64.29% (18/28) || +23.21%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_stats_entry.rs&amp;lt;/code&amp;gt; || 94.67% (142/150) || 82.22% (74/90) || +12.44%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (23/23) || +0.00%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile_config.rs&amp;lt;/code&amp;gt; || 70.83% (17/24) || 50.00% (7/14) || +20.83%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/temperature_gradient.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (32/32) || +0.00%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/thermal_control_status.rs&amp;lt;/code&amp;gt; || 81.25% (26/32) || 58.82% (10/17) || +22.43%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation.rs&amp;lt;/code&amp;gt; || 87.67% (192/219) || 61.16% (74/121) || +26.51%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 58.82% (30/51) || 58.82% (20/34) || +0.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/config.rs&amp;lt;/code&amp;gt; || 76.99% (830/1078) || 72.84% (437/600) || +4.15%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/spin_sleeper.rs&amp;lt;/code&amp;gt; || 79.17% (19/24) || 69.23% (9/13) || +9.94%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/thread_builder.rs&amp;lt;/code&amp;gt; || 92.59% (25/27) || 78.57% (11/14) || +14.02%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill.rs&amp;lt;/code&amp;gt; || 86.81% (447/515) || 69.45% (216/311) || +17.36%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch.rs&amp;lt;/code&amp;gt; || 84.14% (191/227) || 74.47% (105/141) || +9.67%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch_signals.rs&amp;lt;/code&amp;gt; || 90.36% (75/83) || 86.96% (40/46) || +3.40%                                                                                                    &lt;br /&gt;
    |}&lt;br /&gt;
&lt;br /&gt;
== System tests using the simulator ==&lt;br /&gt;
Running the application in simulator mode.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=563</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=563"/>
		<updated>2026-07-31T17:15:34Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Using llvm-cov */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Requirements =&lt;br /&gt;
&lt;br /&gt;
The requirement are structured in two levels:&lt;br /&gt;
* system requirements&lt;br /&gt;
* software requirements&lt;br /&gt;
&lt;br /&gt;
The requirements coverage report is generated with the following command: &amp;lt;code&amp;gt;oft trace doc src tests -o Html &amp;gt; doc/req/coverage.html&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The linkage of the software requirements to the implementation and related test cases has been reverse engineered using the following two prompts for each function group:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Check all software requirements of thermal.md if you can find the matching implementations and tests.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Now do the reverse: Go through the implementation and test cases in src/sensors and the integration tests in tests/sensors and label every test case and at least every function linking to a matching software requirement from sensors.md.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;br /&gt;
&lt;br /&gt;
= Test strategy =&lt;br /&gt;
== Unit tests ==&lt;br /&gt;
Located within the implementation code files within &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Integration tests in test ==&lt;br /&gt;
Separated from implementation code to the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
== Code coverage measurement ==&lt;br /&gt;
=== Using tarpaulin ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo tarpaulin --features &amp;quot;test-utils&amp;quot; --out Html&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurement takes app. 22 minutes.&lt;br /&gt;
&lt;br /&gt;
=== Using llvm-cov ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo llvm-cov --features &amp;quot;test-utils&amp;quot; --html --ignore-filename-regex '(main\.rs|.*_channels\.rs|mock.*\.rs|pingable\.rs|privilege_check\.rs)'&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The excluded files contain primarily debug functionality which distorts the coverage measurement and where adding test cases would be meaningless.&lt;br /&gt;
&lt;br /&gt;
The measurements takes app. 8 minutes.&lt;br /&gt;
&lt;br /&gt;
=== Comparing the results from llvm-cov and tarpaulin ===&lt;br /&gt;
Based on commit &amp;lt;code&amp;gt;8f557814f679c30ebd70e181f2a3f996ce0e295c&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Module Summary Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! Subsystem / Module !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                        &lt;br /&gt;
    |- style=&amp;quot;font-weight:bold; background-color:#f0f0f0;&amp;quot;                                                                                                                                                 &lt;br /&gt;
    | '''TOTAL (All Application Source Files)''' || 71.53% (11216/15681) || 54.48% (4929/9047) || +17.04%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/&amp;lt;/code&amp;gt; || 0.00% (0/597) || 0.00% (0/457) || +0.00%                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/&amp;lt;/code&amp;gt; || 69.73% (1283/1840) || 52.72% (631/1197) || +17.01%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/&amp;lt;/code&amp;gt; || 46.32% (44/95) || 33.80% (24/71) || +12.51%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/&amp;lt;/code&amp;gt; || 79.06% (721/912) || 58.80% (244/415) || +20.26%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/&amp;lt;/code&amp;gt; || 84.06% (944/1123) || 59.48% (411/691) || +24.58%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/&amp;lt;/code&amp;gt; || 88.11% (467/530) || 60.33% (181/300) || +27.78%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/&amp;lt;/code&amp;gt; || 58.82% (610/1037) || 55.57% (334/601) || +3.25%                                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/&amp;lt;/code&amp;gt; || 77.46% (701/905) || 47.50% (304/640) || +29.96%                                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/&amp;lt;/code&amp;gt; || 60.22% (448/744) || 47.58% (216/454) || +12.64%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/&amp;lt;/code&amp;gt; || 75.70% (1748/2309) || 54.76% (656/1198) || +20.95%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/simulator/&amp;lt;/code&amp;gt; || 3.07% (10/326) || 2.05% (4/195) || +1.02%                                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/test_server/&amp;lt;/code&amp;gt; || 67.93% (822/1210) || 59.67% (429/719) || +8.27%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/&amp;lt;/code&amp;gt; || 88.45% (1019/1152) || 67.96% (437/643) || +20.49%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/&amp;lt;/code&amp;gt; || 80.91% (1187/1467) || 75.10% (564/751) || +5.81%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/&amp;lt;/code&amp;gt; || 86.10% (700/813) || 71.18% (331/465) || +14.92%                                                                                                                           &lt;br /&gt;
    |}  &lt;br /&gt;
&lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Detailed File Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! File Path !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_client.rs&amp;lt;/code&amp;gt; || 0.00% (0/117) || 0.00% (0/98) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_commissioning.rs&amp;lt;/code&amp;gt; || 0.00% (0/426) || 0.00% (0/315) || +0.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_test_server.rs&amp;lt;/code&amp;gt; || 0.00% (0/54) || 0.00% (0/44) || +0.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/database_interface_feed_trait.rs&amp;lt;/code&amp;gt; || 77.86% (102/131) || 58.06% (54/93) || +19.80%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/pingable.rs&amp;lt;/code&amp;gt; || 44.44% (4/9) || 33.33% (2/6) || +11.11%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface.rs&amp;lt;/code&amp;gt; || 65.79% (250/380) || 48.57% (119/245) || +17.22%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_balling.rs&amp;lt;/code&amp;gt; || 69.31% (140/202) || 53.91% (69/128) || +15.40%                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_data.rs&amp;lt;/code&amp;gt; || 60.76% (48/79) || 59.62% (31/52) || +1.14%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_feed.rs&amp;lt;/code&amp;gt; || 63.70% (186/292) || 45.00% (90/200) || +18.70%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_set_vals.rs&amp;lt;/code&amp;gt; || 66.12% (80/121) || 37.97% (30/79) || +28.15%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats.rs&amp;lt;/code&amp;gt; || 77.65% (139/179) || 67.59% (73/108) || +10.06%                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats_data_transfer.rs&amp;lt;/code&amp;gt; || 100.00% (7/7) || 100.00% (2/2) || +0.00%                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_midnight.rs&amp;lt;/code&amp;gt; || 100.00% (8/8) || 100.00% (4/4) || +0.00%                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_refill.rs&amp;lt;/code&amp;gt; || 66.94% (83/124) || 57.14% (48/84) || +9.80%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_schedule.rs&amp;lt;/code&amp;gt; || 85.54% (142/166) || 69.16% (74/107) || +16.38%                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_test_utils.rs&amp;lt;/code&amp;gt; || 100.00% (10/10) || 100.00% (5/5) || +0.00%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 63.64% (84/132) || 35.71% (30/84) || +27.92%                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/ipc_message.rs&amp;lt;/code&amp;gt; || 0.00% (0/51) || 0.00% (0/41) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/messaging.rs&amp;lt;/code&amp;gt; || 100.00% (44/44) || 80.00% (24/30) || +20.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed.rs&amp;lt;/code&amp;gt; || 86.40% (305/353) || 62.65% (104/166) || +23.75%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_profile.rs&amp;lt;/code&amp;gt; || 100.00% (100/100) || 100.00% (4/4) || +0.00%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_schedule_entry.rs&amp;lt;/code&amp;gt; || 97.62% (82/84) || 100.00% (10/10) || -2.38%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection.rs&amp;lt;/code&amp;gt; || 62.43% (113/181) || 56.64% (64/113) || +5.79%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_actuator_state.rs&amp;lt;/code&amp;gt; || 61.11% (11/18) || 54.55% (6/11) || +6.57%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_signals.rs&amp;lt;/code&amp;gt; || 62.50% (100/160) || 48.08% (50/104) || +14.42%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/config_app.rs&amp;lt;/code&amp;gt; || 82.35% (98/119) || 57.14% (44/77) || +25.21%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/execution_config.rs&amp;lt;/code&amp;gt; || 86.30% (63/73) || 72.34% (34/47) || +13.96%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/pid_file.rs&amp;lt;/code&amp;gt; || 96.67% (58/60) || 77.14% (27/35) || +19.52%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/run.rs&amp;lt;/code&amp;gt; || 83.40% (725/869) || 58.74% (306/521) || +24.66%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling.rs&amp;lt;/code&amp;gt; || 88.08% (340/386) || 61.08% (135/221) || +27.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_profile.rs&amp;lt;/code&amp;gt; || 100.00% (71/71) || 100.00% (18/18) || +0.00%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_set_val.rs&amp;lt;/code&amp;gt; || 77.97% (46/59) || 44.44% (24/54) || +33.53%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_dht.rs&amp;lt;/code&amp;gt; || 74.07% (40/54) || 64.71% (22/34) || +9.36%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_food_injection.rs&amp;lt;/code&amp;gt; || 83.33% (20/24) || 75.00% (12/16) || +8.33%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_i2c_interface.rs&amp;lt;/code&amp;gt; || 32.50% (13/40) || 28.00% (7/25) || +4.50%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_peristaltic_pump.rs&amp;lt;/code&amp;gt; || 85.00% (17/20) || 72.73% (8/11) || +12.27%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_relay_manager.rs&amp;lt;/code&amp;gt; || 82.50% (33/40) || 75.86% (22/29) || +6.64%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_sql_interface.rs&amp;lt;/code&amp;gt; || 66.21% (241/364) || 63.81% (134/210) || +2.40%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_water_injection.rs&amp;lt;/code&amp;gt; || 82.61% (19/23) || 75.00% (12/16) || +7.61%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/process_privileges.rs&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder.rs&amp;lt;/code&amp;gt; || 84.77% (590/696) || 54.12% (269/497) || +30.65%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_channels.rs&amp;lt;/code&amp;gt; || 58.82% (10/17) || 45.45% (5/11) || +13.37%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_data_frame.rs&amp;lt;/code&amp;gt; || 52.66% (101/192) || 22.31% (27/121) || +30.35%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/actuator_state.rs&amp;lt;/code&amp;gt; || 74.07% (20/27) || 75.00% (15/20) || -0.93%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/aquarium_device.rs&amp;lt;/code&amp;gt; || 66.67% (14/21) || 68.75% (11/16) || -2.08%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager.rs&amp;lt;/code&amp;gt; || 58.46% (394/674) || 45.43% (179/394) || +13.03%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager_channels.rs&amp;lt;/code&amp;gt; || 90.91% (20/22) || 45.83% (11/24) || +45.08%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific.rs&amp;lt;/code&amp;gt; || 74.77% (329/440) || 55.42% (92/166) || +19.35%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht.rs&amp;lt;/code&amp;gt; || 75.00% (105/140) || 53.64% (59/110) || +21.36%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20.rs&amp;lt;/code&amp;gt; || 74.55% (123/165) || 51.09% (47/92) || +23.46%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface.rs&amp;lt;/code&amp;gt; || 76.54% (362/473) || 57.51% (153/266) || +19.03%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager.rs&amp;lt;/code&amp;gt; || 76.10% (761/1000) || 54.34% (282/519) || +21.76%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager_channels.rs&amp;lt;/code&amp;gt; || 84.62% (33/39) || 75.00% (9/12) || +9.62%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating.rs&amp;lt;/code&amp;gt; || 90.96% (332/365) || 68.60% (148/216) || +22.36%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_actuation.rs&amp;lt;/code&amp;gt; || 84.47% (87/103) || 67.24% (39/58) || +17.23%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_set_vals.rs&amp;lt;/code&amp;gt; || 87.50% (35/40) || 64.29% (18/28) || +23.21%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_stats_entry.rs&amp;lt;/code&amp;gt; || 94.67% (142/150) || 82.22% (74/90) || +12.44%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (23/23) || +0.00%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile_config.rs&amp;lt;/code&amp;gt; || 70.83% (17/24) || 50.00% (7/14) || +20.83%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/temperature_gradient.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (32/32) || +0.00%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/thermal_control_status.rs&amp;lt;/code&amp;gt; || 81.25% (26/32) || 58.82% (10/17) || +22.43%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation.rs&amp;lt;/code&amp;gt; || 87.67% (192/219) || 61.16% (74/121) || +26.51%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 58.82% (30/51) || 58.82% (20/34) || +0.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/config.rs&amp;lt;/code&amp;gt; || 76.99% (830/1078) || 72.84% (437/600) || +4.15%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/spin_sleeper.rs&amp;lt;/code&amp;gt; || 79.17% (19/24) || 69.23% (9/13) || +9.94%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/thread_builder.rs&amp;lt;/code&amp;gt; || 92.59% (25/27) || 78.57% (11/14) || +14.02%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill.rs&amp;lt;/code&amp;gt; || 86.81% (447/515) || 69.45% (216/311) || +17.36%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch.rs&amp;lt;/code&amp;gt; || 84.14% (191/227) || 74.47% (105/141) || +9.67%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch_signals.rs&amp;lt;/code&amp;gt; || 90.36% (75/83) || 86.96% (40/46) || +3.40%                                                                                                    &lt;br /&gt;
    |}&lt;br /&gt;
&lt;br /&gt;
== System tests using the simulator ==&lt;br /&gt;
Running the application in simulator mode.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=562</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=562"/>
		<updated>2026-07-31T06:18:08Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Code coverage measurement */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Requirements =&lt;br /&gt;
&lt;br /&gt;
The requirement are structured in two levels:&lt;br /&gt;
* system requirements&lt;br /&gt;
* software requirements&lt;br /&gt;
&lt;br /&gt;
The requirements coverage report is generated with the following command: &amp;lt;code&amp;gt;oft trace doc src tests -o Html &amp;gt; doc/req/coverage.html&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The linkage of the software requirements to the implementation and related test cases has been reverse engineered using the following two prompts for each function group:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Check all software requirements of thermal.md if you can find the matching implementations and tests.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Now do the reverse: Go through the implementation and test cases in src/sensors and the integration tests in tests/sensors and label every test case and at least every function linking to a matching software requirement from sensors.md.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;br /&gt;
&lt;br /&gt;
= Test strategy =&lt;br /&gt;
== Unit tests ==&lt;br /&gt;
Located within the implementation code files within &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Integration tests in test ==&lt;br /&gt;
Separated from implementation code to the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
== Code coverage measurement ==&lt;br /&gt;
=== Using tarpaulin ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo tarpaulin --features &amp;quot;test-utils&amp;quot; --out Html&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurement takes app. 22 minutes.&lt;br /&gt;
&lt;br /&gt;
=== Using llvm-cov ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo llvm-cov --features &amp;quot;test-utils&amp;quot; --html --ignore-filename-regex '(main\.rs|.*_channels\.rs|mock.*\.rs|pingable\.rs)'&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The excluded files contain primarily debug functionality which distorts the coverage measurement and where adding test cases would be meaningless.&lt;br /&gt;
&lt;br /&gt;
The measurements takes app. 8 minutes.&lt;br /&gt;
=== Comparing the results from llvm-cov and tarpaulin ===&lt;br /&gt;
Based on commit &amp;lt;code&amp;gt;8f557814f679c30ebd70e181f2a3f996ce0e295c&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Module Summary Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! Subsystem / Module !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                        &lt;br /&gt;
    |- style=&amp;quot;font-weight:bold; background-color:#f0f0f0;&amp;quot;                                                                                                                                                 &lt;br /&gt;
    | '''TOTAL (All Application Source Files)''' || 71.53% (11216/15681) || 54.48% (4929/9047) || +17.04%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/&amp;lt;/code&amp;gt; || 0.00% (0/597) || 0.00% (0/457) || +0.00%                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/&amp;lt;/code&amp;gt; || 69.73% (1283/1840) || 52.72% (631/1197) || +17.01%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/&amp;lt;/code&amp;gt; || 46.32% (44/95) || 33.80% (24/71) || +12.51%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/&amp;lt;/code&amp;gt; || 79.06% (721/912) || 58.80% (244/415) || +20.26%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/&amp;lt;/code&amp;gt; || 84.06% (944/1123) || 59.48% (411/691) || +24.58%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/&amp;lt;/code&amp;gt; || 88.11% (467/530) || 60.33% (181/300) || +27.78%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/&amp;lt;/code&amp;gt; || 58.82% (610/1037) || 55.57% (334/601) || +3.25%                                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/&amp;lt;/code&amp;gt; || 77.46% (701/905) || 47.50% (304/640) || +29.96%                                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/&amp;lt;/code&amp;gt; || 60.22% (448/744) || 47.58% (216/454) || +12.64%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/&amp;lt;/code&amp;gt; || 75.70% (1748/2309) || 54.76% (656/1198) || +20.95%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/simulator/&amp;lt;/code&amp;gt; || 3.07% (10/326) || 2.05% (4/195) || +1.02%                                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/test_server/&amp;lt;/code&amp;gt; || 67.93% (822/1210) || 59.67% (429/719) || +8.27%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/&amp;lt;/code&amp;gt; || 88.45% (1019/1152) || 67.96% (437/643) || +20.49%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/&amp;lt;/code&amp;gt; || 80.91% (1187/1467) || 75.10% (564/751) || +5.81%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/&amp;lt;/code&amp;gt; || 86.10% (700/813) || 71.18% (331/465) || +14.92%                                                                                                                           &lt;br /&gt;
    |}  &lt;br /&gt;
&lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Detailed File Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! File Path !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_client.rs&amp;lt;/code&amp;gt; || 0.00% (0/117) || 0.00% (0/98) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_commissioning.rs&amp;lt;/code&amp;gt; || 0.00% (0/426) || 0.00% (0/315) || +0.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_test_server.rs&amp;lt;/code&amp;gt; || 0.00% (0/54) || 0.00% (0/44) || +0.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/database_interface_feed_trait.rs&amp;lt;/code&amp;gt; || 77.86% (102/131) || 58.06% (54/93) || +19.80%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/pingable.rs&amp;lt;/code&amp;gt; || 44.44% (4/9) || 33.33% (2/6) || +11.11%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface.rs&amp;lt;/code&amp;gt; || 65.79% (250/380) || 48.57% (119/245) || +17.22%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_balling.rs&amp;lt;/code&amp;gt; || 69.31% (140/202) || 53.91% (69/128) || +15.40%                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_data.rs&amp;lt;/code&amp;gt; || 60.76% (48/79) || 59.62% (31/52) || +1.14%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_feed.rs&amp;lt;/code&amp;gt; || 63.70% (186/292) || 45.00% (90/200) || +18.70%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_set_vals.rs&amp;lt;/code&amp;gt; || 66.12% (80/121) || 37.97% (30/79) || +28.15%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats.rs&amp;lt;/code&amp;gt; || 77.65% (139/179) || 67.59% (73/108) || +10.06%                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats_data_transfer.rs&amp;lt;/code&amp;gt; || 100.00% (7/7) || 100.00% (2/2) || +0.00%                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_midnight.rs&amp;lt;/code&amp;gt; || 100.00% (8/8) || 100.00% (4/4) || +0.00%                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_refill.rs&amp;lt;/code&amp;gt; || 66.94% (83/124) || 57.14% (48/84) || +9.80%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_schedule.rs&amp;lt;/code&amp;gt; || 85.54% (142/166) || 69.16% (74/107) || +16.38%                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_test_utils.rs&amp;lt;/code&amp;gt; || 100.00% (10/10) || 100.00% (5/5) || +0.00%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 63.64% (84/132) || 35.71% (30/84) || +27.92%                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/ipc_message.rs&amp;lt;/code&amp;gt; || 0.00% (0/51) || 0.00% (0/41) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/messaging.rs&amp;lt;/code&amp;gt; || 100.00% (44/44) || 80.00% (24/30) || +20.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed.rs&amp;lt;/code&amp;gt; || 86.40% (305/353) || 62.65% (104/166) || +23.75%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_profile.rs&amp;lt;/code&amp;gt; || 100.00% (100/100) || 100.00% (4/4) || +0.00%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_schedule_entry.rs&amp;lt;/code&amp;gt; || 97.62% (82/84) || 100.00% (10/10) || -2.38%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection.rs&amp;lt;/code&amp;gt; || 62.43% (113/181) || 56.64% (64/113) || +5.79%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_actuator_state.rs&amp;lt;/code&amp;gt; || 61.11% (11/18) || 54.55% (6/11) || +6.57%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_signals.rs&amp;lt;/code&amp;gt; || 62.50% (100/160) || 48.08% (50/104) || +14.42%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/config_app.rs&amp;lt;/code&amp;gt; || 82.35% (98/119) || 57.14% (44/77) || +25.21%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/execution_config.rs&amp;lt;/code&amp;gt; || 86.30% (63/73) || 72.34% (34/47) || +13.96%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/pid_file.rs&amp;lt;/code&amp;gt; || 96.67% (58/60) || 77.14% (27/35) || +19.52%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/run.rs&amp;lt;/code&amp;gt; || 83.40% (725/869) || 58.74% (306/521) || +24.66%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling.rs&amp;lt;/code&amp;gt; || 88.08% (340/386) || 61.08% (135/221) || +27.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_profile.rs&amp;lt;/code&amp;gt; || 100.00% (71/71) || 100.00% (18/18) || +0.00%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_set_val.rs&amp;lt;/code&amp;gt; || 77.97% (46/59) || 44.44% (24/54) || +33.53%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_dht.rs&amp;lt;/code&amp;gt; || 74.07% (40/54) || 64.71% (22/34) || +9.36%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_food_injection.rs&amp;lt;/code&amp;gt; || 83.33% (20/24) || 75.00% (12/16) || +8.33%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_i2c_interface.rs&amp;lt;/code&amp;gt; || 32.50% (13/40) || 28.00% (7/25) || +4.50%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_peristaltic_pump.rs&amp;lt;/code&amp;gt; || 85.00% (17/20) || 72.73% (8/11) || +12.27%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_relay_manager.rs&amp;lt;/code&amp;gt; || 82.50% (33/40) || 75.86% (22/29) || +6.64%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_sql_interface.rs&amp;lt;/code&amp;gt; || 66.21% (241/364) || 63.81% (134/210) || +2.40%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_water_injection.rs&amp;lt;/code&amp;gt; || 82.61% (19/23) || 75.00% (12/16) || +7.61%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/process_privileges.rs&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder.rs&amp;lt;/code&amp;gt; || 84.77% (590/696) || 54.12% (269/497) || +30.65%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_channels.rs&amp;lt;/code&amp;gt; || 58.82% (10/17) || 45.45% (5/11) || +13.37%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_data_frame.rs&amp;lt;/code&amp;gt; || 52.66% (101/192) || 22.31% (27/121) || +30.35%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/actuator_state.rs&amp;lt;/code&amp;gt; || 74.07% (20/27) || 75.00% (15/20) || -0.93%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/aquarium_device.rs&amp;lt;/code&amp;gt; || 66.67% (14/21) || 68.75% (11/16) || -2.08%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager.rs&amp;lt;/code&amp;gt; || 58.46% (394/674) || 45.43% (179/394) || +13.03%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager_channels.rs&amp;lt;/code&amp;gt; || 90.91% (20/22) || 45.83% (11/24) || +45.08%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific.rs&amp;lt;/code&amp;gt; || 74.77% (329/440) || 55.42% (92/166) || +19.35%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht.rs&amp;lt;/code&amp;gt; || 75.00% (105/140) || 53.64% (59/110) || +21.36%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20.rs&amp;lt;/code&amp;gt; || 74.55% (123/165) || 51.09% (47/92) || +23.46%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface.rs&amp;lt;/code&amp;gt; || 76.54% (362/473) || 57.51% (153/266) || +19.03%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager.rs&amp;lt;/code&amp;gt; || 76.10% (761/1000) || 54.34% (282/519) || +21.76%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager_channels.rs&amp;lt;/code&amp;gt; || 84.62% (33/39) || 75.00% (9/12) || +9.62%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating.rs&amp;lt;/code&amp;gt; || 90.96% (332/365) || 68.60% (148/216) || +22.36%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_actuation.rs&amp;lt;/code&amp;gt; || 84.47% (87/103) || 67.24% (39/58) || +17.23%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_set_vals.rs&amp;lt;/code&amp;gt; || 87.50% (35/40) || 64.29% (18/28) || +23.21%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_stats_entry.rs&amp;lt;/code&amp;gt; || 94.67% (142/150) || 82.22% (74/90) || +12.44%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (23/23) || +0.00%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile_config.rs&amp;lt;/code&amp;gt; || 70.83% (17/24) || 50.00% (7/14) || +20.83%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/temperature_gradient.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (32/32) || +0.00%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/thermal_control_status.rs&amp;lt;/code&amp;gt; || 81.25% (26/32) || 58.82% (10/17) || +22.43%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation.rs&amp;lt;/code&amp;gt; || 87.67% (192/219) || 61.16% (74/121) || +26.51%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 58.82% (30/51) || 58.82% (20/34) || +0.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/config.rs&amp;lt;/code&amp;gt; || 76.99% (830/1078) || 72.84% (437/600) || +4.15%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/spin_sleeper.rs&amp;lt;/code&amp;gt; || 79.17% (19/24) || 69.23% (9/13) || +9.94%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/thread_builder.rs&amp;lt;/code&amp;gt; || 92.59% (25/27) || 78.57% (11/14) || +14.02%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill.rs&amp;lt;/code&amp;gt; || 86.81% (447/515) || 69.45% (216/311) || +17.36%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch.rs&amp;lt;/code&amp;gt; || 84.14% (191/227) || 74.47% (105/141) || +9.67%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch_signals.rs&amp;lt;/code&amp;gt; || 90.36% (75/83) || 86.96% (40/46) || +3.40%                                                                                                    &lt;br /&gt;
    |}&lt;br /&gt;
&lt;br /&gt;
== System tests using the simulator ==&lt;br /&gt;
Running the application in simulator mode.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=561</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=561"/>
		<updated>2026-07-26T06:43:49Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Requirements */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Requirements =&lt;br /&gt;
&lt;br /&gt;
The requirement are structured in two levels:&lt;br /&gt;
* system requirements&lt;br /&gt;
* software requirements&lt;br /&gt;
&lt;br /&gt;
The requirements coverage report is generated with the following command: &amp;lt;code&amp;gt;oft trace doc src tests -o Html &amp;gt; doc/req/coverage.html&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The linkage of the software requirements to the implementation and related test cases has been reverse engineered using the following two prompts for each function group:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Check all software requirements of thermal.md if you can find the matching implementations and tests.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Now do the reverse: Go through the implementation and test cases in src/sensors and the integration tests in tests/sensors and label every test case and at least every function linking to a matching software requirement from sensors.md.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;br /&gt;
&lt;br /&gt;
= Test strategy =&lt;br /&gt;
== Unit tests ==&lt;br /&gt;
Located within the implementation code files within &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Integration tests in test ==&lt;br /&gt;
Separated from implementation code to the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
== Code coverage measurement ==&lt;br /&gt;
=== Using tarpaulin ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo tarpaulin --features &amp;quot;test-utils&amp;quot; --out Html&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurement takes app. 22 minutes.&lt;br /&gt;
&lt;br /&gt;
=== Using llvm-cov ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo llvm-cov --features &amp;quot;test-utils&amp;quot; --html &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurements takes app. 8 minutes.&lt;br /&gt;
=== Comparing the results from llvm-cov and tarpaulin ===&lt;br /&gt;
Based on commit &amp;lt;code&amp;gt;8f557814f679c30ebd70e181f2a3f996ce0e295c&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Module Summary Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! Subsystem / Module !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                        &lt;br /&gt;
    |- style=&amp;quot;font-weight:bold; background-color:#f0f0f0;&amp;quot;                                                                                                                                                 &lt;br /&gt;
    | '''TOTAL (All Application Source Files)''' || 71.53% (11216/15681) || 54.48% (4929/9047) || +17.04%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/&amp;lt;/code&amp;gt; || 0.00% (0/597) || 0.00% (0/457) || +0.00%                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/&amp;lt;/code&amp;gt; || 69.73% (1283/1840) || 52.72% (631/1197) || +17.01%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/&amp;lt;/code&amp;gt; || 46.32% (44/95) || 33.80% (24/71) || +12.51%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/&amp;lt;/code&amp;gt; || 79.06% (721/912) || 58.80% (244/415) || +20.26%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/&amp;lt;/code&amp;gt; || 84.06% (944/1123) || 59.48% (411/691) || +24.58%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/&amp;lt;/code&amp;gt; || 88.11% (467/530) || 60.33% (181/300) || +27.78%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/&amp;lt;/code&amp;gt; || 58.82% (610/1037) || 55.57% (334/601) || +3.25%                                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/&amp;lt;/code&amp;gt; || 77.46% (701/905) || 47.50% (304/640) || +29.96%                                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/&amp;lt;/code&amp;gt; || 60.22% (448/744) || 47.58% (216/454) || +12.64%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/&amp;lt;/code&amp;gt; || 75.70% (1748/2309) || 54.76% (656/1198) || +20.95%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/simulator/&amp;lt;/code&amp;gt; || 3.07% (10/326) || 2.05% (4/195) || +1.02%                                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/test_server/&amp;lt;/code&amp;gt; || 67.93% (822/1210) || 59.67% (429/719) || +8.27%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/&amp;lt;/code&amp;gt; || 88.45% (1019/1152) || 67.96% (437/643) || +20.49%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/&amp;lt;/code&amp;gt; || 80.91% (1187/1467) || 75.10% (564/751) || +5.81%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/&amp;lt;/code&amp;gt; || 86.10% (700/813) || 71.18% (331/465) || +14.92%                                                                                                                           &lt;br /&gt;
    |}  &lt;br /&gt;
&lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Detailed File Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! File Path !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_client.rs&amp;lt;/code&amp;gt; || 0.00% (0/117) || 0.00% (0/98) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_commissioning.rs&amp;lt;/code&amp;gt; || 0.00% (0/426) || 0.00% (0/315) || +0.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_test_server.rs&amp;lt;/code&amp;gt; || 0.00% (0/54) || 0.00% (0/44) || +0.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/database_interface_feed_trait.rs&amp;lt;/code&amp;gt; || 77.86% (102/131) || 58.06% (54/93) || +19.80%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/pingable.rs&amp;lt;/code&amp;gt; || 44.44% (4/9) || 33.33% (2/6) || +11.11%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface.rs&amp;lt;/code&amp;gt; || 65.79% (250/380) || 48.57% (119/245) || +17.22%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_balling.rs&amp;lt;/code&amp;gt; || 69.31% (140/202) || 53.91% (69/128) || +15.40%                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_data.rs&amp;lt;/code&amp;gt; || 60.76% (48/79) || 59.62% (31/52) || +1.14%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_feed.rs&amp;lt;/code&amp;gt; || 63.70% (186/292) || 45.00% (90/200) || +18.70%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_set_vals.rs&amp;lt;/code&amp;gt; || 66.12% (80/121) || 37.97% (30/79) || +28.15%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats.rs&amp;lt;/code&amp;gt; || 77.65% (139/179) || 67.59% (73/108) || +10.06%                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats_data_transfer.rs&amp;lt;/code&amp;gt; || 100.00% (7/7) || 100.00% (2/2) || +0.00%                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_midnight.rs&amp;lt;/code&amp;gt; || 100.00% (8/8) || 100.00% (4/4) || +0.00%                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_refill.rs&amp;lt;/code&amp;gt; || 66.94% (83/124) || 57.14% (48/84) || +9.80%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_schedule.rs&amp;lt;/code&amp;gt; || 85.54% (142/166) || 69.16% (74/107) || +16.38%                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_test_utils.rs&amp;lt;/code&amp;gt; || 100.00% (10/10) || 100.00% (5/5) || +0.00%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 63.64% (84/132) || 35.71% (30/84) || +27.92%                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/ipc_message.rs&amp;lt;/code&amp;gt; || 0.00% (0/51) || 0.00% (0/41) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/messaging.rs&amp;lt;/code&amp;gt; || 100.00% (44/44) || 80.00% (24/30) || +20.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed.rs&amp;lt;/code&amp;gt; || 86.40% (305/353) || 62.65% (104/166) || +23.75%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_profile.rs&amp;lt;/code&amp;gt; || 100.00% (100/100) || 100.00% (4/4) || +0.00%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_schedule_entry.rs&amp;lt;/code&amp;gt; || 97.62% (82/84) || 100.00% (10/10) || -2.38%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection.rs&amp;lt;/code&amp;gt; || 62.43% (113/181) || 56.64% (64/113) || +5.79%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_actuator_state.rs&amp;lt;/code&amp;gt; || 61.11% (11/18) || 54.55% (6/11) || +6.57%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_signals.rs&amp;lt;/code&amp;gt; || 62.50% (100/160) || 48.08% (50/104) || +14.42%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/config_app.rs&amp;lt;/code&amp;gt; || 82.35% (98/119) || 57.14% (44/77) || +25.21%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/execution_config.rs&amp;lt;/code&amp;gt; || 86.30% (63/73) || 72.34% (34/47) || +13.96%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/pid_file.rs&amp;lt;/code&amp;gt; || 96.67% (58/60) || 77.14% (27/35) || +19.52%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/run.rs&amp;lt;/code&amp;gt; || 83.40% (725/869) || 58.74% (306/521) || +24.66%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling.rs&amp;lt;/code&amp;gt; || 88.08% (340/386) || 61.08% (135/221) || +27.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_profile.rs&amp;lt;/code&amp;gt; || 100.00% (71/71) || 100.00% (18/18) || +0.00%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_set_val.rs&amp;lt;/code&amp;gt; || 77.97% (46/59) || 44.44% (24/54) || +33.53%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_dht.rs&amp;lt;/code&amp;gt; || 74.07% (40/54) || 64.71% (22/34) || +9.36%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_food_injection.rs&amp;lt;/code&amp;gt; || 83.33% (20/24) || 75.00% (12/16) || +8.33%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_i2c_interface.rs&amp;lt;/code&amp;gt; || 32.50% (13/40) || 28.00% (7/25) || +4.50%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_peristaltic_pump.rs&amp;lt;/code&amp;gt; || 85.00% (17/20) || 72.73% (8/11) || +12.27%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_relay_manager.rs&amp;lt;/code&amp;gt; || 82.50% (33/40) || 75.86% (22/29) || +6.64%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_sql_interface.rs&amp;lt;/code&amp;gt; || 66.21% (241/364) || 63.81% (134/210) || +2.40%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_water_injection.rs&amp;lt;/code&amp;gt; || 82.61% (19/23) || 75.00% (12/16) || +7.61%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/process_privileges.rs&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder.rs&amp;lt;/code&amp;gt; || 84.77% (590/696) || 54.12% (269/497) || +30.65%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_channels.rs&amp;lt;/code&amp;gt; || 58.82% (10/17) || 45.45% (5/11) || +13.37%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_data_frame.rs&amp;lt;/code&amp;gt; || 52.66% (101/192) || 22.31% (27/121) || +30.35%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/actuator_state.rs&amp;lt;/code&amp;gt; || 74.07% (20/27) || 75.00% (15/20) || -0.93%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/aquarium_device.rs&amp;lt;/code&amp;gt; || 66.67% (14/21) || 68.75% (11/16) || -2.08%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager.rs&amp;lt;/code&amp;gt; || 58.46% (394/674) || 45.43% (179/394) || +13.03%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager_channels.rs&amp;lt;/code&amp;gt; || 90.91% (20/22) || 45.83% (11/24) || +45.08%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific.rs&amp;lt;/code&amp;gt; || 74.77% (329/440) || 55.42% (92/166) || +19.35%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht.rs&amp;lt;/code&amp;gt; || 75.00% (105/140) || 53.64% (59/110) || +21.36%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20.rs&amp;lt;/code&amp;gt; || 74.55% (123/165) || 51.09% (47/92) || +23.46%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface.rs&amp;lt;/code&amp;gt; || 76.54% (362/473) || 57.51% (153/266) || +19.03%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager.rs&amp;lt;/code&amp;gt; || 76.10% (761/1000) || 54.34% (282/519) || +21.76%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager_channels.rs&amp;lt;/code&amp;gt; || 84.62% (33/39) || 75.00% (9/12) || +9.62%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating.rs&amp;lt;/code&amp;gt; || 90.96% (332/365) || 68.60% (148/216) || +22.36%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_actuation.rs&amp;lt;/code&amp;gt; || 84.47% (87/103) || 67.24% (39/58) || +17.23%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_set_vals.rs&amp;lt;/code&amp;gt; || 87.50% (35/40) || 64.29% (18/28) || +23.21%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_stats_entry.rs&amp;lt;/code&amp;gt; || 94.67% (142/150) || 82.22% (74/90) || +12.44%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (23/23) || +0.00%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile_config.rs&amp;lt;/code&amp;gt; || 70.83% (17/24) || 50.00% (7/14) || +20.83%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/temperature_gradient.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (32/32) || +0.00%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/thermal_control_status.rs&amp;lt;/code&amp;gt; || 81.25% (26/32) || 58.82% (10/17) || +22.43%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation.rs&amp;lt;/code&amp;gt; || 87.67% (192/219) || 61.16% (74/121) || +26.51%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 58.82% (30/51) || 58.82% (20/34) || +0.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/config.rs&amp;lt;/code&amp;gt; || 76.99% (830/1078) || 72.84% (437/600) || +4.15%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/spin_sleeper.rs&amp;lt;/code&amp;gt; || 79.17% (19/24) || 69.23% (9/13) || +9.94%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/thread_builder.rs&amp;lt;/code&amp;gt; || 92.59% (25/27) || 78.57% (11/14) || +14.02%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill.rs&amp;lt;/code&amp;gt; || 86.81% (447/515) || 69.45% (216/311) || +17.36%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch.rs&amp;lt;/code&amp;gt; || 84.14% (191/227) || 74.47% (105/141) || +9.67%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch_signals.rs&amp;lt;/code&amp;gt; || 90.36% (75/83) || 86.96% (40/46) || +3.40%                                                                                                    &lt;br /&gt;
    |}&lt;br /&gt;
&lt;br /&gt;
== System tests using the simulator ==&lt;br /&gt;
Running the application in simulator mode.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=560</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=560"/>
		<updated>2026-07-26T06:41:46Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Requirements */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Requirements =&lt;br /&gt;
&lt;br /&gt;
The requirement are structured in two levels:&lt;br /&gt;
* system requirements&lt;br /&gt;
* software requirements&lt;br /&gt;
&lt;br /&gt;
The linkage of the software requirements to the implementation and related test cases has been reverse engineered using the following two prompts for each function group:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Check all software requirements of thermal.md if you can find the matching implementations and tests.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Now do the reverse: Go through the implementation and test cases in src/sensors and the integration tests in tests/sensors and label every test case and at least every function linking to a matching software requirement from sensors.md.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;br /&gt;
&lt;br /&gt;
= Test strategy =&lt;br /&gt;
== Unit tests ==&lt;br /&gt;
Located within the implementation code files within &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Integration tests in test ==&lt;br /&gt;
Separated from implementation code to the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
== Code coverage measurement ==&lt;br /&gt;
=== Using tarpaulin ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo tarpaulin --features &amp;quot;test-utils&amp;quot; --out Html&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurement takes app. 22 minutes.&lt;br /&gt;
&lt;br /&gt;
=== Using llvm-cov ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo llvm-cov --features &amp;quot;test-utils&amp;quot; --html &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurements takes app. 8 minutes.&lt;br /&gt;
=== Comparing the results from llvm-cov and tarpaulin ===&lt;br /&gt;
Based on commit &amp;lt;code&amp;gt;8f557814f679c30ebd70e181f2a3f996ce0e295c&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Module Summary Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! Subsystem / Module !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                        &lt;br /&gt;
    |- style=&amp;quot;font-weight:bold; background-color:#f0f0f0;&amp;quot;                                                                                                                                                 &lt;br /&gt;
    | '''TOTAL (All Application Source Files)''' || 71.53% (11216/15681) || 54.48% (4929/9047) || +17.04%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/&amp;lt;/code&amp;gt; || 0.00% (0/597) || 0.00% (0/457) || +0.00%                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/&amp;lt;/code&amp;gt; || 69.73% (1283/1840) || 52.72% (631/1197) || +17.01%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/&amp;lt;/code&amp;gt; || 46.32% (44/95) || 33.80% (24/71) || +12.51%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/&amp;lt;/code&amp;gt; || 79.06% (721/912) || 58.80% (244/415) || +20.26%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/&amp;lt;/code&amp;gt; || 84.06% (944/1123) || 59.48% (411/691) || +24.58%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/&amp;lt;/code&amp;gt; || 88.11% (467/530) || 60.33% (181/300) || +27.78%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/&amp;lt;/code&amp;gt; || 58.82% (610/1037) || 55.57% (334/601) || +3.25%                                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/&amp;lt;/code&amp;gt; || 77.46% (701/905) || 47.50% (304/640) || +29.96%                                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/&amp;lt;/code&amp;gt; || 60.22% (448/744) || 47.58% (216/454) || +12.64%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/&amp;lt;/code&amp;gt; || 75.70% (1748/2309) || 54.76% (656/1198) || +20.95%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/simulator/&amp;lt;/code&amp;gt; || 3.07% (10/326) || 2.05% (4/195) || +1.02%                                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/test_server/&amp;lt;/code&amp;gt; || 67.93% (822/1210) || 59.67% (429/719) || +8.27%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/&amp;lt;/code&amp;gt; || 88.45% (1019/1152) || 67.96% (437/643) || +20.49%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/&amp;lt;/code&amp;gt; || 80.91% (1187/1467) || 75.10% (564/751) || +5.81%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/&amp;lt;/code&amp;gt; || 86.10% (700/813) || 71.18% (331/465) || +14.92%                                                                                                                           &lt;br /&gt;
    |}  &lt;br /&gt;
&lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Detailed File Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! File Path !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_client.rs&amp;lt;/code&amp;gt; || 0.00% (0/117) || 0.00% (0/98) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_commissioning.rs&amp;lt;/code&amp;gt; || 0.00% (0/426) || 0.00% (0/315) || +0.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_test_server.rs&amp;lt;/code&amp;gt; || 0.00% (0/54) || 0.00% (0/44) || +0.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/database_interface_feed_trait.rs&amp;lt;/code&amp;gt; || 77.86% (102/131) || 58.06% (54/93) || +19.80%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/pingable.rs&amp;lt;/code&amp;gt; || 44.44% (4/9) || 33.33% (2/6) || +11.11%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface.rs&amp;lt;/code&amp;gt; || 65.79% (250/380) || 48.57% (119/245) || +17.22%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_balling.rs&amp;lt;/code&amp;gt; || 69.31% (140/202) || 53.91% (69/128) || +15.40%                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_data.rs&amp;lt;/code&amp;gt; || 60.76% (48/79) || 59.62% (31/52) || +1.14%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_feed.rs&amp;lt;/code&amp;gt; || 63.70% (186/292) || 45.00% (90/200) || +18.70%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_set_vals.rs&amp;lt;/code&amp;gt; || 66.12% (80/121) || 37.97% (30/79) || +28.15%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats.rs&amp;lt;/code&amp;gt; || 77.65% (139/179) || 67.59% (73/108) || +10.06%                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats_data_transfer.rs&amp;lt;/code&amp;gt; || 100.00% (7/7) || 100.00% (2/2) || +0.00%                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_midnight.rs&amp;lt;/code&amp;gt; || 100.00% (8/8) || 100.00% (4/4) || +0.00%                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_refill.rs&amp;lt;/code&amp;gt; || 66.94% (83/124) || 57.14% (48/84) || +9.80%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_schedule.rs&amp;lt;/code&amp;gt; || 85.54% (142/166) || 69.16% (74/107) || +16.38%                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_test_utils.rs&amp;lt;/code&amp;gt; || 100.00% (10/10) || 100.00% (5/5) || +0.00%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 63.64% (84/132) || 35.71% (30/84) || +27.92%                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/ipc_message.rs&amp;lt;/code&amp;gt; || 0.00% (0/51) || 0.00% (0/41) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/messaging.rs&amp;lt;/code&amp;gt; || 100.00% (44/44) || 80.00% (24/30) || +20.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed.rs&amp;lt;/code&amp;gt; || 86.40% (305/353) || 62.65% (104/166) || +23.75%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_profile.rs&amp;lt;/code&amp;gt; || 100.00% (100/100) || 100.00% (4/4) || +0.00%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_schedule_entry.rs&amp;lt;/code&amp;gt; || 97.62% (82/84) || 100.00% (10/10) || -2.38%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection.rs&amp;lt;/code&amp;gt; || 62.43% (113/181) || 56.64% (64/113) || +5.79%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_actuator_state.rs&amp;lt;/code&amp;gt; || 61.11% (11/18) || 54.55% (6/11) || +6.57%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_signals.rs&amp;lt;/code&amp;gt; || 62.50% (100/160) || 48.08% (50/104) || +14.42%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/config_app.rs&amp;lt;/code&amp;gt; || 82.35% (98/119) || 57.14% (44/77) || +25.21%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/execution_config.rs&amp;lt;/code&amp;gt; || 86.30% (63/73) || 72.34% (34/47) || +13.96%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/pid_file.rs&amp;lt;/code&amp;gt; || 96.67% (58/60) || 77.14% (27/35) || +19.52%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/run.rs&amp;lt;/code&amp;gt; || 83.40% (725/869) || 58.74% (306/521) || +24.66%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling.rs&amp;lt;/code&amp;gt; || 88.08% (340/386) || 61.08% (135/221) || +27.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_profile.rs&amp;lt;/code&amp;gt; || 100.00% (71/71) || 100.00% (18/18) || +0.00%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_set_val.rs&amp;lt;/code&amp;gt; || 77.97% (46/59) || 44.44% (24/54) || +33.53%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_dht.rs&amp;lt;/code&amp;gt; || 74.07% (40/54) || 64.71% (22/34) || +9.36%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_food_injection.rs&amp;lt;/code&amp;gt; || 83.33% (20/24) || 75.00% (12/16) || +8.33%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_i2c_interface.rs&amp;lt;/code&amp;gt; || 32.50% (13/40) || 28.00% (7/25) || +4.50%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_peristaltic_pump.rs&amp;lt;/code&amp;gt; || 85.00% (17/20) || 72.73% (8/11) || +12.27%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_relay_manager.rs&amp;lt;/code&amp;gt; || 82.50% (33/40) || 75.86% (22/29) || +6.64%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_sql_interface.rs&amp;lt;/code&amp;gt; || 66.21% (241/364) || 63.81% (134/210) || +2.40%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_water_injection.rs&amp;lt;/code&amp;gt; || 82.61% (19/23) || 75.00% (12/16) || +7.61%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/process_privileges.rs&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder.rs&amp;lt;/code&amp;gt; || 84.77% (590/696) || 54.12% (269/497) || +30.65%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_channels.rs&amp;lt;/code&amp;gt; || 58.82% (10/17) || 45.45% (5/11) || +13.37%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_data_frame.rs&amp;lt;/code&amp;gt; || 52.66% (101/192) || 22.31% (27/121) || +30.35%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/actuator_state.rs&amp;lt;/code&amp;gt; || 74.07% (20/27) || 75.00% (15/20) || -0.93%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/aquarium_device.rs&amp;lt;/code&amp;gt; || 66.67% (14/21) || 68.75% (11/16) || -2.08%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager.rs&amp;lt;/code&amp;gt; || 58.46% (394/674) || 45.43% (179/394) || +13.03%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager_channels.rs&amp;lt;/code&amp;gt; || 90.91% (20/22) || 45.83% (11/24) || +45.08%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific.rs&amp;lt;/code&amp;gt; || 74.77% (329/440) || 55.42% (92/166) || +19.35%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht.rs&amp;lt;/code&amp;gt; || 75.00% (105/140) || 53.64% (59/110) || +21.36%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20.rs&amp;lt;/code&amp;gt; || 74.55% (123/165) || 51.09% (47/92) || +23.46%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface.rs&amp;lt;/code&amp;gt; || 76.54% (362/473) || 57.51% (153/266) || +19.03%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager.rs&amp;lt;/code&amp;gt; || 76.10% (761/1000) || 54.34% (282/519) || +21.76%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager_channels.rs&amp;lt;/code&amp;gt; || 84.62% (33/39) || 75.00% (9/12) || +9.62%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating.rs&amp;lt;/code&amp;gt; || 90.96% (332/365) || 68.60% (148/216) || +22.36%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_actuation.rs&amp;lt;/code&amp;gt; || 84.47% (87/103) || 67.24% (39/58) || +17.23%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_set_vals.rs&amp;lt;/code&amp;gt; || 87.50% (35/40) || 64.29% (18/28) || +23.21%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_stats_entry.rs&amp;lt;/code&amp;gt; || 94.67% (142/150) || 82.22% (74/90) || +12.44%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (23/23) || +0.00%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile_config.rs&amp;lt;/code&amp;gt; || 70.83% (17/24) || 50.00% (7/14) || +20.83%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/temperature_gradient.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (32/32) || +0.00%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/thermal_control_status.rs&amp;lt;/code&amp;gt; || 81.25% (26/32) || 58.82% (10/17) || +22.43%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation.rs&amp;lt;/code&amp;gt; || 87.67% (192/219) || 61.16% (74/121) || +26.51%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 58.82% (30/51) || 58.82% (20/34) || +0.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/config.rs&amp;lt;/code&amp;gt; || 76.99% (830/1078) || 72.84% (437/600) || +4.15%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/spin_sleeper.rs&amp;lt;/code&amp;gt; || 79.17% (19/24) || 69.23% (9/13) || +9.94%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/thread_builder.rs&amp;lt;/code&amp;gt; || 92.59% (25/27) || 78.57% (11/14) || +14.02%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill.rs&amp;lt;/code&amp;gt; || 86.81% (447/515) || 69.45% (216/311) || +17.36%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch.rs&amp;lt;/code&amp;gt; || 84.14% (191/227) || 74.47% (105/141) || +9.67%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch_signals.rs&amp;lt;/code&amp;gt; || 90.36% (75/83) || 86.96% (40/46) || +3.40%                                                                                                    &lt;br /&gt;
    |}&lt;br /&gt;
&lt;br /&gt;
== System tests using the simulator ==&lt;br /&gt;
Running the application in simulator mode.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=559</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=559"/>
		<updated>2026-07-26T06:38:59Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Requirements */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Requirements =&lt;br /&gt;
&lt;br /&gt;
The linkage of the software requirements to the implementation and related test cases has been reverse engineered using the following two prompts for each function group:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Check all software requirements of thermal.md if you can find the matching implementations and tests.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Now do the reverse: Go through the implementation and test cases in src/sensors and the integration tests in tests/sensors and label every test case and at least every function linking to a matching software requirement from sensors.md.&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;br /&gt;
&lt;br /&gt;
= Test strategy =&lt;br /&gt;
== Unit tests ==&lt;br /&gt;
Located within the implementation code files within &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Integration tests in test ==&lt;br /&gt;
Separated from implementation code to the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
== Code coverage measurement ==&lt;br /&gt;
=== Using tarpaulin ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo tarpaulin --features &amp;quot;test-utils&amp;quot; --out Html&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurement takes app. 22 minutes.&lt;br /&gt;
&lt;br /&gt;
=== Using llvm-cov ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo llvm-cov --features &amp;quot;test-utils&amp;quot; --html &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurements takes app. 8 minutes.&lt;br /&gt;
=== Comparing the results from llvm-cov and tarpaulin ===&lt;br /&gt;
Based on commit &amp;lt;code&amp;gt;8f557814f679c30ebd70e181f2a3f996ce0e295c&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Module Summary Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! Subsystem / Module !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                        &lt;br /&gt;
    |- style=&amp;quot;font-weight:bold; background-color:#f0f0f0;&amp;quot;                                                                                                                                                 &lt;br /&gt;
    | '''TOTAL (All Application Source Files)''' || 71.53% (11216/15681) || 54.48% (4929/9047) || +17.04%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/&amp;lt;/code&amp;gt; || 0.00% (0/597) || 0.00% (0/457) || +0.00%                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/&amp;lt;/code&amp;gt; || 69.73% (1283/1840) || 52.72% (631/1197) || +17.01%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/&amp;lt;/code&amp;gt; || 46.32% (44/95) || 33.80% (24/71) || +12.51%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/&amp;lt;/code&amp;gt; || 79.06% (721/912) || 58.80% (244/415) || +20.26%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/&amp;lt;/code&amp;gt; || 84.06% (944/1123) || 59.48% (411/691) || +24.58%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/&amp;lt;/code&amp;gt; || 88.11% (467/530) || 60.33% (181/300) || +27.78%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/&amp;lt;/code&amp;gt; || 58.82% (610/1037) || 55.57% (334/601) || +3.25%                                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/&amp;lt;/code&amp;gt; || 77.46% (701/905) || 47.50% (304/640) || +29.96%                                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/&amp;lt;/code&amp;gt; || 60.22% (448/744) || 47.58% (216/454) || +12.64%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/&amp;lt;/code&amp;gt; || 75.70% (1748/2309) || 54.76% (656/1198) || +20.95%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/simulator/&amp;lt;/code&amp;gt; || 3.07% (10/326) || 2.05% (4/195) || +1.02%                                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/test_server/&amp;lt;/code&amp;gt; || 67.93% (822/1210) || 59.67% (429/719) || +8.27%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/&amp;lt;/code&amp;gt; || 88.45% (1019/1152) || 67.96% (437/643) || +20.49%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/&amp;lt;/code&amp;gt; || 80.91% (1187/1467) || 75.10% (564/751) || +5.81%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/&amp;lt;/code&amp;gt; || 86.10% (700/813) || 71.18% (331/465) || +14.92%                                                                                                                           &lt;br /&gt;
    |}  &lt;br /&gt;
&lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Detailed File Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! File Path !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_client.rs&amp;lt;/code&amp;gt; || 0.00% (0/117) || 0.00% (0/98) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_commissioning.rs&amp;lt;/code&amp;gt; || 0.00% (0/426) || 0.00% (0/315) || +0.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_test_server.rs&amp;lt;/code&amp;gt; || 0.00% (0/54) || 0.00% (0/44) || +0.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/database_interface_feed_trait.rs&amp;lt;/code&amp;gt; || 77.86% (102/131) || 58.06% (54/93) || +19.80%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/pingable.rs&amp;lt;/code&amp;gt; || 44.44% (4/9) || 33.33% (2/6) || +11.11%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface.rs&amp;lt;/code&amp;gt; || 65.79% (250/380) || 48.57% (119/245) || +17.22%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_balling.rs&amp;lt;/code&amp;gt; || 69.31% (140/202) || 53.91% (69/128) || +15.40%                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_data.rs&amp;lt;/code&amp;gt; || 60.76% (48/79) || 59.62% (31/52) || +1.14%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_feed.rs&amp;lt;/code&amp;gt; || 63.70% (186/292) || 45.00% (90/200) || +18.70%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_set_vals.rs&amp;lt;/code&amp;gt; || 66.12% (80/121) || 37.97% (30/79) || +28.15%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats.rs&amp;lt;/code&amp;gt; || 77.65% (139/179) || 67.59% (73/108) || +10.06%                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats_data_transfer.rs&amp;lt;/code&amp;gt; || 100.00% (7/7) || 100.00% (2/2) || +0.00%                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_midnight.rs&amp;lt;/code&amp;gt; || 100.00% (8/8) || 100.00% (4/4) || +0.00%                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_refill.rs&amp;lt;/code&amp;gt; || 66.94% (83/124) || 57.14% (48/84) || +9.80%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_schedule.rs&amp;lt;/code&amp;gt; || 85.54% (142/166) || 69.16% (74/107) || +16.38%                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_test_utils.rs&amp;lt;/code&amp;gt; || 100.00% (10/10) || 100.00% (5/5) || +0.00%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 63.64% (84/132) || 35.71% (30/84) || +27.92%                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/ipc_message.rs&amp;lt;/code&amp;gt; || 0.00% (0/51) || 0.00% (0/41) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/messaging.rs&amp;lt;/code&amp;gt; || 100.00% (44/44) || 80.00% (24/30) || +20.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed.rs&amp;lt;/code&amp;gt; || 86.40% (305/353) || 62.65% (104/166) || +23.75%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_profile.rs&amp;lt;/code&amp;gt; || 100.00% (100/100) || 100.00% (4/4) || +0.00%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_schedule_entry.rs&amp;lt;/code&amp;gt; || 97.62% (82/84) || 100.00% (10/10) || -2.38%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection.rs&amp;lt;/code&amp;gt; || 62.43% (113/181) || 56.64% (64/113) || +5.79%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_actuator_state.rs&amp;lt;/code&amp;gt; || 61.11% (11/18) || 54.55% (6/11) || +6.57%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_signals.rs&amp;lt;/code&amp;gt; || 62.50% (100/160) || 48.08% (50/104) || +14.42%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/config_app.rs&amp;lt;/code&amp;gt; || 82.35% (98/119) || 57.14% (44/77) || +25.21%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/execution_config.rs&amp;lt;/code&amp;gt; || 86.30% (63/73) || 72.34% (34/47) || +13.96%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/pid_file.rs&amp;lt;/code&amp;gt; || 96.67% (58/60) || 77.14% (27/35) || +19.52%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/run.rs&amp;lt;/code&amp;gt; || 83.40% (725/869) || 58.74% (306/521) || +24.66%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling.rs&amp;lt;/code&amp;gt; || 88.08% (340/386) || 61.08% (135/221) || +27.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_profile.rs&amp;lt;/code&amp;gt; || 100.00% (71/71) || 100.00% (18/18) || +0.00%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_set_val.rs&amp;lt;/code&amp;gt; || 77.97% (46/59) || 44.44% (24/54) || +33.53%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_dht.rs&amp;lt;/code&amp;gt; || 74.07% (40/54) || 64.71% (22/34) || +9.36%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_food_injection.rs&amp;lt;/code&amp;gt; || 83.33% (20/24) || 75.00% (12/16) || +8.33%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_i2c_interface.rs&amp;lt;/code&amp;gt; || 32.50% (13/40) || 28.00% (7/25) || +4.50%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_peristaltic_pump.rs&amp;lt;/code&amp;gt; || 85.00% (17/20) || 72.73% (8/11) || +12.27%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_relay_manager.rs&amp;lt;/code&amp;gt; || 82.50% (33/40) || 75.86% (22/29) || +6.64%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_sql_interface.rs&amp;lt;/code&amp;gt; || 66.21% (241/364) || 63.81% (134/210) || +2.40%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_water_injection.rs&amp;lt;/code&amp;gt; || 82.61% (19/23) || 75.00% (12/16) || +7.61%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/process_privileges.rs&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder.rs&amp;lt;/code&amp;gt; || 84.77% (590/696) || 54.12% (269/497) || +30.65%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_channels.rs&amp;lt;/code&amp;gt; || 58.82% (10/17) || 45.45% (5/11) || +13.37%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_data_frame.rs&amp;lt;/code&amp;gt; || 52.66% (101/192) || 22.31% (27/121) || +30.35%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/actuator_state.rs&amp;lt;/code&amp;gt; || 74.07% (20/27) || 75.00% (15/20) || -0.93%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/aquarium_device.rs&amp;lt;/code&amp;gt; || 66.67% (14/21) || 68.75% (11/16) || -2.08%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager.rs&amp;lt;/code&amp;gt; || 58.46% (394/674) || 45.43% (179/394) || +13.03%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager_channels.rs&amp;lt;/code&amp;gt; || 90.91% (20/22) || 45.83% (11/24) || +45.08%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific.rs&amp;lt;/code&amp;gt; || 74.77% (329/440) || 55.42% (92/166) || +19.35%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht.rs&amp;lt;/code&amp;gt; || 75.00% (105/140) || 53.64% (59/110) || +21.36%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20.rs&amp;lt;/code&amp;gt; || 74.55% (123/165) || 51.09% (47/92) || +23.46%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface.rs&amp;lt;/code&amp;gt; || 76.54% (362/473) || 57.51% (153/266) || +19.03%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager.rs&amp;lt;/code&amp;gt; || 76.10% (761/1000) || 54.34% (282/519) || +21.76%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager_channels.rs&amp;lt;/code&amp;gt; || 84.62% (33/39) || 75.00% (9/12) || +9.62%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating.rs&amp;lt;/code&amp;gt; || 90.96% (332/365) || 68.60% (148/216) || +22.36%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_actuation.rs&amp;lt;/code&amp;gt; || 84.47% (87/103) || 67.24% (39/58) || +17.23%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_set_vals.rs&amp;lt;/code&amp;gt; || 87.50% (35/40) || 64.29% (18/28) || +23.21%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_stats_entry.rs&amp;lt;/code&amp;gt; || 94.67% (142/150) || 82.22% (74/90) || +12.44%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (23/23) || +0.00%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile_config.rs&amp;lt;/code&amp;gt; || 70.83% (17/24) || 50.00% (7/14) || +20.83%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/temperature_gradient.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (32/32) || +0.00%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/thermal_control_status.rs&amp;lt;/code&amp;gt; || 81.25% (26/32) || 58.82% (10/17) || +22.43%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation.rs&amp;lt;/code&amp;gt; || 87.67% (192/219) || 61.16% (74/121) || +26.51%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 58.82% (30/51) || 58.82% (20/34) || +0.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/config.rs&amp;lt;/code&amp;gt; || 76.99% (830/1078) || 72.84% (437/600) || +4.15%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/spin_sleeper.rs&amp;lt;/code&amp;gt; || 79.17% (19/24) || 69.23% (9/13) || +9.94%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/thread_builder.rs&amp;lt;/code&amp;gt; || 92.59% (25/27) || 78.57% (11/14) || +14.02%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill.rs&amp;lt;/code&amp;gt; || 86.81% (447/515) || 69.45% (216/311) || +17.36%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch.rs&amp;lt;/code&amp;gt; || 84.14% (191/227) || 74.47% (105/141) || +9.67%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch_signals.rs&amp;lt;/code&amp;gt; || 90.36% (75/83) || 86.96% (40/46) || +3.40%                                                                                                    &lt;br /&gt;
    |}&lt;br /&gt;
&lt;br /&gt;
== System tests using the simulator ==&lt;br /&gt;
Running the application in simulator mode.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=558</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=558"/>
		<updated>2026-07-26T06:34:14Z</updated>

		<summary type="html">&lt;p&gt;Uwe: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Requirements =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;quote&amp;gt;Now do the reverse: Go through the implementation and test cases in src/sensors and the integration tests in tests/sensors and label every test case and at least every function linking to a matching software requirement from sensors.md.&amp;lt;/quote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;br /&gt;
&lt;br /&gt;
= Test strategy =&lt;br /&gt;
== Unit tests ==&lt;br /&gt;
Located within the implementation code files within &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Integration tests in test ==&lt;br /&gt;
Separated from implementation code to the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
== Code coverage measurement ==&lt;br /&gt;
=== Using tarpaulin ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo tarpaulin --features &amp;quot;test-utils&amp;quot; --out Html&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurement takes app. 22 minutes.&lt;br /&gt;
&lt;br /&gt;
=== Using llvm-cov ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo llvm-cov --features &amp;quot;test-utils&amp;quot; --html &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurements takes app. 8 minutes.&lt;br /&gt;
=== Comparing the results from llvm-cov and tarpaulin ===&lt;br /&gt;
Based on commit &amp;lt;code&amp;gt;8f557814f679c30ebd70e181f2a3f996ce0e295c&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Module Summary Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! Subsystem / Module !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                        &lt;br /&gt;
    |- style=&amp;quot;font-weight:bold; background-color:#f0f0f0;&amp;quot;                                                                                                                                                 &lt;br /&gt;
    | '''TOTAL (All Application Source Files)''' || 71.53% (11216/15681) || 54.48% (4929/9047) || +17.04%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/&amp;lt;/code&amp;gt; || 0.00% (0/597) || 0.00% (0/457) || +0.00%                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/&amp;lt;/code&amp;gt; || 69.73% (1283/1840) || 52.72% (631/1197) || +17.01%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/&amp;lt;/code&amp;gt; || 46.32% (44/95) || 33.80% (24/71) || +12.51%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/&amp;lt;/code&amp;gt; || 79.06% (721/912) || 58.80% (244/415) || +20.26%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/&amp;lt;/code&amp;gt; || 84.06% (944/1123) || 59.48% (411/691) || +24.58%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/&amp;lt;/code&amp;gt; || 88.11% (467/530) || 60.33% (181/300) || +27.78%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/&amp;lt;/code&amp;gt; || 58.82% (610/1037) || 55.57% (334/601) || +3.25%                                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/&amp;lt;/code&amp;gt; || 77.46% (701/905) || 47.50% (304/640) || +29.96%                                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/&amp;lt;/code&amp;gt; || 60.22% (448/744) || 47.58% (216/454) || +12.64%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/&amp;lt;/code&amp;gt; || 75.70% (1748/2309) || 54.76% (656/1198) || +20.95%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/simulator/&amp;lt;/code&amp;gt; || 3.07% (10/326) || 2.05% (4/195) || +1.02%                                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/test_server/&amp;lt;/code&amp;gt; || 67.93% (822/1210) || 59.67% (429/719) || +8.27%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/&amp;lt;/code&amp;gt; || 88.45% (1019/1152) || 67.96% (437/643) || +20.49%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/&amp;lt;/code&amp;gt; || 80.91% (1187/1467) || 75.10% (564/751) || +5.81%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/&amp;lt;/code&amp;gt; || 86.10% (700/813) || 71.18% (331/465) || +14.92%                                                                                                                           &lt;br /&gt;
    |}  &lt;br /&gt;
&lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Detailed File Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! File Path !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_client.rs&amp;lt;/code&amp;gt; || 0.00% (0/117) || 0.00% (0/98) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_commissioning.rs&amp;lt;/code&amp;gt; || 0.00% (0/426) || 0.00% (0/315) || +0.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_test_server.rs&amp;lt;/code&amp;gt; || 0.00% (0/54) || 0.00% (0/44) || +0.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/database_interface_feed_trait.rs&amp;lt;/code&amp;gt; || 77.86% (102/131) || 58.06% (54/93) || +19.80%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/pingable.rs&amp;lt;/code&amp;gt; || 44.44% (4/9) || 33.33% (2/6) || +11.11%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface.rs&amp;lt;/code&amp;gt; || 65.79% (250/380) || 48.57% (119/245) || +17.22%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_balling.rs&amp;lt;/code&amp;gt; || 69.31% (140/202) || 53.91% (69/128) || +15.40%                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_data.rs&amp;lt;/code&amp;gt; || 60.76% (48/79) || 59.62% (31/52) || +1.14%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_feed.rs&amp;lt;/code&amp;gt; || 63.70% (186/292) || 45.00% (90/200) || +18.70%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_set_vals.rs&amp;lt;/code&amp;gt; || 66.12% (80/121) || 37.97% (30/79) || +28.15%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats.rs&amp;lt;/code&amp;gt; || 77.65% (139/179) || 67.59% (73/108) || +10.06%                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats_data_transfer.rs&amp;lt;/code&amp;gt; || 100.00% (7/7) || 100.00% (2/2) || +0.00%                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_midnight.rs&amp;lt;/code&amp;gt; || 100.00% (8/8) || 100.00% (4/4) || +0.00%                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_refill.rs&amp;lt;/code&amp;gt; || 66.94% (83/124) || 57.14% (48/84) || +9.80%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_schedule.rs&amp;lt;/code&amp;gt; || 85.54% (142/166) || 69.16% (74/107) || +16.38%                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_test_utils.rs&amp;lt;/code&amp;gt; || 100.00% (10/10) || 100.00% (5/5) || +0.00%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 63.64% (84/132) || 35.71% (30/84) || +27.92%                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/ipc_message.rs&amp;lt;/code&amp;gt; || 0.00% (0/51) || 0.00% (0/41) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/messaging.rs&amp;lt;/code&amp;gt; || 100.00% (44/44) || 80.00% (24/30) || +20.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed.rs&amp;lt;/code&amp;gt; || 86.40% (305/353) || 62.65% (104/166) || +23.75%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_profile.rs&amp;lt;/code&amp;gt; || 100.00% (100/100) || 100.00% (4/4) || +0.00%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_schedule_entry.rs&amp;lt;/code&amp;gt; || 97.62% (82/84) || 100.00% (10/10) || -2.38%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection.rs&amp;lt;/code&amp;gt; || 62.43% (113/181) || 56.64% (64/113) || +5.79%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_actuator_state.rs&amp;lt;/code&amp;gt; || 61.11% (11/18) || 54.55% (6/11) || +6.57%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_signals.rs&amp;lt;/code&amp;gt; || 62.50% (100/160) || 48.08% (50/104) || +14.42%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/config_app.rs&amp;lt;/code&amp;gt; || 82.35% (98/119) || 57.14% (44/77) || +25.21%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/execution_config.rs&amp;lt;/code&amp;gt; || 86.30% (63/73) || 72.34% (34/47) || +13.96%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/pid_file.rs&amp;lt;/code&amp;gt; || 96.67% (58/60) || 77.14% (27/35) || +19.52%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/run.rs&amp;lt;/code&amp;gt; || 83.40% (725/869) || 58.74% (306/521) || +24.66%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling.rs&amp;lt;/code&amp;gt; || 88.08% (340/386) || 61.08% (135/221) || +27.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_profile.rs&amp;lt;/code&amp;gt; || 100.00% (71/71) || 100.00% (18/18) || +0.00%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_set_val.rs&amp;lt;/code&amp;gt; || 77.97% (46/59) || 44.44% (24/54) || +33.53%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_dht.rs&amp;lt;/code&amp;gt; || 74.07% (40/54) || 64.71% (22/34) || +9.36%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_food_injection.rs&amp;lt;/code&amp;gt; || 83.33% (20/24) || 75.00% (12/16) || +8.33%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_i2c_interface.rs&amp;lt;/code&amp;gt; || 32.50% (13/40) || 28.00% (7/25) || +4.50%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_peristaltic_pump.rs&amp;lt;/code&amp;gt; || 85.00% (17/20) || 72.73% (8/11) || +12.27%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_relay_manager.rs&amp;lt;/code&amp;gt; || 82.50% (33/40) || 75.86% (22/29) || +6.64%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_sql_interface.rs&amp;lt;/code&amp;gt; || 66.21% (241/364) || 63.81% (134/210) || +2.40%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_water_injection.rs&amp;lt;/code&amp;gt; || 82.61% (19/23) || 75.00% (12/16) || +7.61%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/process_privileges.rs&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder.rs&amp;lt;/code&amp;gt; || 84.77% (590/696) || 54.12% (269/497) || +30.65%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_channels.rs&amp;lt;/code&amp;gt; || 58.82% (10/17) || 45.45% (5/11) || +13.37%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_data_frame.rs&amp;lt;/code&amp;gt; || 52.66% (101/192) || 22.31% (27/121) || +30.35%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/actuator_state.rs&amp;lt;/code&amp;gt; || 74.07% (20/27) || 75.00% (15/20) || -0.93%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/aquarium_device.rs&amp;lt;/code&amp;gt; || 66.67% (14/21) || 68.75% (11/16) || -2.08%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager.rs&amp;lt;/code&amp;gt; || 58.46% (394/674) || 45.43% (179/394) || +13.03%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager_channels.rs&amp;lt;/code&amp;gt; || 90.91% (20/22) || 45.83% (11/24) || +45.08%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific.rs&amp;lt;/code&amp;gt; || 74.77% (329/440) || 55.42% (92/166) || +19.35%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht.rs&amp;lt;/code&amp;gt; || 75.00% (105/140) || 53.64% (59/110) || +21.36%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20.rs&amp;lt;/code&amp;gt; || 74.55% (123/165) || 51.09% (47/92) || +23.46%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface.rs&amp;lt;/code&amp;gt; || 76.54% (362/473) || 57.51% (153/266) || +19.03%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager.rs&amp;lt;/code&amp;gt; || 76.10% (761/1000) || 54.34% (282/519) || +21.76%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager_channels.rs&amp;lt;/code&amp;gt; || 84.62% (33/39) || 75.00% (9/12) || +9.62%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating.rs&amp;lt;/code&amp;gt; || 90.96% (332/365) || 68.60% (148/216) || +22.36%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_actuation.rs&amp;lt;/code&amp;gt; || 84.47% (87/103) || 67.24% (39/58) || +17.23%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_set_vals.rs&amp;lt;/code&amp;gt; || 87.50% (35/40) || 64.29% (18/28) || +23.21%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_stats_entry.rs&amp;lt;/code&amp;gt; || 94.67% (142/150) || 82.22% (74/90) || +12.44%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (23/23) || +0.00%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile_config.rs&amp;lt;/code&amp;gt; || 70.83% (17/24) || 50.00% (7/14) || +20.83%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/temperature_gradient.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (32/32) || +0.00%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/thermal_control_status.rs&amp;lt;/code&amp;gt; || 81.25% (26/32) || 58.82% (10/17) || +22.43%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation.rs&amp;lt;/code&amp;gt; || 87.67% (192/219) || 61.16% (74/121) || +26.51%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 58.82% (30/51) || 58.82% (20/34) || +0.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/config.rs&amp;lt;/code&amp;gt; || 76.99% (830/1078) || 72.84% (437/600) || +4.15%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/spin_sleeper.rs&amp;lt;/code&amp;gt; || 79.17% (19/24) || 69.23% (9/13) || +9.94%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/thread_builder.rs&amp;lt;/code&amp;gt; || 92.59% (25/27) || 78.57% (11/14) || +14.02%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill.rs&amp;lt;/code&amp;gt; || 86.81% (447/515) || 69.45% (216/311) || +17.36%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch.rs&amp;lt;/code&amp;gt; || 84.14% (191/227) || 74.47% (105/141) || +9.67%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch_signals.rs&amp;lt;/code&amp;gt; || 90.36% (75/83) || 86.96% (40/46) || +3.40%                                                                                                    &lt;br /&gt;
    |}&lt;br /&gt;
&lt;br /&gt;
== System tests using the simulator ==&lt;br /&gt;
Running the application in simulator mode.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=557</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=557"/>
		<updated>2026-07-25T09:53:29Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Comparing the results from llvm-cov and tarpaulin */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;br /&gt;
&lt;br /&gt;
= Test strategy =&lt;br /&gt;
== Unit tests ==&lt;br /&gt;
Located within the implementation code files within &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Integration tests in test ==&lt;br /&gt;
Separated from implementation code to the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
== Code coverage measurement ==&lt;br /&gt;
=== Using tarpaulin ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo tarpaulin --features &amp;quot;test-utils&amp;quot; --out Html&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurement takes app. 22 minutes.&lt;br /&gt;
&lt;br /&gt;
=== Using llvm-cov ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo llvm-cov --features &amp;quot;test-utils&amp;quot; --html &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurements takes app. 8 minutes.&lt;br /&gt;
=== Comparing the results from llvm-cov and tarpaulin ===&lt;br /&gt;
Based on commit &amp;lt;code&amp;gt;8f557814f679c30ebd70e181f2a3f996ce0e295c&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Module Summary Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! Subsystem / Module !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                        &lt;br /&gt;
    |- style=&amp;quot;font-weight:bold; background-color:#f0f0f0;&amp;quot;                                                                                                                                                 &lt;br /&gt;
    | '''TOTAL (All Application Source Files)''' || 71.53% (11216/15681) || 54.48% (4929/9047) || +17.04%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/&amp;lt;/code&amp;gt; || 0.00% (0/597) || 0.00% (0/457) || +0.00%                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/&amp;lt;/code&amp;gt; || 69.73% (1283/1840) || 52.72% (631/1197) || +17.01%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/&amp;lt;/code&amp;gt; || 46.32% (44/95) || 33.80% (24/71) || +12.51%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/&amp;lt;/code&amp;gt; || 79.06% (721/912) || 58.80% (244/415) || +20.26%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/&amp;lt;/code&amp;gt; || 84.06% (944/1123) || 59.48% (411/691) || +24.58%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/&amp;lt;/code&amp;gt; || 88.11% (467/530) || 60.33% (181/300) || +27.78%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/&amp;lt;/code&amp;gt; || 58.82% (610/1037) || 55.57% (334/601) || +3.25%                                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/&amp;lt;/code&amp;gt; || 77.46% (701/905) || 47.50% (304/640) || +29.96%                                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/&amp;lt;/code&amp;gt; || 60.22% (448/744) || 47.58% (216/454) || +12.64%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/&amp;lt;/code&amp;gt; || 75.70% (1748/2309) || 54.76% (656/1198) || +20.95%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/simulator/&amp;lt;/code&amp;gt; || 3.07% (10/326) || 2.05% (4/195) || +1.02%                                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/test_server/&amp;lt;/code&amp;gt; || 67.93% (822/1210) || 59.67% (429/719) || +8.27%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/&amp;lt;/code&amp;gt; || 88.45% (1019/1152) || 67.96% (437/643) || +20.49%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/&amp;lt;/code&amp;gt; || 80.91% (1187/1467) || 75.10% (564/751) || +5.81%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/&amp;lt;/code&amp;gt; || 86.10% (700/813) || 71.18% (331/465) || +14.92%                                                                                                                           &lt;br /&gt;
    |}  &lt;br /&gt;
&lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Detailed File Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! File Path !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_client.rs&amp;lt;/code&amp;gt; || 0.00% (0/117) || 0.00% (0/98) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_commissioning.rs&amp;lt;/code&amp;gt; || 0.00% (0/426) || 0.00% (0/315) || +0.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_test_server.rs&amp;lt;/code&amp;gt; || 0.00% (0/54) || 0.00% (0/44) || +0.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/database_interface_feed_trait.rs&amp;lt;/code&amp;gt; || 77.86% (102/131) || 58.06% (54/93) || +19.80%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/pingable.rs&amp;lt;/code&amp;gt; || 44.44% (4/9) || 33.33% (2/6) || +11.11%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface.rs&amp;lt;/code&amp;gt; || 65.79% (250/380) || 48.57% (119/245) || +17.22%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_balling.rs&amp;lt;/code&amp;gt; || 69.31% (140/202) || 53.91% (69/128) || +15.40%                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_data.rs&amp;lt;/code&amp;gt; || 60.76% (48/79) || 59.62% (31/52) || +1.14%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_feed.rs&amp;lt;/code&amp;gt; || 63.70% (186/292) || 45.00% (90/200) || +18.70%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_set_vals.rs&amp;lt;/code&amp;gt; || 66.12% (80/121) || 37.97% (30/79) || +28.15%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats.rs&amp;lt;/code&amp;gt; || 77.65% (139/179) || 67.59% (73/108) || +10.06%                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats_data_transfer.rs&amp;lt;/code&amp;gt; || 100.00% (7/7) || 100.00% (2/2) || +0.00%                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_midnight.rs&amp;lt;/code&amp;gt; || 100.00% (8/8) || 100.00% (4/4) || +0.00%                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_refill.rs&amp;lt;/code&amp;gt; || 66.94% (83/124) || 57.14% (48/84) || +9.80%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_schedule.rs&amp;lt;/code&amp;gt; || 85.54% (142/166) || 69.16% (74/107) || +16.38%                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_test_utils.rs&amp;lt;/code&amp;gt; || 100.00% (10/10) || 100.00% (5/5) || +0.00%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 63.64% (84/132) || 35.71% (30/84) || +27.92%                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/ipc_message.rs&amp;lt;/code&amp;gt; || 0.00% (0/51) || 0.00% (0/41) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/messaging.rs&amp;lt;/code&amp;gt; || 100.00% (44/44) || 80.00% (24/30) || +20.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed.rs&amp;lt;/code&amp;gt; || 86.40% (305/353) || 62.65% (104/166) || +23.75%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_profile.rs&amp;lt;/code&amp;gt; || 100.00% (100/100) || 100.00% (4/4) || +0.00%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_schedule_entry.rs&amp;lt;/code&amp;gt; || 97.62% (82/84) || 100.00% (10/10) || -2.38%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection.rs&amp;lt;/code&amp;gt; || 62.43% (113/181) || 56.64% (64/113) || +5.79%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_actuator_state.rs&amp;lt;/code&amp;gt; || 61.11% (11/18) || 54.55% (6/11) || +6.57%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_signals.rs&amp;lt;/code&amp;gt; || 62.50% (100/160) || 48.08% (50/104) || +14.42%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/config_app.rs&amp;lt;/code&amp;gt; || 82.35% (98/119) || 57.14% (44/77) || +25.21%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/execution_config.rs&amp;lt;/code&amp;gt; || 86.30% (63/73) || 72.34% (34/47) || +13.96%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/pid_file.rs&amp;lt;/code&amp;gt; || 96.67% (58/60) || 77.14% (27/35) || +19.52%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/run.rs&amp;lt;/code&amp;gt; || 83.40% (725/869) || 58.74% (306/521) || +24.66%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling.rs&amp;lt;/code&amp;gt; || 88.08% (340/386) || 61.08% (135/221) || +27.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_profile.rs&amp;lt;/code&amp;gt; || 100.00% (71/71) || 100.00% (18/18) || +0.00%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_set_val.rs&amp;lt;/code&amp;gt; || 77.97% (46/59) || 44.44% (24/54) || +33.53%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_dht.rs&amp;lt;/code&amp;gt; || 74.07% (40/54) || 64.71% (22/34) || +9.36%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_food_injection.rs&amp;lt;/code&amp;gt; || 83.33% (20/24) || 75.00% (12/16) || +8.33%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_i2c_interface.rs&amp;lt;/code&amp;gt; || 32.50% (13/40) || 28.00% (7/25) || +4.50%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_peristaltic_pump.rs&amp;lt;/code&amp;gt; || 85.00% (17/20) || 72.73% (8/11) || +12.27%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_relay_manager.rs&amp;lt;/code&amp;gt; || 82.50% (33/40) || 75.86% (22/29) || +6.64%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_sql_interface.rs&amp;lt;/code&amp;gt; || 66.21% (241/364) || 63.81% (134/210) || +2.40%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_water_injection.rs&amp;lt;/code&amp;gt; || 82.61% (19/23) || 75.00% (12/16) || +7.61%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/process_privileges.rs&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder.rs&amp;lt;/code&amp;gt; || 84.77% (590/696) || 54.12% (269/497) || +30.65%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_channels.rs&amp;lt;/code&amp;gt; || 58.82% (10/17) || 45.45% (5/11) || +13.37%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_data_frame.rs&amp;lt;/code&amp;gt; || 52.66% (101/192) || 22.31% (27/121) || +30.35%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/actuator_state.rs&amp;lt;/code&amp;gt; || 74.07% (20/27) || 75.00% (15/20) || -0.93%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/aquarium_device.rs&amp;lt;/code&amp;gt; || 66.67% (14/21) || 68.75% (11/16) || -2.08%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager.rs&amp;lt;/code&amp;gt; || 58.46% (394/674) || 45.43% (179/394) || +13.03%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager_channels.rs&amp;lt;/code&amp;gt; || 90.91% (20/22) || 45.83% (11/24) || +45.08%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific.rs&amp;lt;/code&amp;gt; || 74.77% (329/440) || 55.42% (92/166) || +19.35%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht.rs&amp;lt;/code&amp;gt; || 75.00% (105/140) || 53.64% (59/110) || +21.36%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20.rs&amp;lt;/code&amp;gt; || 74.55% (123/165) || 51.09% (47/92) || +23.46%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface.rs&amp;lt;/code&amp;gt; || 76.54% (362/473) || 57.51% (153/266) || +19.03%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager.rs&amp;lt;/code&amp;gt; || 76.10% (761/1000) || 54.34% (282/519) || +21.76%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager_channels.rs&amp;lt;/code&amp;gt; || 84.62% (33/39) || 75.00% (9/12) || +9.62%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating.rs&amp;lt;/code&amp;gt; || 90.96% (332/365) || 68.60% (148/216) || +22.36%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_actuation.rs&amp;lt;/code&amp;gt; || 84.47% (87/103) || 67.24% (39/58) || +17.23%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_set_vals.rs&amp;lt;/code&amp;gt; || 87.50% (35/40) || 64.29% (18/28) || +23.21%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_stats_entry.rs&amp;lt;/code&amp;gt; || 94.67% (142/150) || 82.22% (74/90) || +12.44%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (23/23) || +0.00%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile_config.rs&amp;lt;/code&amp;gt; || 70.83% (17/24) || 50.00% (7/14) || +20.83%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/temperature_gradient.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (32/32) || +0.00%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/thermal_control_status.rs&amp;lt;/code&amp;gt; || 81.25% (26/32) || 58.82% (10/17) || +22.43%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation.rs&amp;lt;/code&amp;gt; || 87.67% (192/219) || 61.16% (74/121) || +26.51%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 58.82% (30/51) || 58.82% (20/34) || +0.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/config.rs&amp;lt;/code&amp;gt; || 76.99% (830/1078) || 72.84% (437/600) || +4.15%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/spin_sleeper.rs&amp;lt;/code&amp;gt; || 79.17% (19/24) || 69.23% (9/13) || +9.94%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/thread_builder.rs&amp;lt;/code&amp;gt; || 92.59% (25/27) || 78.57% (11/14) || +14.02%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill.rs&amp;lt;/code&amp;gt; || 86.81% (447/515) || 69.45% (216/311) || +17.36%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch.rs&amp;lt;/code&amp;gt; || 84.14% (191/227) || 74.47% (105/141) || +9.67%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch_signals.rs&amp;lt;/code&amp;gt; || 90.36% (75/83) || 86.96% (40/46) || +3.40%                                                                                                    &lt;br /&gt;
    |}&lt;br /&gt;
&lt;br /&gt;
== System tests using the simulator ==&lt;br /&gt;
Running the application in simulator mode.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=556</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=556"/>
		<updated>2026-07-25T09:52:23Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Comparing the results from llvm-cov and tarpaulin */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;br /&gt;
&lt;br /&gt;
= Test strategy =&lt;br /&gt;
== Unit tests ==&lt;br /&gt;
Located within the implementation code files within &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Integration tests in test ==&lt;br /&gt;
Separated from implementation code to the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
== Code coverage measurement ==&lt;br /&gt;
=== Using tarpaulin ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo tarpaulin --features &amp;quot;test-utils&amp;quot; --out Html&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurement takes app. 22 minutes.&lt;br /&gt;
&lt;br /&gt;
=== Using llvm-cov ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo llvm-cov --features &amp;quot;test-utils&amp;quot; --html &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurements takes app. 8 minutes.&lt;br /&gt;
=== Comparing the results from llvm-cov and tarpaulin ===&lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Module Summary Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! Subsystem / Module !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                        &lt;br /&gt;
    |- style=&amp;quot;font-weight:bold; background-color:#f0f0f0;&amp;quot;                                                                                                                                                 &lt;br /&gt;
    | '''TOTAL (All Application Source Files)''' || 71.53% (11216/15681) || 54.48% (4929/9047) || +17.04%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/&amp;lt;/code&amp;gt; || 0.00% (0/597) || 0.00% (0/457) || +0.00%                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/&amp;lt;/code&amp;gt; || 69.73% (1283/1840) || 52.72% (631/1197) || +17.01%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/&amp;lt;/code&amp;gt; || 46.32% (44/95) || 33.80% (24/71) || +12.51%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/&amp;lt;/code&amp;gt; || 79.06% (721/912) || 58.80% (244/415) || +20.26%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/&amp;lt;/code&amp;gt; || 84.06% (944/1123) || 59.48% (411/691) || +24.58%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/&amp;lt;/code&amp;gt; || 88.11% (467/530) || 60.33% (181/300) || +27.78%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/&amp;lt;/code&amp;gt; || 58.82% (610/1037) || 55.57% (334/601) || +3.25%                                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/&amp;lt;/code&amp;gt; || 77.46% (701/905) || 47.50% (304/640) || +29.96%                                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/&amp;lt;/code&amp;gt; || 60.22% (448/744) || 47.58% (216/454) || +12.64%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/&amp;lt;/code&amp;gt; || 75.70% (1748/2309) || 54.76% (656/1198) || +20.95%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/simulator/&amp;lt;/code&amp;gt; || 3.07% (10/326) || 2.05% (4/195) || +1.02%                                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/test_server/&amp;lt;/code&amp;gt; || 67.93% (822/1210) || 59.67% (429/719) || +8.27%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/&amp;lt;/code&amp;gt; || 88.45% (1019/1152) || 67.96% (437/643) || +20.49%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/&amp;lt;/code&amp;gt; || 80.91% (1187/1467) || 75.10% (564/751) || +5.81%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/&amp;lt;/code&amp;gt; || 86.10% (700/813) || 71.18% (331/465) || +14.92%                                                                                                                           &lt;br /&gt;
    |}  &lt;br /&gt;
&lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Detailed File Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! File Path !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_client.rs&amp;lt;/code&amp;gt; || 0.00% (0/117) || 0.00% (0/98) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_commissioning.rs&amp;lt;/code&amp;gt; || 0.00% (0/426) || 0.00% (0/315) || +0.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_test_server.rs&amp;lt;/code&amp;gt; || 0.00% (0/54) || 0.00% (0/44) || +0.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/database_interface_feed_trait.rs&amp;lt;/code&amp;gt; || 77.86% (102/131) || 58.06% (54/93) || +19.80%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/pingable.rs&amp;lt;/code&amp;gt; || 44.44% (4/9) || 33.33% (2/6) || +11.11%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface.rs&amp;lt;/code&amp;gt; || 65.79% (250/380) || 48.57% (119/245) || +17.22%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_balling.rs&amp;lt;/code&amp;gt; || 69.31% (140/202) || 53.91% (69/128) || +15.40%                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_data.rs&amp;lt;/code&amp;gt; || 60.76% (48/79) || 59.62% (31/52) || +1.14%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_feed.rs&amp;lt;/code&amp;gt; || 63.70% (186/292) || 45.00% (90/200) || +18.70%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_set_vals.rs&amp;lt;/code&amp;gt; || 66.12% (80/121) || 37.97% (30/79) || +28.15%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats.rs&amp;lt;/code&amp;gt; || 77.65% (139/179) || 67.59% (73/108) || +10.06%                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats_data_transfer.rs&amp;lt;/code&amp;gt; || 100.00% (7/7) || 100.00% (2/2) || +0.00%                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_midnight.rs&amp;lt;/code&amp;gt; || 100.00% (8/8) || 100.00% (4/4) || +0.00%                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_refill.rs&amp;lt;/code&amp;gt; || 66.94% (83/124) || 57.14% (48/84) || +9.80%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_schedule.rs&amp;lt;/code&amp;gt; || 85.54% (142/166) || 69.16% (74/107) || +16.38%                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_test_utils.rs&amp;lt;/code&amp;gt; || 100.00% (10/10) || 100.00% (5/5) || +0.00%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 63.64% (84/132) || 35.71% (30/84) || +27.92%                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/ipc_message.rs&amp;lt;/code&amp;gt; || 0.00% (0/51) || 0.00% (0/41) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/messaging.rs&amp;lt;/code&amp;gt; || 100.00% (44/44) || 80.00% (24/30) || +20.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed.rs&amp;lt;/code&amp;gt; || 86.40% (305/353) || 62.65% (104/166) || +23.75%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_profile.rs&amp;lt;/code&amp;gt; || 100.00% (100/100) || 100.00% (4/4) || +0.00%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_schedule_entry.rs&amp;lt;/code&amp;gt; || 97.62% (82/84) || 100.00% (10/10) || -2.38%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection.rs&amp;lt;/code&amp;gt; || 62.43% (113/181) || 56.64% (64/113) || +5.79%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_actuator_state.rs&amp;lt;/code&amp;gt; || 61.11% (11/18) || 54.55% (6/11) || +6.57%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_signals.rs&amp;lt;/code&amp;gt; || 62.50% (100/160) || 48.08% (50/104) || +14.42%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/config_app.rs&amp;lt;/code&amp;gt; || 82.35% (98/119) || 57.14% (44/77) || +25.21%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/execution_config.rs&amp;lt;/code&amp;gt; || 86.30% (63/73) || 72.34% (34/47) || +13.96%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/pid_file.rs&amp;lt;/code&amp;gt; || 96.67% (58/60) || 77.14% (27/35) || +19.52%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/run.rs&amp;lt;/code&amp;gt; || 83.40% (725/869) || 58.74% (306/521) || +24.66%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling.rs&amp;lt;/code&amp;gt; || 88.08% (340/386) || 61.08% (135/221) || +27.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_profile.rs&amp;lt;/code&amp;gt; || 100.00% (71/71) || 100.00% (18/18) || +0.00%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_set_val.rs&amp;lt;/code&amp;gt; || 77.97% (46/59) || 44.44% (24/54) || +33.53%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_dht.rs&amp;lt;/code&amp;gt; || 74.07% (40/54) || 64.71% (22/34) || +9.36%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_food_injection.rs&amp;lt;/code&amp;gt; || 83.33% (20/24) || 75.00% (12/16) || +8.33%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_i2c_interface.rs&amp;lt;/code&amp;gt; || 32.50% (13/40) || 28.00% (7/25) || +4.50%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_peristaltic_pump.rs&amp;lt;/code&amp;gt; || 85.00% (17/20) || 72.73% (8/11) || +12.27%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_relay_manager.rs&amp;lt;/code&amp;gt; || 82.50% (33/40) || 75.86% (22/29) || +6.64%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_sql_interface.rs&amp;lt;/code&amp;gt; || 66.21% (241/364) || 63.81% (134/210) || +2.40%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_water_injection.rs&amp;lt;/code&amp;gt; || 82.61% (19/23) || 75.00% (12/16) || +7.61%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/process_privileges.rs&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder.rs&amp;lt;/code&amp;gt; || 84.77% (590/696) || 54.12% (269/497) || +30.65%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_channels.rs&amp;lt;/code&amp;gt; || 58.82% (10/17) || 45.45% (5/11) || +13.37%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_data_frame.rs&amp;lt;/code&amp;gt; || 52.66% (101/192) || 22.31% (27/121) || +30.35%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/actuator_state.rs&amp;lt;/code&amp;gt; || 74.07% (20/27) || 75.00% (15/20) || -0.93%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/aquarium_device.rs&amp;lt;/code&amp;gt; || 66.67% (14/21) || 68.75% (11/16) || -2.08%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager.rs&amp;lt;/code&amp;gt; || 58.46% (394/674) || 45.43% (179/394) || +13.03%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager_channels.rs&amp;lt;/code&amp;gt; || 90.91% (20/22) || 45.83% (11/24) || +45.08%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific.rs&amp;lt;/code&amp;gt; || 74.77% (329/440) || 55.42% (92/166) || +19.35%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht.rs&amp;lt;/code&amp;gt; || 75.00% (105/140) || 53.64% (59/110) || +21.36%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20.rs&amp;lt;/code&amp;gt; || 74.55% (123/165) || 51.09% (47/92) || +23.46%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface.rs&amp;lt;/code&amp;gt; || 76.54% (362/473) || 57.51% (153/266) || +19.03%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager.rs&amp;lt;/code&amp;gt; || 76.10% (761/1000) || 54.34% (282/519) || +21.76%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager_channels.rs&amp;lt;/code&amp;gt; || 84.62% (33/39) || 75.00% (9/12) || +9.62%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating.rs&amp;lt;/code&amp;gt; || 90.96% (332/365) || 68.60% (148/216) || +22.36%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_actuation.rs&amp;lt;/code&amp;gt; || 84.47% (87/103) || 67.24% (39/58) || +17.23%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_set_vals.rs&amp;lt;/code&amp;gt; || 87.50% (35/40) || 64.29% (18/28) || +23.21%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_stats_entry.rs&amp;lt;/code&amp;gt; || 94.67% (142/150) || 82.22% (74/90) || +12.44%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (23/23) || +0.00%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile_config.rs&amp;lt;/code&amp;gt; || 70.83% (17/24) || 50.00% (7/14) || +20.83%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/temperature_gradient.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (32/32) || +0.00%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/thermal_control_status.rs&amp;lt;/code&amp;gt; || 81.25% (26/32) || 58.82% (10/17) || +22.43%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation.rs&amp;lt;/code&amp;gt; || 87.67% (192/219) || 61.16% (74/121) || +26.51%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 58.82% (30/51) || 58.82% (20/34) || +0.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/config.rs&amp;lt;/code&amp;gt; || 76.99% (830/1078) || 72.84% (437/600) || +4.15%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/spin_sleeper.rs&amp;lt;/code&amp;gt; || 79.17% (19/24) || 69.23% (9/13) || +9.94%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/thread_builder.rs&amp;lt;/code&amp;gt; || 92.59% (25/27) || 78.57% (11/14) || +14.02%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill.rs&amp;lt;/code&amp;gt; || 86.81% (447/515) || 69.45% (216/311) || +17.36%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch.rs&amp;lt;/code&amp;gt; || 84.14% (191/227) || 74.47% (105/141) || +9.67%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch_signals.rs&amp;lt;/code&amp;gt; || 90.36% (75/83) || 86.96% (40/46) || +3.40%                                                                                                    &lt;br /&gt;
    |}&lt;br /&gt;
&lt;br /&gt;
== System tests using the simulator ==&lt;br /&gt;
Running the application in simulator mode.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=555</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=555"/>
		<updated>2026-07-25T09:45:59Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Comparing the results from llvm-cov and tarpaulin */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;br /&gt;
&lt;br /&gt;
= Test strategy =&lt;br /&gt;
== Unit tests ==&lt;br /&gt;
Located within the implementation code files within &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Integration tests in test ==&lt;br /&gt;
Separated from implementation code to the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
== Code coverage measurement ==&lt;br /&gt;
=== Using tarpaulin ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo tarpaulin --features &amp;quot;test-utils&amp;quot; --out Html&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurement takes app. 22 minutes.&lt;br /&gt;
&lt;br /&gt;
=== Using llvm-cov ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo llvm-cov --features &amp;quot;test-utils&amp;quot; --html &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurements takes app. 8 minutes.&lt;br /&gt;
=== Comparing the results from llvm-cov and tarpaulin ===&lt;br /&gt;
    ==== Coverage Summary by Subsystem / Module ====                                                                                                                                                           &lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Module Summary Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! Subsystem / Module !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                        &lt;br /&gt;
    |- style=&amp;quot;font-weight:bold; background-color:#f0f0f0;&amp;quot;                                                                                                                                                 &lt;br /&gt;
    | '''TOTAL (All Application Source Files)''' || 71.53% (11216/15681) || 54.48% (4929/9047) || +17.04%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/&amp;lt;/code&amp;gt; || 0.00% (0/597) || 0.00% (0/457) || +0.00%                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/&amp;lt;/code&amp;gt; || 69.73% (1283/1840) || 52.72% (631/1197) || +17.01%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/&amp;lt;/code&amp;gt; || 46.32% (44/95) || 33.80% (24/71) || +12.51%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/&amp;lt;/code&amp;gt; || 79.06% (721/912) || 58.80% (244/415) || +20.26%                                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/&amp;lt;/code&amp;gt; || 84.06% (944/1123) || 59.48% (411/691) || +24.58%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/&amp;lt;/code&amp;gt; || 88.11% (467/530) || 60.33% (181/300) || +27.78%                                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/&amp;lt;/code&amp;gt; || 58.82% (610/1037) || 55.57% (334/601) || +3.25%                                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/&amp;lt;/code&amp;gt; || 77.46% (701/905) || 47.50% (304/640) || +29.96%                                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/&amp;lt;/code&amp;gt; || 60.22% (448/744) || 47.58% (216/454) || +12.64%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/&amp;lt;/code&amp;gt; || 75.70% (1748/2309) || 54.76% (656/1198) || +20.95%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/simulator/&amp;lt;/code&amp;gt; || 3.07% (10/326) || 2.05% (4/195) || +1.02%                                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/test_server/&amp;lt;/code&amp;gt; || 67.93% (822/1210) || 59.67% (429/719) || +8.27%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/&amp;lt;/code&amp;gt; || 88.45% (1019/1152) || 67.96% (437/643) || +20.49%                                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/&amp;lt;/code&amp;gt; || 80.91% (1187/1467) || 75.10% (564/751) || +5.81%                                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/&amp;lt;/code&amp;gt; || 86.10% (700/813) || 71.18% (331/465) || +14.92%                                                                                                                           &lt;br /&gt;
    |}  &lt;br /&gt;
&lt;br /&gt;
    ==== Detailed Coverage Comparison by File ====                                                                                                                                                             &lt;br /&gt;
    {| class=&amp;quot;wikitable sortable&amp;quot;                                                                                                                                                                          &lt;br /&gt;
    |+ Detailed File Comparison: llvm-cov vs. tarpaulin (Line Coverage)                                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    ! File Path !! llvm-cov Line Coverage !! tarpaulin Line Coverage !! Difference (% pts)                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_client.rs&amp;lt;/code&amp;gt; || 0.00% (0/117) || 0.00% (0/98) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_commissioning.rs&amp;lt;/code&amp;gt; || 0.00% (0/426) || 0.00% (0/315) || +0.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/bin/aquarium_test_server.rs&amp;lt;/code&amp;gt; || 0.00% (0/54) || 0.00% (0/44) || +0.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/database_interface_feed_trait.rs&amp;lt;/code&amp;gt; || 77.86% (102/131) || 58.06% (54/93) || +19.80%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/pingable.rs&amp;lt;/code&amp;gt; || 44.44% (4/9) || 33.33% (2/6) || +11.11%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface.rs&amp;lt;/code&amp;gt; || 65.79% (250/380) || 48.57% (119/245) || +17.22%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_balling.rs&amp;lt;/code&amp;gt; || 69.31% (140/202) || 53.91% (69/128) || +15.40%                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_data.rs&amp;lt;/code&amp;gt; || 60.76% (48/79) || 59.62% (31/52) || +1.14%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_feed.rs&amp;lt;/code&amp;gt; || 63.70% (186/292) || 45.00% (90/200) || +18.70%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_set_vals.rs&amp;lt;/code&amp;gt; || 66.12% (80/121) || 37.97% (30/79) || +28.15%                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats.rs&amp;lt;/code&amp;gt; || 77.65% (139/179) || 67.59% (73/108) || +10.06%                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_heating_stats_data_transfer.rs&amp;lt;/code&amp;gt; || 100.00% (7/7) || 100.00% (2/2) || +0.00%                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_midnight.rs&amp;lt;/code&amp;gt; || 100.00% (8/8) || 100.00% (4/4) || +0.00%                                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_refill.rs&amp;lt;/code&amp;gt; || 66.94% (83/124) || 57.14% (48/84) || +9.80%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_schedule.rs&amp;lt;/code&amp;gt; || 85.54% (142/166) || 69.16% (74/107) || +16.38%                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_test_utils.rs&amp;lt;/code&amp;gt; || 100.00% (10/10) || 100.00% (5/5) || +0.00%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/database/sql_interface_ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 63.64% (84/132) || 35.71% (30/84) || +27.92%                                                                                      &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/ipc_message.rs&amp;lt;/code&amp;gt; || 0.00% (0/51) || 0.00% (0/41) || +0.00%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/dispatch/messaging.rs&amp;lt;/code&amp;gt; || 100.00% (44/44) || 80.00% (24/30) || +20.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed.rs&amp;lt;/code&amp;gt; || 86.40% (305/353) || 62.65% (104/166) || +23.75%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_profile.rs&amp;lt;/code&amp;gt; || 100.00% (100/100) || 100.00% (4/4) || +0.00%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/feed_schedule_entry.rs&amp;lt;/code&amp;gt; || 97.62% (82/84) || 100.00% (10/10) || -2.38%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection.rs&amp;lt;/code&amp;gt; || 62.43% (113/181) || 56.64% (64/113) || +5.79%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_actuator_state.rs&amp;lt;/code&amp;gt; || 61.11% (11/18) || 54.55% (6/11) || +6.57%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/food/food_injection_signals.rs&amp;lt;/code&amp;gt; || 62.50% (100/160) || 48.08% (50/104) || +14.42%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/config_app.rs&amp;lt;/code&amp;gt; || 82.35% (98/119) || 57.14% (44/77) || +25.21%                                                                                                                &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/execution_config.rs&amp;lt;/code&amp;gt; || 86.30% (63/73) || 72.34% (34/47) || +13.96%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/pid_file.rs&amp;lt;/code&amp;gt; || 96.67% (58/60) || 77.14% (27/35) || +19.52%                                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/launch/run.rs&amp;lt;/code&amp;gt; || 83.40% (725/869) || 58.74% (306/521) || +24.66%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling.rs&amp;lt;/code&amp;gt; || 88.08% (340/386) || 61.08% (135/221) || +27.00%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_profile.rs&amp;lt;/code&amp;gt; || 100.00% (71/71) || 100.00% (18/18) || +0.00%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mineral/balling_set_val.rs&amp;lt;/code&amp;gt; || 77.97% (46/59) || 44.44% (24/54) || +33.53%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_dht.rs&amp;lt;/code&amp;gt; || 74.07% (40/54) || 64.71% (22/34) || +9.36%                                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_food_injection.rs&amp;lt;/code&amp;gt; || 83.33% (20/24) || 75.00% (12/16) || +8.33%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_i2c_interface.rs&amp;lt;/code&amp;gt; || 32.50% (13/40) || 28.00% (7/25) || +4.50%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_peristaltic_pump.rs&amp;lt;/code&amp;gt; || 85.00% (17/20) || 72.73% (8/11) || +12.27%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_relay_manager.rs&amp;lt;/code&amp;gt; || 82.50% (33/40) || 75.86% (22/29) || +6.64%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_sql_interface.rs&amp;lt;/code&amp;gt; || 66.21% (241/364) || 63.81% (134/210) || +2.40%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/mocks/mock_water_injection.rs&amp;lt;/code&amp;gt; || 82.61% (19/23) || 75.00% (12/16) || +7.61%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/permission/process_privileges.rs&amp;lt;/code&amp;gt; || 94.25% (377/400) || 81.58% (93/114) || +12.67%                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder.rs&amp;lt;/code&amp;gt; || 84.77% (590/696) || 54.12% (269/497) || +30.65%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_channels.rs&amp;lt;/code&amp;gt; || 58.82% (10/17) || 45.45% (5/11) || +13.37%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/recorder/recorder_data_frame.rs&amp;lt;/code&amp;gt; || 52.66% (101/192) || 22.31% (27/121) || +30.35%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/actuator_state.rs&amp;lt;/code&amp;gt; || 74.07% (20/27) || 75.00% (15/20) || -0.93%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/aquarium_device.rs&amp;lt;/code&amp;gt; || 66.67% (14/21) || 68.75% (11/16) || -2.08%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager.rs&amp;lt;/code&amp;gt; || 58.46% (394/674) || 45.43% (179/394) || +13.03%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/relays/relay_manager_channels.rs&amp;lt;/code&amp;gt; || 90.91% (20/22) || 45.83% (11/24) || +45.08%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific.rs&amp;lt;/code&amp;gt; || 74.77% (329/440) || 55.42% (92/166) || +19.35%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/atlas_scientific_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht.rs&amp;lt;/code&amp;gt; || 75.00% (105/140) || 53.64% (59/110) || +21.36%                                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/dht_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20.rs&amp;lt;/code&amp;gt; || 74.55% (123/165) || 51.09% (47/92) || +23.46%                                                                                                                 &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/ds18b20_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                             &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface.rs&amp;lt;/code&amp;gt; || 76.54% (362/473) || 57.51% (153/266) || +19.03%                                                                                                         &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/i2c_interface_channels.rs&amp;lt;/code&amp;gt; || 69.23% (9/13) || 50.00% (3/6) || +19.23%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager.rs&amp;lt;/code&amp;gt; || 76.10% (761/1000) || 54.34% (282/519) || +21.76%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/sensors/sensor_manager_channels.rs&amp;lt;/code&amp;gt; || 84.62% (33/39) || 75.00% (9/12) || +9.62%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating.rs&amp;lt;/code&amp;gt; || 90.96% (332/365) || 68.60% (148/216) || +22.36%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_actuation.rs&amp;lt;/code&amp;gt; || 84.47% (87/103) || 67.24% (39/58) || +17.23%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                           &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_set_vals.rs&amp;lt;/code&amp;gt; || 87.50% (35/40) || 64.29% (18/28) || +23.21%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/heating_stats_entry.rs&amp;lt;/code&amp;gt; || 94.67% (142/150) || 82.22% (74/90) || +12.44%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (23/23) || +0.00%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/saw_tooth_profile_config.rs&amp;lt;/code&amp;gt; || 70.83% (17/24) || 50.00% (7/14) || +20.83%                                                                                                   &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/temperature_gradient.rs&amp;lt;/code&amp;gt; || 100.00% (64/64) || 100.00% (32/32) || +0.00%                                                                                                     &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/thermal_control_status.rs&amp;lt;/code&amp;gt; || 81.25% (26/32) || 58.82% (10/17) || +22.43%                                                                                                    &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation.rs&amp;lt;/code&amp;gt; || 87.67% (192/219) || 61.16% (74/121) || +26.51%                                                                                                            &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_channels.rs&amp;lt;/code&amp;gt; || 75.00% (15/20) || 50.00% (5/10) || +25.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/thermal/ventilation_set_vals.rs&amp;lt;/code&amp;gt; || 58.82% (30/51) || 58.82% (20/34) || +0.00%                                                                                                       &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/config.rs&amp;lt;/code&amp;gt; || 76.99% (830/1078) || 72.84% (437/600) || +4.15%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/spin_sleeper.rs&amp;lt;/code&amp;gt; || 79.17% (19/24) || 69.23% (9/13) || +9.94%                                                                                                              &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/utilities/thread_builder.rs&amp;lt;/code&amp;gt; || 92.59% (25/27) || 78.57% (11/14) || +14.02%                                                                                                          &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt; || 64.29% (135/210) || 54.69% (70/128) || +9.60%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill.rs&amp;lt;/code&amp;gt; || 86.81% (447/515) || 69.45% (216/311) || +17.36%                                                                                                                  &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/refill_channels.rs&amp;lt;/code&amp;gt; || 71.43% (10/14) || 57.14% (4/7) || +14.29%                                                                                                               &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch.rs&amp;lt;/code&amp;gt; || 84.14% (191/227) || 74.47% (105/141) || +9.67%                                                                                                        &lt;br /&gt;
    |-                                                                                                                                                                                                     &lt;br /&gt;
    | &amp;lt;code&amp;gt;src/water/tank_level_switch_signals.rs&amp;lt;/code&amp;gt; || 90.36% (75/83) || 86.96% (40/46) || +3.40%                                                                                                    &lt;br /&gt;
    |}&lt;br /&gt;
&lt;br /&gt;
== System tests using the simulator ==&lt;br /&gt;
Running the application in simulator mode.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=554</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=554"/>
		<updated>2026-07-25T09:36:13Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Code coverage measurement */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;br /&gt;
&lt;br /&gt;
= Test strategy =&lt;br /&gt;
== Unit tests ==&lt;br /&gt;
Located within the implementation code files within &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Integration tests in test ==&lt;br /&gt;
Separated from implementation code to the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
== Code coverage measurement ==&lt;br /&gt;
=== Using tarpaulin ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo tarpaulin --features &amp;quot;test-utils&amp;quot; --out Html&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurement takes app. 22 minutes.&lt;br /&gt;
&lt;br /&gt;
=== Using llvm-cov ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo llvm-cov --features &amp;quot;test-utils&amp;quot; --html &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurements takes app. 8 minutes.&lt;br /&gt;
=== Comparing the results from llvm-cov and tarpaulin ===&lt;br /&gt;
&lt;br /&gt;
== System tests using the simulator ==&lt;br /&gt;
Running the application in simulator mode.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=553</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=553"/>
		<updated>2026-07-25T09:30:31Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Using tarpaulin */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;br /&gt;
&lt;br /&gt;
= Test strategy =&lt;br /&gt;
== Unit tests ==&lt;br /&gt;
Located within the implementation code files within &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Integration tests in test ==&lt;br /&gt;
Separated from implementation code to the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
== Code coverage measurement ==&lt;br /&gt;
=== Using tarpaulin ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo tarpaulin --features &amp;quot;test-utils&amp;quot; --out html&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurement takes app. 22 minutes.&lt;br /&gt;
&lt;br /&gt;
=== Using llvm-cov ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo llvm-cov --features &amp;quot;test-utils&amp;quot; --html &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurements takes app. 8 minutes.&lt;br /&gt;
=== Comparing the results from llvm-cov and tarpaulin ===&lt;br /&gt;
&lt;br /&gt;
== System tests using the simulator ==&lt;br /&gt;
Running the application in simulator mode.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=552</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=552"/>
		<updated>2026-07-25T09:30:20Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Using tarpaulin */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;br /&gt;
&lt;br /&gt;
= Test strategy =&lt;br /&gt;
== Unit tests ==&lt;br /&gt;
Located within the implementation code files within &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Integration tests in test ==&lt;br /&gt;
Separated from implementation code to the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
== Code coverage measurement ==&lt;br /&gt;
=== Using tarpaulin ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo tarpaulin --features &amp;quot;test-utils&amp;quot; --out html&amp;lt;/code&amp;gt;&lt;br /&gt;
The measurement takes app. 22 minutes.&lt;br /&gt;
&lt;br /&gt;
=== Using llvm-cov ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo llvm-cov --features &amp;quot;test-utils&amp;quot; --html &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurements takes app. 8 minutes.&lt;br /&gt;
=== Comparing the results from llvm-cov and tarpaulin ===&lt;br /&gt;
&lt;br /&gt;
== System tests using the simulator ==&lt;br /&gt;
Running the application in simulator mode.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=551</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=551"/>
		<updated>2026-07-25T09:17:53Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Code coverage measurement */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;br /&gt;
&lt;br /&gt;
= Test strategy =&lt;br /&gt;
== Unit tests ==&lt;br /&gt;
Located within the implementation code files within &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Integration tests in test ==&lt;br /&gt;
Separated from implementation code to the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
== Code coverage measurement ==&lt;br /&gt;
=== Using tarpaulin ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo tarpaulin --features &amp;quot;test-utils&amp;quot; --out html&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Using llvm-cov ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo llvm-cov --features &amp;quot;test-utils&amp;quot; --html &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurements takes app. 8 minutes.&lt;br /&gt;
=== Comparing the results from llvm-cov and tarpaulin ===&lt;br /&gt;
&lt;br /&gt;
== System tests using the simulator ==&lt;br /&gt;
Running the application in simulator mode.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=550</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=550"/>
		<updated>2026-07-25T09:03:35Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Using llvm-cov */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;br /&gt;
&lt;br /&gt;
= Test strategy =&lt;br /&gt;
== Unit tests ==&lt;br /&gt;
Located within the implementation code files within &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Integration tests in test ==&lt;br /&gt;
Separated from implementation code to the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
== Code coverage measurement ==&lt;br /&gt;
=== Using tarpaulin ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo tarpaulin --features &amp;quot;test-utils&amp;quot; --out html&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Using llvm-cov ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo llvm-cov --features &amp;quot;test-utils&amp;quot; --html &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The measurements takes app. 8 minutes.&lt;br /&gt;
&lt;br /&gt;
== System tests using the simulator ==&lt;br /&gt;
Running the application in simulator mode.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=549</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=549"/>
		<updated>2026-07-25T08:37:35Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Using llvm-cov */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;br /&gt;
&lt;br /&gt;
= Test strategy =&lt;br /&gt;
== Unit tests ==&lt;br /&gt;
Located within the implementation code files within &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Integration tests in test ==&lt;br /&gt;
Separated from implementation code to the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
== Code coverage measurement ==&lt;br /&gt;
=== Using tarpaulin ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo tarpaulin --features &amp;quot;test-utils&amp;quot; --out html&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Using llvm-cov ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo llvm-cov --features &amp;quot;test-utils&amp;quot; --html &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== System tests using the simulator ==&lt;br /&gt;
Running the application in simulator mode.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=548</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=548"/>
		<updated>2026-07-25T08:37:00Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Using llvm-cov */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;br /&gt;
&lt;br /&gt;
= Test strategy =&lt;br /&gt;
== Unit tests ==&lt;br /&gt;
Located within the implementation code files within &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Integration tests in test ==&lt;br /&gt;
Separated from implementation code to the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
== Code coverage measurement ==&lt;br /&gt;
=== Using tarpaulin ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo tarpaulin --features &amp;quot;test-utils&amp;quot; --out html&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Using llvm-cov ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo llvm-cov --features &amp;quot;test-utils&amp;quot; --html&amp;lt;c/ode&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== System tests using the simulator ==&lt;br /&gt;
Running the application in simulator mode.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=547</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=547"/>
		<updated>2026-07-25T08:36:48Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Integration tests in test */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;br /&gt;
&lt;br /&gt;
= Test strategy =&lt;br /&gt;
== Unit tests ==&lt;br /&gt;
Located within the implementation code files within &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Integration tests in test ==&lt;br /&gt;
Separated from implementation code to the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
== Code coverage measurement ==&lt;br /&gt;
=== Using tarpaulin ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo tarpaulin --features &amp;quot;test-utils&amp;quot; --out html&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Using llvm-cov ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo llvm-cov --features &amp;quot;test-utils&amp;quot; --html&amp;lt;c/ode&amp;gt;&lt;br /&gt;
== System tests using the simulator ==&lt;br /&gt;
Running the application in simulator mode.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=546</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=546"/>
		<updated>2026-07-25T08:36:36Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* = Integration tests in test */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;br /&gt;
&lt;br /&gt;
= Test strategy =&lt;br /&gt;
== Unit tests ==&lt;br /&gt;
Located within the implementation code files within &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Integration tests in test =&lt;br /&gt;
Separated from implementation code to the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
== Code coverage measurement ==&lt;br /&gt;
=== Using tarpaulin ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo tarpaulin --features &amp;quot;test-utils&amp;quot; --out html&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Using llvm-cov ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo llvm-cov --features &amp;quot;test-utils&amp;quot; --html&amp;lt;c/ode&amp;gt;&lt;br /&gt;
== System tests using the simulator ==&lt;br /&gt;
Running the application in simulator mode.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=545</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=545"/>
		<updated>2026-07-25T08:36:25Z</updated>

		<summary type="html">&lt;p&gt;Uwe: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;br /&gt;
&lt;br /&gt;
= Test strategy =&lt;br /&gt;
== Unit tests ==&lt;br /&gt;
Located within the implementation code files within &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Integration tests in test =&lt;br /&gt;
Separated from implementation code to the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
== Code coverage measurement ==&lt;br /&gt;
=== Using tarpaulin ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo tarpaulin --features &amp;quot;test-utils&amp;quot; --out html&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Using llvm-cov ===&lt;br /&gt;
&amp;lt;code&amp;gt;cargo llvm-cov --features &amp;quot;test-utils&amp;quot; --html&amp;lt;c/ode&amp;gt;&lt;br /&gt;
== System tests using the simulator ==&lt;br /&gt;
Running the application in simulator mode.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=544</id>
		<title>Control application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Control_application&amp;diff=544"/>
		<updated>2026-07-24T06:19:05Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Development environment */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main control application of Aquarium control implements the following features:&lt;br /&gt;
&lt;br /&gt;
* [[Data acquisition of water temperature, pH and conductivity]]&lt;br /&gt;
* [[Data acquisition of ambient temperature and humidity]]&lt;br /&gt;
* [[Fresh water refill control|Refill control for fresh water]]&lt;br /&gt;
* Feed control&lt;br /&gt;
* Water temperature control using air ventilation and heater&lt;br /&gt;
* Balling mineral dosing&lt;br /&gt;
&lt;br /&gt;
The control of the relays for operating the devices can use two different modes:&lt;br /&gt;
* [[Arduino-based relay actuation|Using an Arduino-based relay actuation]]&lt;br /&gt;
* Using the GPIO of the Raspberry Pi (requires additional hardware) &lt;br /&gt;
&lt;br /&gt;
Additionally, the main control application implements the following supporting features:&lt;br /&gt;
* [[Version comparison between database and application]] &lt;br /&gt;
* Configuration using a Rust .toml configuration file&lt;br /&gt;
* Logging&lt;br /&gt;
* Command interface to receive instructions from outside of the application via a POSIX message queue&lt;br /&gt;
* Storage of recorded data in SQL database&lt;br /&gt;
* File-based communication to RAM-disk&lt;br /&gt;
* Usage of simulator to run with simulated sensor data for development purposes&lt;br /&gt;
* Schedule checks to limit the hour of day of operating certain actuators&lt;br /&gt;
* [[Communication with HW Watchdog]]&lt;br /&gt;
* [[Temperature gradient calculation]]&lt;br /&gt;
The application is written in Rust and is [[automatic start by using systemd| automatically started using systemd]].&lt;br /&gt;
&lt;br /&gt;
The documentation of the software is available [http://vps2483992.servdiscount-customer.com/aquarium_control_doc/aquarium_control/ here].&lt;br /&gt;
&lt;br /&gt;
Development for the main control application requires the [[setup of the development environment]].&lt;br /&gt;
&lt;br /&gt;
There is a [[Release procedure|release procedure]] which is highly recommended to be followed also by anyone developing the SW further.&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
== Startup of the application ==&lt;br /&gt;
The startup uses a two-layer approach:&lt;br /&gt;
# main() - Simple error handler wrapper that calls run() and exits with code 1 on errors&lt;br /&gt;
# run() - The actual initialization orchestrator that performs the major steps&lt;br /&gt;
&lt;br /&gt;
=== Phase 1: Command Line &amp;amp; Configuration ===&lt;br /&gt;
* Parses command line arguments (&amp;quot;help&amp;quot;, &amp;quot;version&amp;quot;, config file)&lt;br /&gt;
* Sets the default config file: &amp;lt;code&amp;gt;/etc/aquarium_control/aquarium_control.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
* Loads configuration and creates &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; (boolean flags determining which modules to run)&lt;br /&gt;
&lt;br /&gt;
=== Phase 2: System Setup ===&lt;br /&gt;
* Initializes the logging system&lt;br /&gt;
* Logs version information and executable hash&lt;br /&gt;
* Verifies root privileges (required for hardware access)&lt;br /&gt;
* Publishes process ID to file for client communication&lt;br /&gt;
&lt;br /&gt;
=== Phase 3: Database &amp;amp; Hardware ===&lt;br /&gt;
* Connects to database (MariaDB)&lt;br /&gt;
* Creates component-specific SQL interfaces (&amp;lt;code&amp;gt;Schedule&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Balling&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Heating&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Feed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Validates database schema and version compatibility&lt;br /&gt;
* Initializes hardware components if configured:&lt;br /&gt;
** RGB LEDs&lt;br /&gt;
** GPIO handlers&lt;br /&gt;
** Tank level switch&lt;br /&gt;
** I2C interface&lt;br /&gt;
** Atlas Scientific sensors&lt;br /&gt;
** DHT temperature/humidity sensors&lt;br /&gt;
** Relay actuator (Controllino)&lt;br /&gt;
&lt;br /&gt;
=== Phase 4: Communication Channels ===&lt;br /&gt;
* Creates the &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; struct containing ~30+ inter-thread communication channels&lt;br /&gt;
* Uses custom &amp;lt;code&amp;gt;AquaSender&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;AquaReceiver&amp;lt;/code&amp;gt; wrappers for MPSC channels&lt;br /&gt;
* Establishes bidirectional and unidirectional message paths between all modules&lt;br /&gt;
&lt;br /&gt;
=== Phase 5: Component Initialization ===&lt;br /&gt;
Instantiates all high-level control components:&lt;br /&gt;
* data logger&lt;br /&gt;
* sensor manager&lt;br /&gt;
* relay manager&lt;br /&gt;
* food injection&lt;br /&gt;
* mineral dosing (balling)&lt;br /&gt;
* water injection/refill&lt;br /&gt;
* heating control&lt;br /&gt;
* ventilation control&lt;br /&gt;
* monitors&lt;br /&gt;
* watchdog&lt;br /&gt;
* schedule checker&lt;br /&gt;
* temperature gradient calculation&lt;br /&gt;
* IPC messaging system&lt;br /&gt;
&lt;br /&gt;
=== Phase 6: Thread Spawning ===&lt;br /&gt;
* Uses &amp;lt;code&amp;gt;std::thread::scope&amp;lt;/code&amp;gt; for guaranteed cleanup&lt;br /&gt;
* Spawns threads conditionally based on &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; flags&lt;br /&gt;
* Critical: A 3-second startup delay allows sensors to acquire first data before control loops activate&lt;br /&gt;
&lt;br /&gt;
=== Architecture of thread configuration ===&lt;br /&gt;
* The &amp;lt;code&amp;gt;Channels&amp;lt;/code&amp;gt; module (&amp;lt;code&amp;gt;src/launch/channels.rs&amp;lt;/code&amp;gt;) acts as a central wiring diagram, with the Signal Handler serving as the main event coordinator.&lt;br /&gt;
* The &amp;lt;code&amp;gt;ExecutionConfig&amp;lt;/code&amp;gt; decouples configuration from execution - threads only receive flags about which modules are active, not the full config - this also avoids ownership issues.&lt;br /&gt;
&lt;br /&gt;
The application makes [[use of an asynchronous runtime for the main application]].&lt;br /&gt;
&lt;br /&gt;
= Measurement using Atlas Scientific circuits =&lt;br /&gt;
The core Abstraction (&amp;lt;code&amp;gt;AtlasScientificDriver&amp;lt;/code&amp;gt; Trait) is defined in &amp;lt;code&amp;gt;src/sensors/atlas_scientific_driver.rs&amp;lt;/code&amp;gt;, this trait serves as the common interface for all sensor types. It enforces three key behaviors:&lt;br /&gt;
* &amp;lt;code&amp;gt;send_request_to_i2c_interface&amp;lt;/code&amp;gt;: Encapsulates how to trigger a measurement (e.g., constructing specific I2C commands).&lt;br /&gt;
* &amp;lt;code&amp;gt;receive_response_from_i2c_interface&amp;lt;/code&amp;gt;: Handles parsing the raw byte response from the I2C bus into a normalized floating-point value.&lt;br /&gt;
* &amp;lt;code&amp;gt;device_init&amp;lt;/code&amp;gt;: Performs hardware-specific setup (crucial for OEM type sensor circuit).&lt;br /&gt;
&lt;br /&gt;
Circuit-type-specific implementations&lt;br /&gt;
* OEM Driver (&amp;lt;code&amp;gt;AtlasScientificOem&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a binary, register-based protocol.&lt;br /&gt;
** Initialization: Requires an explicit &amp;quot;Wake Up&amp;quot; command (writing to &amp;lt;code&amp;gt;REG_WAKEUP&amp;lt;/code&amp;gt;) and verifies the hardware by reading the &amp;lt;code&amp;gt;REG_DEVICE_TYPE&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Read Cycle: Writes a register address (e.g., &amp;lt;code&amp;gt;REG_DATA_MSB_PH&amp;lt;/code&amp;gt;), waits for a short processing time, and reads 4 bytes.&lt;br /&gt;
** Parsing: Decodes the 4-byte response as a Big Endian unsigned integer and divides it by a specific scale factor (e.g., 1000.0) to get the float value.&lt;br /&gt;
* EZO Driver (&amp;lt;code&amp;gt;AtlasScientificEzo&amp;lt;/code&amp;gt;):&lt;br /&gt;
** Protocol: Uses a text-based (ASCII) command protocol.&lt;br /&gt;
** Initialization: Minimal or no-op (these devices are typically always ready or auto-on).&lt;br /&gt;
** Read Cycle: Sends the ASCII character 'R', waits for a longer processing time, and reads a fixed 8-byte response.&lt;br /&gt;
** Parsing: Validates &amp;quot;magic&amp;quot; start/end bytes (1 and 0), converts the inner ASCII string (e.g., &amp;quot;23.50&amp;quot;) to a float.&lt;br /&gt;
&lt;br /&gt;
Context and factory (&amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct)&lt;br /&gt;
* The main &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; struct acts as the context. It holds a &amp;lt;code&amp;gt;HashMap&amp;lt;AquariumSignal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Box&amp;lt;dyn AtlasScientificDriver&amp;gt;&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Factory Logic: The &amp;lt;code&amp;gt;create_drivers&amp;lt;/code&amp;gt; method functions as a factory. It reads the &amp;lt;code&amp;gt;aquarium_control.toml&amp;lt;/code&amp;gt; configuration (specifically fields like &amp;lt;code&amp;gt;sensor_type_ph = &amp;quot;oem&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;ezo&amp;quot;&amp;lt;/code&amp;gt;) and&lt;br /&gt;
instantiates the correct driver implementation for each sensor (temperature, pH, conductivity).&lt;br /&gt;
* Execution: The main thread loop calls &amp;lt;code&amp;gt;driver.send_request...&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;driver.receive_response...&amp;lt;/code&amp;gt; on the active driver, unaware of whether it is communicating via binary registers or ASCII commands.&lt;br /&gt;
&lt;br /&gt;
Communication Flow&lt;br /&gt;
&lt;br /&gt;
The architecture decouples protocol logic from bus I/O:&lt;br /&gt;
# Driver creates an abstraction-specific &amp;lt;code&amp;gt;I2cRequest&amp;lt;/code&amp;gt; (containing address, write buffer, read length, delay).&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread sends this request via a channel to the &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread.&lt;br /&gt;
# &amp;lt;code&amp;gt;I2cInterface&amp;lt;/code&amp;gt; thread performs the physical I2C transaction.&lt;br /&gt;
# &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; thread receives the raw bytes and passes them back to the driver for parsing.&lt;br /&gt;
# Additionally, &amp;lt;code&amp;gt;AtlasScientific&amp;lt;/code&amp;gt; performs measurement value checks informing deviations to &amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
This design enables the system to mix and match sensor types (e.g., an OEM type pH sensor circuit with an EZO type temperature sensor circuit) purely through configuration changes.&lt;br /&gt;
&lt;br /&gt;
= Monitors =&lt;br /&gt;
The Monitors module (&amp;lt;code&amp;gt;src/watchmen/monitors.rs&amp;lt;/code&amp;gt;) is a dedicated thread that aggregates and stores historical system state information.&lt;br /&gt;
It interacts with the following modules:&lt;br /&gt;
* Refill&lt;br /&gt;
* Signal handler&lt;br /&gt;
&lt;br /&gt;
The Monitors thread receives data from the other thread by polling the channels.&lt;br /&gt;
&lt;br /&gt;
The data is transferred in specific structs (&amp;quot;Views&amp;quot;), e.g. &amp;lt;code&amp;gt;RefillView&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Monitors&amp;lt;/code&amp;gt; contains a deduplication logic: Data is only stored when it differs from previous sample.&lt;br /&gt;
&lt;br /&gt;
The number of samples is also limited (rolling window), to avoid overflow.&lt;br /&gt;
&lt;br /&gt;
= Commissioning =&lt;br /&gt;
For the [[commissioning software|commissioning of the aquarium control]], there is a separate binary which resides in &amp;lt;code&amp;gt;src/bin/commissioning.rs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Development environment =&lt;br /&gt;
== Install Rust ==&lt;br /&gt;
&amp;lt;code&amp;gt;curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Install additional development libraries ==&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install -y libudev-dev pkg-config&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Build time ==&lt;br /&gt;
Building the release on a Raspberry Pi 4 takes around 16 minutes.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Event-based_deletion_of_old_database_entries&amp;diff=543</id>
		<title>Event-based deletion of old database entries</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Event-based_deletion_of_old_database_entries&amp;diff=543"/>
		<updated>2026-07-24T05:58:36Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* User privileges */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Problem description ==&lt;br /&gt;
Over time, the size of the following four tables grows: &amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ballingdosinglog&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;feedlog&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can query the size in the database with the following commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MariaDB [aquarium]&amp;gt; select count(*) from refill;&lt;br /&gt;
+----------+&lt;br /&gt;
| count(*) |&lt;br /&gt;
+----------+&lt;br /&gt;
|      689 |&lt;br /&gt;
+----------+&lt;br /&gt;
1 row in set (0.001 sec)&lt;br /&gt;
&lt;br /&gt;
MariaDB [aquarium]&amp;gt; select count(*) from ballingdosinglog;&lt;br /&gt;
+----------+&lt;br /&gt;
| count(*) |&lt;br /&gt;
+----------+&lt;br /&gt;
|    13362 |&lt;br /&gt;
+----------+&lt;br /&gt;
1 row in set (0.018 sec)&lt;br /&gt;
&lt;br /&gt;
MariaDB [aquarium]&amp;gt; select count(*) from feedlog;&lt;br /&gt;
+----------+&lt;br /&gt;
| count(*) |&lt;br /&gt;
+----------+&lt;br /&gt;
|      263 |&lt;br /&gt;
+----------+&lt;br /&gt;
1 row in set (0.001 sec)&lt;br /&gt;
&lt;br /&gt;
MariaDB [aquarium]&amp;gt; select count(*) from data;&lt;br /&gt;
+----------+&lt;br /&gt;
| count(*) |&lt;br /&gt;
+----------+&lt;br /&gt;
|   213043 |&lt;br /&gt;
+----------+&lt;br /&gt;
1 row in set (0.166 sec)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Excessive database size impacts the performance and hence the stability of the aquarium control.&lt;br /&gt;
For this reason, the size needs to be limited by deleting old entries.&lt;br /&gt;
&lt;br /&gt;
== Configuration of MariaDb ==&lt;br /&gt;
Activate the event scheduler by adding the two last lines of below example to MariaDb configuration file (usually &amp;lt;code&amp;gt;/etc/mysql/my.cnf&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;/etc/mysql/mariadb.conf.d/50-server.cnf&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# The MariaDB configuration file&lt;br /&gt;
#&lt;br /&gt;
# The MariaDB/MySQL tools read configuration files in the following order:&lt;br /&gt;
# 1. &amp;quot;/etc/mysql/mariadb.cnf&amp;quot; (this file) to set global defaults,&lt;br /&gt;
# 2. &amp;quot;/etc/mysql/conf.d/*.cnf&amp;quot; to set global options.&lt;br /&gt;
# 3. &amp;quot;/etc/mysql/mariadb.conf.d/*.cnf&amp;quot; to set MariaDB-only options.&lt;br /&gt;
# 4. &amp;quot;~/.my.cnf&amp;quot; to set user-specific options.&lt;br /&gt;
#&lt;br /&gt;
# If the same option is defined multiple times, the last one will apply.&lt;br /&gt;
#&lt;br /&gt;
# One can use all long options that the program supports.&lt;br /&gt;
# Run program with --help to get a list of available options and with&lt;br /&gt;
# --print-defaults to see which it would actually understand and use.&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# This group is read both both by the client and the server&lt;br /&gt;
# use it for options that affect everything&lt;br /&gt;
#&lt;br /&gt;
[client-server]&lt;br /&gt;
&lt;br /&gt;
# Import all .cnf files from configuration directory&lt;br /&gt;
!includedir /etc/mysql/conf.d/&lt;br /&gt;
!includedir /etc/mysql/mariadb.conf.d/&lt;br /&gt;
&lt;br /&gt;
[mysqld]&lt;br /&gt;
event_scheduler=ON&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can check the status of the event schedule by running the query &amp;lt;code&amp;gt;SHOW VARIABLES LIKE 'event_scheduler';&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MariaDB [(none)]&amp;gt; SHOW VARIABLES LIKE 'event_scheduler';&lt;br /&gt;
+-----------------+-------+&lt;br /&gt;
| Variable_name   | Value |&lt;br /&gt;
+-----------------+-------+&lt;br /&gt;
| event_scheduler | ON    |&lt;br /&gt;
+-----------------+-------+&lt;br /&gt;
1 row in set (0.002 sec)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== User privileges ==&lt;br /&gt;
Check if your database user account has sufficient privileges by running &amp;lt;code&amp;gt;SHOW GRANTS FOR user@localhost;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can assign specific privilege for executing events by running:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;GRANT EVENT ON `your_database_name`.* TO 'your_username'@'localhost';&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;GRANT EXECUTE ON `your_database_name`.* TO 'your_username'@'localhost';&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;GRANT SUPER ON *.* TO 'your_username'@'localhost';&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;FLUSH PRIVILEGES;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Restart of MariaDb ==&lt;br /&gt;
In the terminal, execute &amp;lt;code&amp;gt;sudo systemctl restart mariadb&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Status of MariaDb ==&lt;br /&gt;
In the terminal, execute &amp;lt;code&amp;gt;sudo systemctl status mariadb&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The output should look similar to the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
● mariadb.service - MariaDB 10.3.39 database server&lt;br /&gt;
   Loaded: loaded (/lib/systemd/system/mariadb.service; enabled; vendor preset: enabled)&lt;br /&gt;
   Active: active (running) since Sat 2025-08-02 08:00:18 CEST; 1h 14min ago&lt;br /&gt;
     Docs: man:mysqld(8)&lt;br /&gt;
           https://mariadb.com/kb/en/library/systemd/&lt;br /&gt;
  Process: 32624 ExecStartPre=/usr/bin/install -m 755 -o mysql -g root -d /var/run/mysqld (code=exited, status=0/SUCCESS)&lt;br /&gt;
  Process: 32625 ExecStartPre=/bin/sh -c systemctl unset-environment _WSREP_START_POSITION (code=exited, status=0/SUCCESS)&lt;br /&gt;
  Process: 32627 ExecStartPre=/bin/sh -c [ ! -e /usr/bin/galera_recovery ] &amp;amp;&amp;amp; VAR= ||   VAR=`cd /usr/bin/..; /usr/bin/galera_recovery`; [ $? -eq 0 ]   &amp;amp;&amp;amp; systemctl set-environment _WSREP_START_POSITION=$VAR || exit 1 (code=exited, status=0/SUCCESS)&lt;br /&gt;
  Process: 32717 ExecStartPost=/bin/sh -c systemctl unset-environment _WSREP_START_POSITION (code=exited, status=0/SUCCESS)&lt;br /&gt;
  Process: 32719 ExecStartPost=/etc/mysql/debian-start (code=exited, status=0/SUCCESS)&lt;br /&gt;
 Main PID: 32681 (mysqld)&lt;br /&gt;
   Status: &amp;quot;Taking your SQL requests now...&amp;quot;&lt;br /&gt;
    Tasks: 42 (limit: 1592)&lt;br /&gt;
   CGroup: /system.slice/mariadb.service&lt;br /&gt;
           └─32681 /usr/sbin/mysqld&lt;br /&gt;
&lt;br /&gt;
Aug 02 08:00:17 raspberrypi systemd[1]: Starting MariaDB 10.3.39 database server...&lt;br /&gt;
Aug 02 08:00:18 raspberrypi systemd[1]: Started MariaDB 10.3.39 database server.&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32721]: Upgrading MySQL tables if necessary.&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: /usr/bin/mysql_upgrade: the '--basedir' option is always ignored&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: Looking for 'mysql' as: /usr/bin/mysql&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: Looking for 'mysqlcheck' as: /usr/bin/mysqlcheck&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: This installation of MariaDB is already upgraded to 10.3.39-MariaDB.&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: There is no need to run mysql_upgrade again for 10.3.39-MariaDB.&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: You can use --force if you still want to run mysql_upgrade&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32732]: Checking for insecure root accounts.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Adding events ==&lt;br /&gt;
The repository of the database contains a script &amp;lt;code&amp;gt;aquariumcontrol_database_cleanup_setup.sh&amp;lt;/code&amp;gt;.&lt;br /&gt;
The script will set up the cleanup for the following tables:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Configuration of retention period&lt;br /&gt;
! table name !! default period in days&lt;br /&gt;
|-&lt;br /&gt;
| balling_log || 60&lt;br /&gt;
|-&lt;br /&gt;
| data || 30&lt;br /&gt;
|-&lt;br /&gt;
| feed_log || 90&lt;br /&gt;
|-&lt;br /&gt;
| refill_log || 60&lt;br /&gt;
|-&lt;br /&gt;
| heating_stats || 1000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== View events in MariaDb ==&lt;br /&gt;
You can display the configured events using &amp;lt;code&amp;gt;show events;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MariaDB [aquarium]&amp;gt; show events;&lt;br /&gt;
+----------+----------------------------------+--------------------+-----------+-----------+------------+----------------+----------------+---------------------+------+---------+------------+----------------------+----------------------+--------------------+&lt;br /&gt;
| Db       | Name                             | Definer            | Time zone | Type      | Execute at | Interval value | Interval field | Starts              | Ends | Status  | Originator | character_set_client | collation_connection | Database Collation |&lt;br /&gt;
+----------+----------------------------------+--------------------+-----------+-----------+------------+----------------+----------------+---------------------+------+---------+------------+----------------------+----------------------+--------------------+&lt;br /&gt;
| aquarium | cleanup_old_ballingdosinglog_60d | aquarium@localhost | SYSTEM    | RECURRING | NULL       | 1              | DAY            | 2025-08-02 07:57:19 | NULL | ENABLED |          1 | utf8mb4              | utf8mb4_general_ci   | utf8mb4_general_ci |&lt;br /&gt;
| aquarium | cleanup_old_data_30d             | aquarium@localhost | SYSTEM    | RECURRING | NULL       | 1              | DAY            | 2025-08-02 07:57:19 | NULL | ENABLED |          1 | utf8mb4              | utf8mb4_general_ci   | utf8mb4_general_ci |&lt;br /&gt;
| aquarium | cleanup_old_feedlog_90d          | aquarium@localhost | SYSTEM    | RECURRING | NULL       | 1              | DAY            | 2025-08-02 07:57:19 | NULL | ENABLED |          1 | utf8mb4              | utf8mb4_general_ci   | utf8mb4_general_ci |&lt;br /&gt;
| aquarium | cleanup_old_refill_60d           | aquarium@localhost | SYSTEM    | RECURRING | NULL       | 1              | DAY            | 2025-08-02 07:57:19 | NULL | ENABLED |          1 | utf8mb4              | utf8mb4_general_ci   | utf8mb4_general_ci |&lt;br /&gt;
+----------+----------------------------------+--------------------+-----------+-----------+------------+----------------+----------------+---------------------+------+---------+------------+----------------------+----------------------+--------------------+&lt;br /&gt;
4 rows in set (0.002 sec)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Event-based_deletion_of_old_database_entries&amp;diff=542</id>
		<title>Event-based deletion of old database entries</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Event-based_deletion_of_old_database_entries&amp;diff=542"/>
		<updated>2026-07-24T05:49:17Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* User privileges */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Problem description ==&lt;br /&gt;
Over time, the size of the following four tables grows: &amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ballingdosinglog&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;feedlog&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can query the size in the database with the following commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MariaDB [aquarium]&amp;gt; select count(*) from refill;&lt;br /&gt;
+----------+&lt;br /&gt;
| count(*) |&lt;br /&gt;
+----------+&lt;br /&gt;
|      689 |&lt;br /&gt;
+----------+&lt;br /&gt;
1 row in set (0.001 sec)&lt;br /&gt;
&lt;br /&gt;
MariaDB [aquarium]&amp;gt; select count(*) from ballingdosinglog;&lt;br /&gt;
+----------+&lt;br /&gt;
| count(*) |&lt;br /&gt;
+----------+&lt;br /&gt;
|    13362 |&lt;br /&gt;
+----------+&lt;br /&gt;
1 row in set (0.018 sec)&lt;br /&gt;
&lt;br /&gt;
MariaDB [aquarium]&amp;gt; select count(*) from feedlog;&lt;br /&gt;
+----------+&lt;br /&gt;
| count(*) |&lt;br /&gt;
+----------+&lt;br /&gt;
|      263 |&lt;br /&gt;
+----------+&lt;br /&gt;
1 row in set (0.001 sec)&lt;br /&gt;
&lt;br /&gt;
MariaDB [aquarium]&amp;gt; select count(*) from data;&lt;br /&gt;
+----------+&lt;br /&gt;
| count(*) |&lt;br /&gt;
+----------+&lt;br /&gt;
|   213043 |&lt;br /&gt;
+----------+&lt;br /&gt;
1 row in set (0.166 sec)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Excessive database size impacts the performance and hence the stability of the aquarium control.&lt;br /&gt;
For this reason, the size needs to be limited by deleting old entries.&lt;br /&gt;
&lt;br /&gt;
== Configuration of MariaDb ==&lt;br /&gt;
Activate the event scheduler by adding the two last lines of below example to MariaDb configuration file (usually &amp;lt;code&amp;gt;/etc/mysql/my.cnf&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;/etc/mysql/mariadb.conf.d/50-server.cnf&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# The MariaDB configuration file&lt;br /&gt;
#&lt;br /&gt;
# The MariaDB/MySQL tools read configuration files in the following order:&lt;br /&gt;
# 1. &amp;quot;/etc/mysql/mariadb.cnf&amp;quot; (this file) to set global defaults,&lt;br /&gt;
# 2. &amp;quot;/etc/mysql/conf.d/*.cnf&amp;quot; to set global options.&lt;br /&gt;
# 3. &amp;quot;/etc/mysql/mariadb.conf.d/*.cnf&amp;quot; to set MariaDB-only options.&lt;br /&gt;
# 4. &amp;quot;~/.my.cnf&amp;quot; to set user-specific options.&lt;br /&gt;
#&lt;br /&gt;
# If the same option is defined multiple times, the last one will apply.&lt;br /&gt;
#&lt;br /&gt;
# One can use all long options that the program supports.&lt;br /&gt;
# Run program with --help to get a list of available options and with&lt;br /&gt;
# --print-defaults to see which it would actually understand and use.&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# This group is read both both by the client and the server&lt;br /&gt;
# use it for options that affect everything&lt;br /&gt;
#&lt;br /&gt;
[client-server]&lt;br /&gt;
&lt;br /&gt;
# Import all .cnf files from configuration directory&lt;br /&gt;
!includedir /etc/mysql/conf.d/&lt;br /&gt;
!includedir /etc/mysql/mariadb.conf.d/&lt;br /&gt;
&lt;br /&gt;
[mysqld]&lt;br /&gt;
event_scheduler=ON&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can check the status of the event schedule by running the query &amp;lt;code&amp;gt;SHOW VARIABLES LIKE 'event_scheduler';&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MariaDB [(none)]&amp;gt; SHOW VARIABLES LIKE 'event_scheduler';&lt;br /&gt;
+-----------------+-------+&lt;br /&gt;
| Variable_name   | Value |&lt;br /&gt;
+-----------------+-------+&lt;br /&gt;
| event_scheduler | ON    |&lt;br /&gt;
+-----------------+-------+&lt;br /&gt;
1 row in set (0.002 sec)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== User privileges ==&lt;br /&gt;
Check if your database user account has sufficient privileges by running &amp;lt;code&amp;gt;SHOW GRANTS FOR user@localhost;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can assign specific privilege for executing events by running:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;GRANT EVENT ON `your_database_name`.* TO 'your_username'@'localhost';&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;GRANT EXECUTE ON `your_database_name`.* TO 'your_username'@'localhost';&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Alternatively, you can assign all privileges to your database user account by running &amp;lt;code&amp;gt;GRANT ALL PRIVILEGES ON `your_database_name`.* TO 'your_username'@'localhost';&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In both cases, run additionally &amp;lt;code&amp;gt;FLUSH PRIVILEGES;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Restart of MariaDb ==&lt;br /&gt;
In the terminal, execute &amp;lt;code&amp;gt;sudo systemctl restart mariadb&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Status of MariaDb ==&lt;br /&gt;
In the terminal, execute &amp;lt;code&amp;gt;sudo systemctl status mariadb&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The output should look similar to the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
● mariadb.service - MariaDB 10.3.39 database server&lt;br /&gt;
   Loaded: loaded (/lib/systemd/system/mariadb.service; enabled; vendor preset: enabled)&lt;br /&gt;
   Active: active (running) since Sat 2025-08-02 08:00:18 CEST; 1h 14min ago&lt;br /&gt;
     Docs: man:mysqld(8)&lt;br /&gt;
           https://mariadb.com/kb/en/library/systemd/&lt;br /&gt;
  Process: 32624 ExecStartPre=/usr/bin/install -m 755 -o mysql -g root -d /var/run/mysqld (code=exited, status=0/SUCCESS)&lt;br /&gt;
  Process: 32625 ExecStartPre=/bin/sh -c systemctl unset-environment _WSREP_START_POSITION (code=exited, status=0/SUCCESS)&lt;br /&gt;
  Process: 32627 ExecStartPre=/bin/sh -c [ ! -e /usr/bin/galera_recovery ] &amp;amp;&amp;amp; VAR= ||   VAR=`cd /usr/bin/..; /usr/bin/galera_recovery`; [ $? -eq 0 ]   &amp;amp;&amp;amp; systemctl set-environment _WSREP_START_POSITION=$VAR || exit 1 (code=exited, status=0/SUCCESS)&lt;br /&gt;
  Process: 32717 ExecStartPost=/bin/sh -c systemctl unset-environment _WSREP_START_POSITION (code=exited, status=0/SUCCESS)&lt;br /&gt;
  Process: 32719 ExecStartPost=/etc/mysql/debian-start (code=exited, status=0/SUCCESS)&lt;br /&gt;
 Main PID: 32681 (mysqld)&lt;br /&gt;
   Status: &amp;quot;Taking your SQL requests now...&amp;quot;&lt;br /&gt;
    Tasks: 42 (limit: 1592)&lt;br /&gt;
   CGroup: /system.slice/mariadb.service&lt;br /&gt;
           └─32681 /usr/sbin/mysqld&lt;br /&gt;
&lt;br /&gt;
Aug 02 08:00:17 raspberrypi systemd[1]: Starting MariaDB 10.3.39 database server...&lt;br /&gt;
Aug 02 08:00:18 raspberrypi systemd[1]: Started MariaDB 10.3.39 database server.&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32721]: Upgrading MySQL tables if necessary.&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: /usr/bin/mysql_upgrade: the '--basedir' option is always ignored&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: Looking for 'mysql' as: /usr/bin/mysql&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: Looking for 'mysqlcheck' as: /usr/bin/mysqlcheck&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: This installation of MariaDB is already upgraded to 10.3.39-MariaDB.&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: There is no need to run mysql_upgrade again for 10.3.39-MariaDB.&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: You can use --force if you still want to run mysql_upgrade&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32732]: Checking for insecure root accounts.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Adding events ==&lt;br /&gt;
The repository of the database contains a script &amp;lt;code&amp;gt;aquariumcontrol_database_cleanup_setup.sh&amp;lt;/code&amp;gt;.&lt;br /&gt;
The script will set up the cleanup for the following tables:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Configuration of retention period&lt;br /&gt;
! table name !! default period in days&lt;br /&gt;
|-&lt;br /&gt;
| balling_log || 60&lt;br /&gt;
|-&lt;br /&gt;
| data || 30&lt;br /&gt;
|-&lt;br /&gt;
| feed_log || 90&lt;br /&gt;
|-&lt;br /&gt;
| refill_log || 60&lt;br /&gt;
|-&lt;br /&gt;
| heating_stats || 1000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== View events in MariaDb ==&lt;br /&gt;
You can display the configured events using &amp;lt;code&amp;gt;show events;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MariaDB [aquarium]&amp;gt; show events;&lt;br /&gt;
+----------+----------------------------------+--------------------+-----------+-----------+------------+----------------+----------------+---------------------+------+---------+------------+----------------------+----------------------+--------------------+&lt;br /&gt;
| Db       | Name                             | Definer            | Time zone | Type      | Execute at | Interval value | Interval field | Starts              | Ends | Status  | Originator | character_set_client | collation_connection | Database Collation |&lt;br /&gt;
+----------+----------------------------------+--------------------+-----------+-----------+------------+----------------+----------------+---------------------+------+---------+------------+----------------------+----------------------+--------------------+&lt;br /&gt;
| aquarium | cleanup_old_ballingdosinglog_60d | aquarium@localhost | SYSTEM    | RECURRING | NULL       | 1              | DAY            | 2025-08-02 07:57:19 | NULL | ENABLED |          1 | utf8mb4              | utf8mb4_general_ci   | utf8mb4_general_ci |&lt;br /&gt;
| aquarium | cleanup_old_data_30d             | aquarium@localhost | SYSTEM    | RECURRING | NULL       | 1              | DAY            | 2025-08-02 07:57:19 | NULL | ENABLED |          1 | utf8mb4              | utf8mb4_general_ci   | utf8mb4_general_ci |&lt;br /&gt;
| aquarium | cleanup_old_feedlog_90d          | aquarium@localhost | SYSTEM    | RECURRING | NULL       | 1              | DAY            | 2025-08-02 07:57:19 | NULL | ENABLED |          1 | utf8mb4              | utf8mb4_general_ci   | utf8mb4_general_ci |&lt;br /&gt;
| aquarium | cleanup_old_refill_60d           | aquarium@localhost | SYSTEM    | RECURRING | NULL       | 1              | DAY            | 2025-08-02 07:57:19 | NULL | ENABLED |          1 | utf8mb4              | utf8mb4_general_ci   | utf8mb4_general_ci |&lt;br /&gt;
+----------+----------------------------------+--------------------+-----------+-----------+------------+----------------+----------------+---------------------+------+---------+------------+----------------------+----------------------+--------------------+&lt;br /&gt;
4 rows in set (0.002 sec)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Event-based_deletion_of_old_database_entries&amp;diff=541</id>
		<title>Event-based deletion of old database entries</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Event-based_deletion_of_old_database_entries&amp;diff=541"/>
		<updated>2026-07-24T05:41:05Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Adding events */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Problem description ==&lt;br /&gt;
Over time, the size of the following four tables grows: &amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ballingdosinglog&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;feedlog&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can query the size in the database with the following commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MariaDB [aquarium]&amp;gt; select count(*) from refill;&lt;br /&gt;
+----------+&lt;br /&gt;
| count(*) |&lt;br /&gt;
+----------+&lt;br /&gt;
|      689 |&lt;br /&gt;
+----------+&lt;br /&gt;
1 row in set (0.001 sec)&lt;br /&gt;
&lt;br /&gt;
MariaDB [aquarium]&amp;gt; select count(*) from ballingdosinglog;&lt;br /&gt;
+----------+&lt;br /&gt;
| count(*) |&lt;br /&gt;
+----------+&lt;br /&gt;
|    13362 |&lt;br /&gt;
+----------+&lt;br /&gt;
1 row in set (0.018 sec)&lt;br /&gt;
&lt;br /&gt;
MariaDB [aquarium]&amp;gt; select count(*) from feedlog;&lt;br /&gt;
+----------+&lt;br /&gt;
| count(*) |&lt;br /&gt;
+----------+&lt;br /&gt;
|      263 |&lt;br /&gt;
+----------+&lt;br /&gt;
1 row in set (0.001 sec)&lt;br /&gt;
&lt;br /&gt;
MariaDB [aquarium]&amp;gt; select count(*) from data;&lt;br /&gt;
+----------+&lt;br /&gt;
| count(*) |&lt;br /&gt;
+----------+&lt;br /&gt;
|   213043 |&lt;br /&gt;
+----------+&lt;br /&gt;
1 row in set (0.166 sec)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Excessive database size impacts the performance and hence the stability of the aquarium control.&lt;br /&gt;
For this reason, the size needs to be limited by deleting old entries.&lt;br /&gt;
&lt;br /&gt;
== Configuration of MariaDb ==&lt;br /&gt;
Activate the event scheduler by adding the two last lines of below example to MariaDb configuration file (usually &amp;lt;code&amp;gt;/etc/mysql/my.cnf&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;/etc/mysql/mariadb.conf.d/50-server.cnf&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# The MariaDB configuration file&lt;br /&gt;
#&lt;br /&gt;
# The MariaDB/MySQL tools read configuration files in the following order:&lt;br /&gt;
# 1. &amp;quot;/etc/mysql/mariadb.cnf&amp;quot; (this file) to set global defaults,&lt;br /&gt;
# 2. &amp;quot;/etc/mysql/conf.d/*.cnf&amp;quot; to set global options.&lt;br /&gt;
# 3. &amp;quot;/etc/mysql/mariadb.conf.d/*.cnf&amp;quot; to set MariaDB-only options.&lt;br /&gt;
# 4. &amp;quot;~/.my.cnf&amp;quot; to set user-specific options.&lt;br /&gt;
#&lt;br /&gt;
# If the same option is defined multiple times, the last one will apply.&lt;br /&gt;
#&lt;br /&gt;
# One can use all long options that the program supports.&lt;br /&gt;
# Run program with --help to get a list of available options and with&lt;br /&gt;
# --print-defaults to see which it would actually understand and use.&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# This group is read both both by the client and the server&lt;br /&gt;
# use it for options that affect everything&lt;br /&gt;
#&lt;br /&gt;
[client-server]&lt;br /&gt;
&lt;br /&gt;
# Import all .cnf files from configuration directory&lt;br /&gt;
!includedir /etc/mysql/conf.d/&lt;br /&gt;
!includedir /etc/mysql/mariadb.conf.d/&lt;br /&gt;
&lt;br /&gt;
[mysqld]&lt;br /&gt;
event_scheduler=ON&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can check the status of the event schedule by running the query &amp;lt;code&amp;gt;SHOW VARIABLES LIKE 'event_scheduler';&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MariaDB [(none)]&amp;gt; SHOW VARIABLES LIKE 'event_scheduler';&lt;br /&gt;
+-----------------+-------+&lt;br /&gt;
| Variable_name   | Value |&lt;br /&gt;
+-----------------+-------+&lt;br /&gt;
| event_scheduler | ON    |&lt;br /&gt;
+-----------------+-------+&lt;br /&gt;
1 row in set (0.002 sec)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== User privileges ==&lt;br /&gt;
Check if your database user account has sufficient privileges by running &amp;lt;code&amp;gt;show grants for user@localhost;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can assign specific privilege for executing events by running &amp;lt;code&amp;gt;GRANT ALL PRIVILEGES ON `your_database_name`.* TO 'your_username'@'localhost';&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Alternatively, you can assign all privileges to your database user account by running &amp;lt;code&amp;gt;GRANT ALL PRIVILEGES ON `your_database_name`.* TO 'your_username'@'localhost';&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In both cases, run additionally &amp;lt;code&amp;gt;FLUSH PRIVILEGES;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Restart of MariaDb ==&lt;br /&gt;
In the terminal, execute &amp;lt;code&amp;gt;sudo systemctl restart mariadb&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Status of MariaDb ==&lt;br /&gt;
In the terminal, execute &amp;lt;code&amp;gt;sudo systemctl status mariadb&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The output should look similar to the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
● mariadb.service - MariaDB 10.3.39 database server&lt;br /&gt;
   Loaded: loaded (/lib/systemd/system/mariadb.service; enabled; vendor preset: enabled)&lt;br /&gt;
   Active: active (running) since Sat 2025-08-02 08:00:18 CEST; 1h 14min ago&lt;br /&gt;
     Docs: man:mysqld(8)&lt;br /&gt;
           https://mariadb.com/kb/en/library/systemd/&lt;br /&gt;
  Process: 32624 ExecStartPre=/usr/bin/install -m 755 -o mysql -g root -d /var/run/mysqld (code=exited, status=0/SUCCESS)&lt;br /&gt;
  Process: 32625 ExecStartPre=/bin/sh -c systemctl unset-environment _WSREP_START_POSITION (code=exited, status=0/SUCCESS)&lt;br /&gt;
  Process: 32627 ExecStartPre=/bin/sh -c [ ! -e /usr/bin/galera_recovery ] &amp;amp;&amp;amp; VAR= ||   VAR=`cd /usr/bin/..; /usr/bin/galera_recovery`; [ $? -eq 0 ]   &amp;amp;&amp;amp; systemctl set-environment _WSREP_START_POSITION=$VAR || exit 1 (code=exited, status=0/SUCCESS)&lt;br /&gt;
  Process: 32717 ExecStartPost=/bin/sh -c systemctl unset-environment _WSREP_START_POSITION (code=exited, status=0/SUCCESS)&lt;br /&gt;
  Process: 32719 ExecStartPost=/etc/mysql/debian-start (code=exited, status=0/SUCCESS)&lt;br /&gt;
 Main PID: 32681 (mysqld)&lt;br /&gt;
   Status: &amp;quot;Taking your SQL requests now...&amp;quot;&lt;br /&gt;
    Tasks: 42 (limit: 1592)&lt;br /&gt;
   CGroup: /system.slice/mariadb.service&lt;br /&gt;
           └─32681 /usr/sbin/mysqld&lt;br /&gt;
&lt;br /&gt;
Aug 02 08:00:17 raspberrypi systemd[1]: Starting MariaDB 10.3.39 database server...&lt;br /&gt;
Aug 02 08:00:18 raspberrypi systemd[1]: Started MariaDB 10.3.39 database server.&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32721]: Upgrading MySQL tables if necessary.&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: /usr/bin/mysql_upgrade: the '--basedir' option is always ignored&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: Looking for 'mysql' as: /usr/bin/mysql&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: Looking for 'mysqlcheck' as: /usr/bin/mysqlcheck&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: This installation of MariaDB is already upgraded to 10.3.39-MariaDB.&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: There is no need to run mysql_upgrade again for 10.3.39-MariaDB.&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: You can use --force if you still want to run mysql_upgrade&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32732]: Checking for insecure root accounts.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Adding events ==&lt;br /&gt;
The repository of the database contains a script &amp;lt;code&amp;gt;aquariumcontrol_database_cleanup_setup.sh&amp;lt;/code&amp;gt;.&lt;br /&gt;
The script will set up the cleanup for the following tables:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Configuration of retention period&lt;br /&gt;
! table name !! default period in days&lt;br /&gt;
|-&lt;br /&gt;
| balling_log || 60&lt;br /&gt;
|-&lt;br /&gt;
| data || 30&lt;br /&gt;
|-&lt;br /&gt;
| feed_log || 90&lt;br /&gt;
|-&lt;br /&gt;
| refill_log || 60&lt;br /&gt;
|-&lt;br /&gt;
| heating_stats || 1000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== View events in MariaDb ==&lt;br /&gt;
You can display the configured events using &amp;lt;code&amp;gt;show events;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MariaDB [aquarium]&amp;gt; show events;&lt;br /&gt;
+----------+----------------------------------+--------------------+-----------+-----------+------------+----------------+----------------+---------------------+------+---------+------------+----------------------+----------------------+--------------------+&lt;br /&gt;
| Db       | Name                             | Definer            | Time zone | Type      | Execute at | Interval value | Interval field | Starts              | Ends | Status  | Originator | character_set_client | collation_connection | Database Collation |&lt;br /&gt;
+----------+----------------------------------+--------------------+-----------+-----------+------------+----------------+----------------+---------------------+------+---------+------------+----------------------+----------------------+--------------------+&lt;br /&gt;
| aquarium | cleanup_old_ballingdosinglog_60d | aquarium@localhost | SYSTEM    | RECURRING | NULL       | 1              | DAY            | 2025-08-02 07:57:19 | NULL | ENABLED |          1 | utf8mb4              | utf8mb4_general_ci   | utf8mb4_general_ci |&lt;br /&gt;
| aquarium | cleanup_old_data_30d             | aquarium@localhost | SYSTEM    | RECURRING | NULL       | 1              | DAY            | 2025-08-02 07:57:19 | NULL | ENABLED |          1 | utf8mb4              | utf8mb4_general_ci   | utf8mb4_general_ci |&lt;br /&gt;
| aquarium | cleanup_old_feedlog_90d          | aquarium@localhost | SYSTEM    | RECURRING | NULL       | 1              | DAY            | 2025-08-02 07:57:19 | NULL | ENABLED |          1 | utf8mb4              | utf8mb4_general_ci   | utf8mb4_general_ci |&lt;br /&gt;
| aquarium | cleanup_old_refill_60d           | aquarium@localhost | SYSTEM    | RECURRING | NULL       | 1              | DAY            | 2025-08-02 07:57:19 | NULL | ENABLED |          1 | utf8mb4              | utf8mb4_general_ci   | utf8mb4_general_ci |&lt;br /&gt;
+----------+----------------------------------+--------------------+-----------+-----------+------------+----------------+----------------+---------------------+------+---------+------------+----------------------+----------------------+--------------------+&lt;br /&gt;
4 rows in set (0.002 sec)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Event-based_deletion_of_old_database_entries&amp;diff=540</id>
		<title>Event-based deletion of old database entries</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Event-based_deletion_of_old_database_entries&amp;diff=540"/>
		<updated>2026-07-24T05:38:52Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Configuration of MariaDb */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Problem description ==&lt;br /&gt;
Over time, the size of the following four tables grows: &amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ballingdosinglog&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;feedlog&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can query the size in the database with the following commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MariaDB [aquarium]&amp;gt; select count(*) from refill;&lt;br /&gt;
+----------+&lt;br /&gt;
| count(*) |&lt;br /&gt;
+----------+&lt;br /&gt;
|      689 |&lt;br /&gt;
+----------+&lt;br /&gt;
1 row in set (0.001 sec)&lt;br /&gt;
&lt;br /&gt;
MariaDB [aquarium]&amp;gt; select count(*) from ballingdosinglog;&lt;br /&gt;
+----------+&lt;br /&gt;
| count(*) |&lt;br /&gt;
+----------+&lt;br /&gt;
|    13362 |&lt;br /&gt;
+----------+&lt;br /&gt;
1 row in set (0.018 sec)&lt;br /&gt;
&lt;br /&gt;
MariaDB [aquarium]&amp;gt; select count(*) from feedlog;&lt;br /&gt;
+----------+&lt;br /&gt;
| count(*) |&lt;br /&gt;
+----------+&lt;br /&gt;
|      263 |&lt;br /&gt;
+----------+&lt;br /&gt;
1 row in set (0.001 sec)&lt;br /&gt;
&lt;br /&gt;
MariaDB [aquarium]&amp;gt; select count(*) from data;&lt;br /&gt;
+----------+&lt;br /&gt;
| count(*) |&lt;br /&gt;
+----------+&lt;br /&gt;
|   213043 |&lt;br /&gt;
+----------+&lt;br /&gt;
1 row in set (0.166 sec)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Excessive database size impacts the performance and hence the stability of the aquarium control.&lt;br /&gt;
For this reason, the size needs to be limited by deleting old entries.&lt;br /&gt;
&lt;br /&gt;
== Configuration of MariaDb ==&lt;br /&gt;
Activate the event scheduler by adding the two last lines of below example to MariaDb configuration file (usually &amp;lt;code&amp;gt;/etc/mysql/my.cnf&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;/etc/mysql/mariadb.conf.d/50-server.cnf&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# The MariaDB configuration file&lt;br /&gt;
#&lt;br /&gt;
# The MariaDB/MySQL tools read configuration files in the following order:&lt;br /&gt;
# 1. &amp;quot;/etc/mysql/mariadb.cnf&amp;quot; (this file) to set global defaults,&lt;br /&gt;
# 2. &amp;quot;/etc/mysql/conf.d/*.cnf&amp;quot; to set global options.&lt;br /&gt;
# 3. &amp;quot;/etc/mysql/mariadb.conf.d/*.cnf&amp;quot; to set MariaDB-only options.&lt;br /&gt;
# 4. &amp;quot;~/.my.cnf&amp;quot; to set user-specific options.&lt;br /&gt;
#&lt;br /&gt;
# If the same option is defined multiple times, the last one will apply.&lt;br /&gt;
#&lt;br /&gt;
# One can use all long options that the program supports.&lt;br /&gt;
# Run program with --help to get a list of available options and with&lt;br /&gt;
# --print-defaults to see which it would actually understand and use.&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# This group is read both both by the client and the server&lt;br /&gt;
# use it for options that affect everything&lt;br /&gt;
#&lt;br /&gt;
[client-server]&lt;br /&gt;
&lt;br /&gt;
# Import all .cnf files from configuration directory&lt;br /&gt;
!includedir /etc/mysql/conf.d/&lt;br /&gt;
!includedir /etc/mysql/mariadb.conf.d/&lt;br /&gt;
&lt;br /&gt;
[mysqld]&lt;br /&gt;
event_scheduler=ON&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can check the status of the event schedule by running the query &amp;lt;code&amp;gt;SHOW VARIABLES LIKE 'event_scheduler';&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MariaDB [(none)]&amp;gt; SHOW VARIABLES LIKE 'event_scheduler';&lt;br /&gt;
+-----------------+-------+&lt;br /&gt;
| Variable_name   | Value |&lt;br /&gt;
+-----------------+-------+&lt;br /&gt;
| event_scheduler | ON    |&lt;br /&gt;
+-----------------+-------+&lt;br /&gt;
1 row in set (0.002 sec)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== User privileges ==&lt;br /&gt;
Check if your database user account has sufficient privileges by running &amp;lt;code&amp;gt;show grants for user@localhost;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can assign specific privilege for executing events by running &amp;lt;code&amp;gt;GRANT ALL PRIVILEGES ON `your_database_name`.* TO 'your_username'@'localhost';&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Alternatively, you can assign all privileges to your database user account by running &amp;lt;code&amp;gt;GRANT ALL PRIVILEGES ON `your_database_name`.* TO 'your_username'@'localhost';&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In both cases, run additionally &amp;lt;code&amp;gt;FLUSH PRIVILEGES;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Restart of MariaDb ==&lt;br /&gt;
In the terminal, execute &amp;lt;code&amp;gt;sudo systemctl restart mariadb&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Status of MariaDb ==&lt;br /&gt;
In the terminal, execute &amp;lt;code&amp;gt;sudo systemctl status mariadb&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The output should look similar to the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
● mariadb.service - MariaDB 10.3.39 database server&lt;br /&gt;
   Loaded: loaded (/lib/systemd/system/mariadb.service; enabled; vendor preset: enabled)&lt;br /&gt;
   Active: active (running) since Sat 2025-08-02 08:00:18 CEST; 1h 14min ago&lt;br /&gt;
     Docs: man:mysqld(8)&lt;br /&gt;
           https://mariadb.com/kb/en/library/systemd/&lt;br /&gt;
  Process: 32624 ExecStartPre=/usr/bin/install -m 755 -o mysql -g root -d /var/run/mysqld (code=exited, status=0/SUCCESS)&lt;br /&gt;
  Process: 32625 ExecStartPre=/bin/sh -c systemctl unset-environment _WSREP_START_POSITION (code=exited, status=0/SUCCESS)&lt;br /&gt;
  Process: 32627 ExecStartPre=/bin/sh -c [ ! -e /usr/bin/galera_recovery ] &amp;amp;&amp;amp; VAR= ||   VAR=`cd /usr/bin/..; /usr/bin/galera_recovery`; [ $? -eq 0 ]   &amp;amp;&amp;amp; systemctl set-environment _WSREP_START_POSITION=$VAR || exit 1 (code=exited, status=0/SUCCESS)&lt;br /&gt;
  Process: 32717 ExecStartPost=/bin/sh -c systemctl unset-environment _WSREP_START_POSITION (code=exited, status=0/SUCCESS)&lt;br /&gt;
  Process: 32719 ExecStartPost=/etc/mysql/debian-start (code=exited, status=0/SUCCESS)&lt;br /&gt;
 Main PID: 32681 (mysqld)&lt;br /&gt;
   Status: &amp;quot;Taking your SQL requests now...&amp;quot;&lt;br /&gt;
    Tasks: 42 (limit: 1592)&lt;br /&gt;
   CGroup: /system.slice/mariadb.service&lt;br /&gt;
           └─32681 /usr/sbin/mysqld&lt;br /&gt;
&lt;br /&gt;
Aug 02 08:00:17 raspberrypi systemd[1]: Starting MariaDB 10.3.39 database server...&lt;br /&gt;
Aug 02 08:00:18 raspberrypi systemd[1]: Started MariaDB 10.3.39 database server.&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32721]: Upgrading MySQL tables if necessary.&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: /usr/bin/mysql_upgrade: the '--basedir' option is always ignored&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: Looking for 'mysql' as: /usr/bin/mysql&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: Looking for 'mysqlcheck' as: /usr/bin/mysqlcheck&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: This installation of MariaDB is already upgraded to 10.3.39-MariaDB.&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: There is no need to run mysql_upgrade again for 10.3.39-MariaDB.&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32724]: You can use --force if you still want to run mysql_upgrade&lt;br /&gt;
Aug 02 08:00:18 raspberrypi /etc/mysql/debian-start[32732]: Checking for insecure root accounts.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Adding events ==&lt;br /&gt;
The repository of the database contains a script &amp;lt;code&amp;gt;database_cleanup_setup.sh&amp;lt;/code&amp;gt;.&lt;br /&gt;
The script will set up the cleanup for the following tables:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Configuration of retention period&lt;br /&gt;
! table name !! default period in days&lt;br /&gt;
|-&lt;br /&gt;
| balling_log || 60&lt;br /&gt;
|-&lt;br /&gt;
| data || 30&lt;br /&gt;
|-&lt;br /&gt;
| feed_log || 90&lt;br /&gt;
|-&lt;br /&gt;
| refill_log || 60&lt;br /&gt;
|-&lt;br /&gt;
| heating_stats || 1000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== View events in MariaDb ==&lt;br /&gt;
You can display the configured events using &amp;lt;code&amp;gt;show events;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MariaDB [aquarium]&amp;gt; show events;&lt;br /&gt;
+----------+----------------------------------+--------------------+-----------+-----------+------------+----------------+----------------+---------------------+------+---------+------------+----------------------+----------------------+--------------------+&lt;br /&gt;
| Db       | Name                             | Definer            | Time zone | Type      | Execute at | Interval value | Interval field | Starts              | Ends | Status  | Originator | character_set_client | collation_connection | Database Collation |&lt;br /&gt;
+----------+----------------------------------+--------------------+-----------+-----------+------------+----------------+----------------+---------------------+------+---------+------------+----------------------+----------------------+--------------------+&lt;br /&gt;
| aquarium | cleanup_old_ballingdosinglog_60d | aquarium@localhost | SYSTEM    | RECURRING | NULL       | 1              | DAY            | 2025-08-02 07:57:19 | NULL | ENABLED |          1 | utf8mb4              | utf8mb4_general_ci   | utf8mb4_general_ci |&lt;br /&gt;
| aquarium | cleanup_old_data_30d             | aquarium@localhost | SYSTEM    | RECURRING | NULL       | 1              | DAY            | 2025-08-02 07:57:19 | NULL | ENABLED |          1 | utf8mb4              | utf8mb4_general_ci   | utf8mb4_general_ci |&lt;br /&gt;
| aquarium | cleanup_old_feedlog_90d          | aquarium@localhost | SYSTEM    | RECURRING | NULL       | 1              | DAY            | 2025-08-02 07:57:19 | NULL | ENABLED |          1 | utf8mb4              | utf8mb4_general_ci   | utf8mb4_general_ci |&lt;br /&gt;
| aquarium | cleanup_old_refill_60d           | aquarium@localhost | SYSTEM    | RECURRING | NULL       | 1              | DAY            | 2025-08-02 07:57:19 | NULL | ENABLED |          1 | utf8mb4              | utf8mb4_general_ci   | utf8mb4_general_ci |&lt;br /&gt;
+----------+----------------------------------+--------------------+-----------+-----------+------------+----------------+----------------+---------------------+------+---------+------------+----------------------+----------------------+--------------------+&lt;br /&gt;
4 rows in set (0.002 sec)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=SQL_database&amp;diff=539</id>
		<title>SQL database</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=SQL_database&amp;diff=539"/>
		<updated>2026-07-18T17:08:31Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Configuration of connection timeout */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The control application uses a MySQL database for persistent storing of states and logging of activities as well as storage of input data.&lt;br /&gt;
&lt;br /&gt;
The SQL database is also the main interface between the control application and the outside world (webpage, Apps).&lt;br /&gt;
&lt;br /&gt;
The empty SQL dump of the databases are stored in the bitbucket repository:&lt;br /&gt;
git clone https://in-dubio@bitbucket.org/in-dubio/aquarium-database.git&lt;br /&gt;
&lt;br /&gt;
As of December 2024, the databases are empty.&lt;br /&gt;
The databases for the tests are emptied and filled programmatically by the test cases before the execution of each test case.&lt;br /&gt;
The high number of test databases shall allow maximum parallelisation of the test case execution which takes several minutes.&lt;br /&gt;
&lt;br /&gt;
After cloning the database repository, first create the test databases with the statement provided in &amp;lt;code&amp;gt;create_databases.sql&amp;lt;/code&amp;gt;.&lt;br /&gt;
You might want to consider using different accounts for the databases between normal operation and test execution. The account data (user and password) are stated in the .toml configuration files.&lt;br /&gt;
&lt;br /&gt;
Second, create the user(s): &amp;lt;code&amp;gt;CREATE USER aquarium@localhost;&amp;lt;/code&amp;gt;&lt;br /&gt;
Set a password for the user with &amp;lt;code&amp;gt;ALTER USER 'aquarium'@'localhost' IDENTIFIED BY 'password';&amp;lt;/code&amp;gt;&lt;br /&gt;
The combination of user and password in the .toml configuration file(s) must match with the provided password.&lt;br /&gt;
&lt;br /&gt;
Next, import the database dumps into the database using &amp;lt;code&amp;gt;import_databases.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then, grant the access to the user(s) using &amp;lt;code&amp;gt;grant_access_rights.sql&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For operating the control, the SQL database must include the Balling dosing configuration of those pumps which are activated in the .toml configuration file.&lt;br /&gt;
The table &amp;lt;code&amp;gt;ballingsetvals&amp;lt;/code&amp;gt; for the configuration of the Balling dosing pumps has the following structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
+--------------+-------------+------+-----+---------+-------+&lt;br /&gt;
| Field        | Type        | Null | Key | Default | Extra |&lt;br /&gt;
+--------------+-------------+------+-----+---------+-------+&lt;br /&gt;
| pumpid       | int(11)     | NO   |     | NULL    |       |&lt;br /&gt;
| dosingspeed  | float       | NO   |     | NULL    |       |&lt;br /&gt;
| dosingvolume | float       | NO   |     | NULL    |       |&lt;br /&gt;
| label        | varchar(10) | NO   |     | NULL    |       |&lt;br /&gt;
+--------------+-------------+------+-----+---------+-------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can insert values as follows:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
INSERT INTO ballingsetvals VALUES(1, 1.0, 0.5, &amp;quot;KH&amp;quot;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For stable operation of the database in the longterm, it is paramount to limit the database size.&lt;br /&gt;
This requires deletion of old entries. MariaDb offers the functionality of event scheduling.&lt;br /&gt;
The [[Event-based deletion of old database entries|event based deletion of old database entries]] needs to be setup by the user.&lt;br /&gt;
&lt;br /&gt;
== Script-based setup of the database ==&lt;br /&gt;
The repository provides a script which conducts terminal-based communication with the user to fill the database.&lt;br /&gt;
&lt;br /&gt;
=== Credentials for script-based setup of the database ===&lt;br /&gt;
Optionally, the user can provide credentials for accessing the database (by default, root privileges allow modification of all MariaDB databases).&lt;br /&gt;
&lt;br /&gt;
The script checks if the provided credentials are valid.&lt;br /&gt;
&lt;br /&gt;
=== Script-based setup of the user data ===&lt;br /&gt;
Optionally, the user can setup the user configuration in the table &amp;lt;code&amp;gt;users&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The script offers the user the possibility to erase all existing user data.&lt;br /&gt;
&lt;br /&gt;
The script repeatedly asks the user if he wishes to add user data.&lt;br /&gt;
&lt;br /&gt;
For each entry that is added in the &amp;lt;code&amp;gt;users&amp;lt;/code&amp;gt; table, the script asks the name stored in the column &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The name must comply with a simple pattern: Only letters a-z and numbers are allowed - no special characters, no space.&lt;br /&gt;
&lt;br /&gt;
If the name already exists, the script asks the user if he wishes to overwrite the data.&lt;br /&gt;
&lt;br /&gt;
The script asks for the password that shall be used for the entry in the table.&lt;br /&gt;
The script stores a &amp;lt;code&amp;gt;bcrypt&amp;lt;/code&amp;gt; hash in the column &amp;lt;code&amp;gt;password&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Script-based setup of the Balling dosing configuration ===&lt;br /&gt;
Optionally, the user can setup the Balling mineral dosing configuration in the table &amp;lt;code&amp;gt;balling_set_vals&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The script offers the user the possibility to erase all existing Balling dosing configuration.&lt;br /&gt;
&lt;br /&gt;
The user can modify any pump configuration identified by the pump id which ranges from one to four.&lt;br /&gt;
&lt;br /&gt;
Alternatively, the user can leave the configuration of a specific pump empty.&lt;br /&gt;
&lt;br /&gt;
If there is an existing entry in the balling dosing configuration, the script asks the user if he wishes to overwrite this entry.&lt;br /&gt;
&lt;br /&gt;
=== Script-based setup of the heating set values ===&lt;br /&gt;
Optionally, the user can setup the heating control in the table &amp;lt;code&amp;gt;heating_set_vals&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is existing data in the table, the script asks the user if the data shall be erased. &lt;br /&gt;
&lt;br /&gt;
The script asks the user for the temperature value to switch off the heating stored in the column &amp;lt;code&amp;gt;heating_switch_off_temp&amp;lt;/code&amp;gt; and the temperature value to switch on the heating stored in the column &amp;lt;code&amp;gt;heating_switch_on_temp&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The script checks if the value provided by the user for &amp;lt;code&amp;gt;heating_switch_off_temp&amp;lt;/code&amp;gt; is higher than the value provided by the user for &amp;lt;code&amp;gt;heating_switch_on_temp&amp;lt;/code&amp;gt;. If this is not the case, the script repeats the sequence for entering the data.&lt;br /&gt;
&lt;br /&gt;
=== Script-based setup of the ventilation set values ===&lt;br /&gt;
Optionally, the user can setup the ventilation control in the table &amp;lt;code&amp;gt;ventilation_set_vals&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is existing data in the table, the script asks the user if the data shall be erased. &lt;br /&gt;
&lt;br /&gt;
The script asks the user for the temperature value to switch off the ventilation stored in the column &amp;lt;code&amp;gt;ventilation_switch_off_temp&amp;lt;/code&amp;gt; and the temperature value to switch on the ventilation stored in the column &amp;lt;code&amp;gt;ventilation_switch_on_temp&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The script checks if the value provided by the user for &amp;lt;code&amp;gt;ventilation_switch_off_temp&amp;lt;/code&amp;gt; is lower than the value provided by the user for &amp;lt;code&amp;gt;ventilation_switch_on_temp&amp;lt;/code&amp;gt;. If this is not the case, the script repeats the sequence for entering the data.&lt;br /&gt;
&lt;br /&gt;
=== Script-based setup of the actuator schedule ===&lt;br /&gt;
Optionally, the user can request to setup a default actuator schedule in the table &amp;lt;code&amp;gt;schedule&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is existing data in the table, the script asks the user if the data shall be erased.&lt;br /&gt;
&lt;br /&gt;
The script creates the following entries:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Default actuator schedule&lt;br /&gt;
|-&lt;br /&gt;
! schedule_type !! start_time !! stop_time !! is_active&lt;br /&gt;
|-&lt;br /&gt;
| balling || 0:00 || 23:59 || 0&lt;br /&gt;
|-&lt;br /&gt;
| refill || 0:00 || 23:59 || 0&lt;br /&gt;
|-&lt;br /&gt;
| ventilation || 0:00 || 23:59 || 0&lt;br /&gt;
|-&lt;br /&gt;
| heating || 0:00 || 23:59 || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Configuration of connection timeout ===&lt;br /&gt;
Edit the file &amp;lt;code&amp;gt;/etc/mysql/mariadb.conf.d/50-server.cnf&amp;lt;/code&amp;gt; and add &amp;lt;code&amp;gt;wait_timeout = 10000&amp;lt;/code&amp;gt; at the bottom of the &amp;lt;code&amp;gt;[mysqld]&amp;lt;/code&amp;gt; block.&lt;br /&gt;
&lt;br /&gt;
Restart the database afterwards with &amp;lt;code&amp;gt;sudo systemctl restart mariadb&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This avoids connection timeouts. The application checks this configuration during startup.&lt;br /&gt;
&lt;br /&gt;
You can verify the change with the SQL command &amp;lt;code&amp;gt;SHOW VARIABLES LIKE '%timeout%';&amp;lt;/code&amp;gt;.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=SQL_database&amp;diff=538</id>
		<title>SQL database</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=SQL_database&amp;diff=538"/>
		<updated>2026-07-18T17:07:02Z</updated>

		<summary type="html">&lt;p&gt;Uwe: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The control application uses a MySQL database for persistent storing of states and logging of activities as well as storage of input data.&lt;br /&gt;
&lt;br /&gt;
The SQL database is also the main interface between the control application and the outside world (webpage, Apps).&lt;br /&gt;
&lt;br /&gt;
The empty SQL dump of the databases are stored in the bitbucket repository:&lt;br /&gt;
git clone https://in-dubio@bitbucket.org/in-dubio/aquarium-database.git&lt;br /&gt;
&lt;br /&gt;
As of December 2024, the databases are empty.&lt;br /&gt;
The databases for the tests are emptied and filled programmatically by the test cases before the execution of each test case.&lt;br /&gt;
The high number of test databases shall allow maximum parallelisation of the test case execution which takes several minutes.&lt;br /&gt;
&lt;br /&gt;
After cloning the database repository, first create the test databases with the statement provided in &amp;lt;code&amp;gt;create_databases.sql&amp;lt;/code&amp;gt;.&lt;br /&gt;
You might want to consider using different accounts for the databases between normal operation and test execution. The account data (user and password) are stated in the .toml configuration files.&lt;br /&gt;
&lt;br /&gt;
Second, create the user(s): &amp;lt;code&amp;gt;CREATE USER aquarium@localhost;&amp;lt;/code&amp;gt;&lt;br /&gt;
Set a password for the user with &amp;lt;code&amp;gt;ALTER USER 'aquarium'@'localhost' IDENTIFIED BY 'password';&amp;lt;/code&amp;gt;&lt;br /&gt;
The combination of user and password in the .toml configuration file(s) must match with the provided password.&lt;br /&gt;
&lt;br /&gt;
Next, import the database dumps into the database using &amp;lt;code&amp;gt;import_databases.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then, grant the access to the user(s) using &amp;lt;code&amp;gt;grant_access_rights.sql&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For operating the control, the SQL database must include the Balling dosing configuration of those pumps which are activated in the .toml configuration file.&lt;br /&gt;
The table &amp;lt;code&amp;gt;ballingsetvals&amp;lt;/code&amp;gt; for the configuration of the Balling dosing pumps has the following structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
+--------------+-------------+------+-----+---------+-------+&lt;br /&gt;
| Field        | Type        | Null | Key | Default | Extra |&lt;br /&gt;
+--------------+-------------+------+-----+---------+-------+&lt;br /&gt;
| pumpid       | int(11)     | NO   |     | NULL    |       |&lt;br /&gt;
| dosingspeed  | float       | NO   |     | NULL    |       |&lt;br /&gt;
| dosingvolume | float       | NO   |     | NULL    |       |&lt;br /&gt;
| label        | varchar(10) | NO   |     | NULL    |       |&lt;br /&gt;
+--------------+-------------+------+-----+---------+-------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can insert values as follows:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
INSERT INTO ballingsetvals VALUES(1, 1.0, 0.5, &amp;quot;KH&amp;quot;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For stable operation of the database in the longterm, it is paramount to limit the database size.&lt;br /&gt;
This requires deletion of old entries. MariaDb offers the functionality of event scheduling.&lt;br /&gt;
The [[Event-based deletion of old database entries|event based deletion of old database entries]] needs to be setup by the user.&lt;br /&gt;
&lt;br /&gt;
== Script-based setup of the database ==&lt;br /&gt;
The repository provides a script which conducts terminal-based communication with the user to fill the database.&lt;br /&gt;
&lt;br /&gt;
=== Credentials for script-based setup of the database ===&lt;br /&gt;
Optionally, the user can provide credentials for accessing the database (by default, root privileges allow modification of all MariaDB databases).&lt;br /&gt;
&lt;br /&gt;
The script checks if the provided credentials are valid.&lt;br /&gt;
&lt;br /&gt;
=== Script-based setup of the user data ===&lt;br /&gt;
Optionally, the user can setup the user configuration in the table &amp;lt;code&amp;gt;users&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The script offers the user the possibility to erase all existing user data.&lt;br /&gt;
&lt;br /&gt;
The script repeatedly asks the user if he wishes to add user data.&lt;br /&gt;
&lt;br /&gt;
For each entry that is added in the &amp;lt;code&amp;gt;users&amp;lt;/code&amp;gt; table, the script asks the name stored in the column &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The name must comply with a simple pattern: Only letters a-z and numbers are allowed - no special characters, no space.&lt;br /&gt;
&lt;br /&gt;
If the name already exists, the script asks the user if he wishes to overwrite the data.&lt;br /&gt;
&lt;br /&gt;
The script asks for the password that shall be used for the entry in the table.&lt;br /&gt;
The script stores a &amp;lt;code&amp;gt;bcrypt&amp;lt;/code&amp;gt; hash in the column &amp;lt;code&amp;gt;password&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Script-based setup of the Balling dosing configuration ===&lt;br /&gt;
Optionally, the user can setup the Balling mineral dosing configuration in the table &amp;lt;code&amp;gt;balling_set_vals&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The script offers the user the possibility to erase all existing Balling dosing configuration.&lt;br /&gt;
&lt;br /&gt;
The user can modify any pump configuration identified by the pump id which ranges from one to four.&lt;br /&gt;
&lt;br /&gt;
Alternatively, the user can leave the configuration of a specific pump empty.&lt;br /&gt;
&lt;br /&gt;
If there is an existing entry in the balling dosing configuration, the script asks the user if he wishes to overwrite this entry.&lt;br /&gt;
&lt;br /&gt;
=== Script-based setup of the heating set values ===&lt;br /&gt;
Optionally, the user can setup the heating control in the table &amp;lt;code&amp;gt;heating_set_vals&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is existing data in the table, the script asks the user if the data shall be erased. &lt;br /&gt;
&lt;br /&gt;
The script asks the user for the temperature value to switch off the heating stored in the column &amp;lt;code&amp;gt;heating_switch_off_temp&amp;lt;/code&amp;gt; and the temperature value to switch on the heating stored in the column &amp;lt;code&amp;gt;heating_switch_on_temp&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The script checks if the value provided by the user for &amp;lt;code&amp;gt;heating_switch_off_temp&amp;lt;/code&amp;gt; is higher than the value provided by the user for &amp;lt;code&amp;gt;heating_switch_on_temp&amp;lt;/code&amp;gt;. If this is not the case, the script repeats the sequence for entering the data.&lt;br /&gt;
&lt;br /&gt;
=== Script-based setup of the ventilation set values ===&lt;br /&gt;
Optionally, the user can setup the ventilation control in the table &amp;lt;code&amp;gt;ventilation_set_vals&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is existing data in the table, the script asks the user if the data shall be erased. &lt;br /&gt;
&lt;br /&gt;
The script asks the user for the temperature value to switch off the ventilation stored in the column &amp;lt;code&amp;gt;ventilation_switch_off_temp&amp;lt;/code&amp;gt; and the temperature value to switch on the ventilation stored in the column &amp;lt;code&amp;gt;ventilation_switch_on_temp&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The script checks if the value provided by the user for &amp;lt;code&amp;gt;ventilation_switch_off_temp&amp;lt;/code&amp;gt; is lower than the value provided by the user for &amp;lt;code&amp;gt;ventilation_switch_on_temp&amp;lt;/code&amp;gt;. If this is not the case, the script repeats the sequence for entering the data.&lt;br /&gt;
&lt;br /&gt;
=== Script-based setup of the actuator schedule ===&lt;br /&gt;
Optionally, the user can request to setup a default actuator schedule in the table &amp;lt;code&amp;gt;schedule&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is existing data in the table, the script asks the user if the data shall be erased.&lt;br /&gt;
&lt;br /&gt;
The script creates the following entries:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Default actuator schedule&lt;br /&gt;
|-&lt;br /&gt;
! schedule_type !! start_time !! stop_time !! is_active&lt;br /&gt;
|-&lt;br /&gt;
| balling || 0:00 || 23:59 || 0&lt;br /&gt;
|-&lt;br /&gt;
| refill || 0:00 || 23:59 || 0&lt;br /&gt;
|-&lt;br /&gt;
| ventilation || 0:00 || 23:59 || 0&lt;br /&gt;
|-&lt;br /&gt;
| heating || 0:00 || 23:59 || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Configuration of connection timeout ===&lt;br /&gt;
Edit the file &amp;lt;code&amp;gt;/etc/mysql/mariadb.conf.d/50-server.cnf&amp;lt;/code&amp;gt; and add &amp;lt;code&amp;gt;wait_timeout = 10000&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This avoids connection timeouts. The application checks this configuration during startup.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=SQL_database&amp;diff=537</id>
		<title>SQL database</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=SQL_database&amp;diff=537"/>
		<updated>2026-07-18T17:06:46Z</updated>

		<summary type="html">&lt;p&gt;Uwe: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The control application uses a MySQL database for persistent storing of states and logging of activities as well as storage of input data.&lt;br /&gt;
&lt;br /&gt;
The SQL database is also the main interface between the control application and the outside world (webpage, Apps).&lt;br /&gt;
&lt;br /&gt;
The empty SQL dump of the databases are stored in the bitbucket repository:&lt;br /&gt;
git clone https://in-dubio@bitbucket.org/in-dubio/aquarium-database.git&lt;br /&gt;
&lt;br /&gt;
As of December 2024, the databases are empty.&lt;br /&gt;
The databases for the tests are emptied and filled programmatically by the test cases before the execution of each test case.&lt;br /&gt;
The high number of test databases shall allow maximum parallelisation of the test case execution which takes several minutes.&lt;br /&gt;
&lt;br /&gt;
After cloning the database repository, first create the test databases with the statement provided in &amp;lt;code&amp;gt;create_databases.sql&amp;lt;/code&amp;gt;.&lt;br /&gt;
You might want to consider using different accounts for the databases between normal operation and test execution. The account data (user and password) are stated in the .toml configuration files.&lt;br /&gt;
&lt;br /&gt;
Second, create the user(s): &amp;lt;code&amp;gt;CREATE USER aquarium@localhost;&amp;lt;/code&amp;gt;&lt;br /&gt;
Set a password for the user with &amp;lt;code&amp;gt;ALTER USER 'aquarium'@'localhost' IDENTIFIED BY 'password';&amp;lt;/code&amp;gt;&lt;br /&gt;
The combination of user and password in the .toml configuration file(s) must match with the provided password.&lt;br /&gt;
&lt;br /&gt;
Next, import the database dumps into the database using &amp;lt;code&amp;gt;import_databases.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then, grant the access to the user(s) using &amp;lt;code&amp;gt;grant_access_rights.sql&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For operating the control, the SQL database must include the Balling dosing configuration of those pumps which are activated in the .toml configuration file.&lt;br /&gt;
The table &amp;lt;code&amp;gt;ballingsetvals&amp;lt;/code&amp;gt; for the configuration of the Balling dosing pumps has the following structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
+--------------+-------------+------+-----+---------+-------+&lt;br /&gt;
| Field        | Type        | Null | Key | Default | Extra |&lt;br /&gt;
+--------------+-------------+------+-----+---------+-------+&lt;br /&gt;
| pumpid       | int(11)     | NO   |     | NULL    |       |&lt;br /&gt;
| dosingspeed  | float       | NO   |     | NULL    |       |&lt;br /&gt;
| dosingvolume | float       | NO   |     | NULL    |       |&lt;br /&gt;
| label        | varchar(10) | NO   |     | NULL    |       |&lt;br /&gt;
+--------------+-------------+------+-----+---------+-------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can insert values as follows:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
INSERT INTO ballingsetvals VALUES(1, 1.0, 0.5, &amp;quot;KH&amp;quot;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For stable operation of the database in the longterm, it is paramount to limit the database size.&lt;br /&gt;
This requires deletion of old entries. MariaDb offers the functionality of event scheduling.&lt;br /&gt;
The [[Event-based deletion of old database entries|event based deletion of old database entries]] needs to be setup by the user.&lt;br /&gt;
&lt;br /&gt;
== Script-based setup of the database ==&lt;br /&gt;
The repository provides a script which conducts terminal-based communication with the user to fill the database.&lt;br /&gt;
&lt;br /&gt;
=== Credentials for script-based setup of the database ===&lt;br /&gt;
Optionally, the user can provide credentials for accessing the database (by default, root privileges allow modification of all MariaDB databases).&lt;br /&gt;
&lt;br /&gt;
The script checks if the provided credentials are valid.&lt;br /&gt;
&lt;br /&gt;
=== Script-based setup of the user data ===&lt;br /&gt;
Optionally, the user can setup the user configuration in the table &amp;lt;code&amp;gt;users&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The script offers the user the possibility to erase all existing user data.&lt;br /&gt;
&lt;br /&gt;
The script repeatedly asks the user if he wishes to add user data.&lt;br /&gt;
&lt;br /&gt;
For each entry that is added in the &amp;lt;code&amp;gt;users&amp;lt;/code&amp;gt; table, the script asks the name stored in the column &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The name must comply with a simple pattern: Only letters a-z and numbers are allowed - no special characters, no space.&lt;br /&gt;
&lt;br /&gt;
If the name already exists, the script asks the user if he wishes to overwrite the data.&lt;br /&gt;
&lt;br /&gt;
The script asks for the password that shall be used for the entry in the table.&lt;br /&gt;
The script stores a &amp;lt;code&amp;gt;bcrypt&amp;lt;/code&amp;gt; hash in the column &amp;lt;code&amp;gt;password&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Script-based setup of the Balling dosing configuration ===&lt;br /&gt;
Optionally, the user can setup the Balling mineral dosing configuration in the table &amp;lt;code&amp;gt;balling_set_vals&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The script offers the user the possibility to erase all existing Balling dosing configuration.&lt;br /&gt;
&lt;br /&gt;
The user can modify any pump configuration identified by the pump id which ranges from one to four.&lt;br /&gt;
&lt;br /&gt;
Alternatively, the user can leave the configuration of a specific pump empty.&lt;br /&gt;
&lt;br /&gt;
If there is an existing entry in the balling dosing configuration, the script asks the user if he wishes to overwrite this entry.&lt;br /&gt;
&lt;br /&gt;
=== Script-based setup of the heating set values ===&lt;br /&gt;
Optionally, the user can setup the heating control in the table &amp;lt;code&amp;gt;heating_set_vals&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is existing data in the table, the script asks the user if the data shall be erased. &lt;br /&gt;
&lt;br /&gt;
The script asks the user for the temperature value to switch off the heating stored in the column &amp;lt;code&amp;gt;heating_switch_off_temp&amp;lt;/code&amp;gt; and the temperature value to switch on the heating stored in the column &amp;lt;code&amp;gt;heating_switch_on_temp&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The script checks if the value provided by the user for &amp;lt;code&amp;gt;heating_switch_off_temp&amp;lt;/code&amp;gt; is higher than the value provided by the user for &amp;lt;code&amp;gt;heating_switch_on_temp&amp;lt;/code&amp;gt;. If this is not the case, the script repeats the sequence for entering the data.&lt;br /&gt;
&lt;br /&gt;
=== Script-based setup of the ventilation set values ===&lt;br /&gt;
Optionally, the user can setup the ventilation control in the table &amp;lt;code&amp;gt;ventilation_set_vals&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is existing data in the table, the script asks the user if the data shall be erased. &lt;br /&gt;
&lt;br /&gt;
The script asks the user for the temperature value to switch off the ventilation stored in the column &amp;lt;code&amp;gt;ventilation_switch_off_temp&amp;lt;/code&amp;gt; and the temperature value to switch on the ventilation stored in the column &amp;lt;code&amp;gt;ventilation_switch_on_temp&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The script checks if the value provided by the user for &amp;lt;code&amp;gt;ventilation_switch_off_temp&amp;lt;/code&amp;gt; is lower than the value provided by the user for &amp;lt;code&amp;gt;ventilation_switch_on_temp&amp;lt;/code&amp;gt;. If this is not the case, the script repeats the sequence for entering the data.&lt;br /&gt;
&lt;br /&gt;
=== Script-based setup of the actuator schedule ===&lt;br /&gt;
Optionally, the user can request to setup a default actuator schedule in the table &amp;lt;code&amp;gt;schedule&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is existing data in the table, the script asks the user if the data shall be erased.&lt;br /&gt;
&lt;br /&gt;
The script creates the following entries:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Default actuator schedule&lt;br /&gt;
|-&lt;br /&gt;
! schedule_type !! start_time !! stop_time !! is_active&lt;br /&gt;
|-&lt;br /&gt;
| balling || 0:00 || 23:59 || 0&lt;br /&gt;
|-&lt;br /&gt;
| refill || 0:00 || 23:59 || 0&lt;br /&gt;
|-&lt;br /&gt;
| ventilation || 0:00 || 23:59 || 0&lt;br /&gt;
|-&lt;br /&gt;
| heating || 0:00 || 23:59 || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Configuration of connection timeout ===&lt;br /&gt;
Edit the file &amp;lt;code&amp;gt;/etc/mysql/mariadb.conf.d/50-server.cnf&amp;lt;/code&amp;gt; and add &amp;lt;code&amp;gt;wait_timeout = 10000&amp;lt;/code&amp;gt;.&lt;br /&gt;
This avoids connection timeouts. The application checks this configuration during startup.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=536</id>
		<title>Use of an asynchronous runtime for the main application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=536"/>
		<updated>2026-07-12T09:16:03Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Overview */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
The Aquarium Control application uses a hybrid execution model to balance responsiveness, low latency, and hardware constraints. It uses the Tokio asynchronous runtime for coordinating sensor logging, network communication, safety interlocks, and scheduling. Concurrently, it spawns dedicated native operating system threads for operations requiring strict timing or blocking I/O (such as direct GPIO sensor reading).&lt;br /&gt;
&lt;br /&gt;
== Task and Thread Inventory ==&lt;br /&gt;
&lt;br /&gt;
The table below lists all tasks and threads running in the application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Description&lt;br /&gt;
! Type (OS Thread / Async Task)&lt;br /&gt;
! Usage of &amp;lt;code&amp;gt;block_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Main Thread'''&lt;br /&gt;
| Bootstraps the application, reads configuration, establishes database pools, builds the Tokio runtime, and blocks on the execution of all tasks.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| Yes (&amp;lt;code&amp;gt;rt.block_on&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;TcpCommunication&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Mock simulator thread that handles TCP connections and commands from the GUI simulator. Runs only when &amp;lt;code&amp;gt;use_simulator&amp;lt;/code&amp;gt; is active.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;Dht&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Direct driver reader thread for the DHT22 ambient temperature and humidity sensor. Handled as an OS thread to prevent blocking the async runtime.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;i2c_interface&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages serial I2C bus communications for sensor expansion units.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Captures shutdown signals (SIGINT, SIGTERM) and coordinates the graceful, phase-based shutdown of all sub-components.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;schedule_check&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs regular database checks against the active rules database to ensure operations are authorized based on the current time.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;sensor_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Coordinates, aggregates, and caches values read from individual sensor tasks (DS18B20, DHT22, Atlas Scientific).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;atlas_scientific&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages data acquisition and calibration for the Atlas Scientific EZO pH and conductivity probes.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Acts as the central actuator gateway, processing on/off/pulse commands and sending them directly to the hardware relays.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically logs sensor values and thermal controller statuses into the MySQL database.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;temperature_gradient&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the rate of temperature changes to detect heater or ventilation inefficiencies/failures.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Orchestrates the heating loop, using hysteresis or safety controls to actuate the aquarium heater.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ventilation&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Controls the surface cooling fan loop, maintaining temperature thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;monitors&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Runs regular diagnostic checks and outputs warning/critical logs when states drift outside target thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Drives the fresh water top-off system, managing level switch states and pump runs with safety timers.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;feed&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the feeding schedule and actuates the automatic feeder.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;balling&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Oversees peristaltic mineral pumps to dose minerals into the aquarium according to schedule.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;tank_level_switch&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically reads the state of physical water level floats or simulator ports.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;watchdog&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs periodic heartbeats to ensure system safety loops are executing.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ds18b20&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically queries 1-Wire DS18B20 digital thermometers for water temperature.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;messaging&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Stub mock receiver that routes command queues when running on non-Linux platforms (like macOS).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Tokio Runtime Configuration ==&lt;br /&gt;
&lt;br /&gt;
The Tokio runtime is explicitly built at startup with a constrained configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread().worker_threads(1).max_blocking_threads(4).enable_all().build()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Why this configuration is chosen ===&lt;br /&gt;
# &amp;lt;code&amp;gt;worker_threads(1)&amp;lt;/code&amp;gt;: The application runs on Raspberry Pi. Limiting the event loop scheduler to one worker thread minimizes CPU context-switching overhead and prevents resource exhaustion.&lt;br /&gt;
# &amp;lt;code&amp;gt;max_blocking_threads(4)&amp;lt;/code&amp;gt;: Restricting the blocking thread pool prevents the runtime from spawning too many concurrent threads when database pings, file operations, or synchronous calculations are invoked (such as via &amp;lt;code&amp;gt;tokio::task::spawn_blocking&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;block_in_place&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Developer Diagnostics with Tokio Console ===&lt;br /&gt;
&lt;br /&gt;
For runtime inspection, task profiling, and detecting deadlocks or starvation, you can run the application with Tokio Console integration.&lt;br /&gt;
&lt;br /&gt;
==== Prerequisites &amp;amp; Compilation ====&lt;br /&gt;
Tokio Console requires unstable instrumentation.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;debug_tokio&amp;lt;/code&amp;gt; feature flag, which initializes the &amp;lt;code&amp;gt;console-subscriber&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;run.rs&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;RUSTFLAGS=&amp;quot;--cfg tokio_unstable&amp;quot; cargo build --features &amp;quot;debug_tokio&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
# The &amp;lt;code&amp;gt;tokio_unstable&amp;lt;/code&amp;gt; compiler flag ensures that Tokio exposes internal tracing endpoints (like the task builder names).&lt;br /&gt;
&lt;br /&gt;
==== Installing the Terminal Console ====&lt;br /&gt;
Install the client CLI on your development machine (macOS/Linux) via Cargo:&lt;br /&gt;
&amp;lt;code&amp;gt;cargo install --locked tokio-console&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Running and Reading ====&lt;br /&gt;
# Launch the compiled application:&lt;br /&gt;
# Open a separate terminal window and run &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;.&lt;br /&gt;
This presents a live dashboard of running tasks, showing busy poll times, total lifetime, and idle periods.&lt;br /&gt;
&lt;br /&gt;
[[File:tokio-console.png | center | 800px | Screenshot of tokio-console during execution of the main application.]]&lt;br /&gt;
&lt;br /&gt;
=== Interpreting Task Size Warnings ===&lt;br /&gt;
When checking with &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;, you see warnings that tasks like &amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt; are 1024 bytes or larger.&lt;br /&gt;
* These warnings can be safely ignored.&lt;br /&gt;
* The size refers to the stack footprint of the compiler-generated &amp;lt;code&amp;gt;Future&amp;lt;/code&amp;gt; state machine. In debug builds, these are larger due to lack of optimization.&lt;br /&gt;
* Because these tasks are long-lived workers spawned once at startup and run until shutdown, they are allocated on the heap only once. A 1–2 KiB heap footprint has zero impact on application performance, fragmentation, or memory leaks.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=535</id>
		<title>Use of an asynchronous runtime for the main application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=535"/>
		<updated>2026-07-11T11:48:26Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Running and Reading */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
The Aquarium Control application uses a hybrid execution model to balance responsiveness, low latency, and hardware constraints. It uses the **Tokio asynchronous runtime** for coordinating sensor logging, network communication, safety interlocks, and scheduling. Concurrently, it spawns dedicated **native operating system threads** for operations requiring strict timing or blocking I/O (such as direct GPIO sensor reading).&lt;br /&gt;
&lt;br /&gt;
== Task and Thread Inventory ==&lt;br /&gt;
&lt;br /&gt;
The table below lists all tasks and threads running in the application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Description&lt;br /&gt;
! Type (OS Thread / Async Task)&lt;br /&gt;
! Usage of &amp;lt;code&amp;gt;block_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Main Thread'''&lt;br /&gt;
| Bootstraps the application, reads configuration, establishes database pools, builds the Tokio runtime, and blocks on the execution of all tasks.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| Yes (&amp;lt;code&amp;gt;rt.block_on&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;TcpCommunication&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Mock simulator thread that handles TCP connections and commands from the GUI simulator. Runs only when &amp;lt;code&amp;gt;use_simulator&amp;lt;/code&amp;gt; is active.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;Dht&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Direct driver reader thread for the DHT22 ambient temperature and humidity sensor. Handled as an OS thread to prevent blocking the async runtime.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;i2c_interface&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages serial I2C bus communications for sensor expansion units.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Captures shutdown signals (SIGINT, SIGTERM) and coordinates the graceful, phase-based shutdown of all sub-components.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;schedule_check&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs regular database checks against the active rules database to ensure operations are authorized based on the current time.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;sensor_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Coordinates, aggregates, and caches values read from individual sensor tasks (DS18B20, DHT22, Atlas Scientific).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;atlas_scientific&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages data acquisition and calibration for the Atlas Scientific EZO pH and conductivity probes.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Acts as the central actuator gateway, processing on/off/pulse commands and sending them directly to the hardware relays.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically logs sensor values and thermal controller statuses into the MySQL database.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;temperature_gradient&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the rate of temperature changes to detect heater or ventilation inefficiencies/failures.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Orchestrates the heating loop, using hysteresis or safety controls to actuate the aquarium heater.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ventilation&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Controls the surface cooling fan loop, maintaining temperature thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;monitors&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Runs regular diagnostic checks and outputs warning/critical logs when states drift outside target thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Drives the fresh water top-off system, managing level switch states and pump runs with safety timers.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;feed&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the feeding schedule and actuates the automatic feeder.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;balling&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Oversees peristaltic mineral pumps to dose minerals into the aquarium according to schedule.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;tank_level_switch&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically reads the state of physical water level floats or simulator ports.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;watchdog&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs periodic heartbeats to ensure system safety loops are executing.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ds18b20&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically queries 1-Wire DS18B20 digital thermometers for water temperature.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;messaging&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Stub mock receiver that routes command queues when running on non-Linux platforms (like macOS).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Tokio Runtime Configuration ==&lt;br /&gt;
&lt;br /&gt;
The Tokio runtime is explicitly built at startup with a constrained configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread().worker_threads(1).max_blocking_threads(4).enable_all().build()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Why this configuration is chosen ===&lt;br /&gt;
# &amp;lt;code&amp;gt;worker_threads(1)&amp;lt;/code&amp;gt;: The application runs on Raspberry Pi. Limiting the event loop scheduler to one worker thread minimizes CPU context-switching overhead and prevents resource exhaustion.&lt;br /&gt;
# &amp;lt;code&amp;gt;max_blocking_threads(4)&amp;lt;/code&amp;gt;: Restricting the blocking thread pool prevents the runtime from spawning too many concurrent threads when database pings, file operations, or synchronous calculations are invoked (such as via &amp;lt;code&amp;gt;tokio::task::spawn_blocking&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;block_in_place&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Developer Diagnostics with Tokio Console ===&lt;br /&gt;
&lt;br /&gt;
For runtime inspection, task profiling, and detecting deadlocks or starvation, you can run the application with Tokio Console integration.&lt;br /&gt;
&lt;br /&gt;
==== Prerequisites &amp;amp; Compilation ====&lt;br /&gt;
Tokio Console requires unstable instrumentation.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;debug_tokio&amp;lt;/code&amp;gt; feature flag, which initializes the &amp;lt;code&amp;gt;console-subscriber&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;run.rs&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;RUSTFLAGS=&amp;quot;--cfg tokio_unstable&amp;quot; cargo build --features &amp;quot;debug_tokio&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
# The &amp;lt;code&amp;gt;tokio_unstable&amp;lt;/code&amp;gt; compiler flag ensures that Tokio exposes internal tracing endpoints (like the task builder names).&lt;br /&gt;
&lt;br /&gt;
==== Installing the Terminal Console ====&lt;br /&gt;
Install the client CLI on your development machine (macOS/Linux) via Cargo:&lt;br /&gt;
&amp;lt;code&amp;gt;cargo install --locked tokio-console&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Running and Reading ====&lt;br /&gt;
# Launch the compiled application:&lt;br /&gt;
# Open a separate terminal window and run &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;.&lt;br /&gt;
This presents a live dashboard of running tasks, showing busy poll times, total lifetime, and idle periods.&lt;br /&gt;
&lt;br /&gt;
[[File:tokio-console.png | center | 800px | Screenshot of tokio-console during execution of the main application.]]&lt;br /&gt;
&lt;br /&gt;
=== Interpreting Task Size Warnings ===&lt;br /&gt;
When checking with &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;, you see warnings that tasks like &amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt; are 1024 bytes or larger.&lt;br /&gt;
* These warnings can be safely ignored.&lt;br /&gt;
* The size refers to the stack footprint of the compiler-generated &amp;lt;code&amp;gt;Future&amp;lt;/code&amp;gt; state machine. In debug builds, these are larger due to lack of optimization.&lt;br /&gt;
* Because these tasks are long-lived workers spawned once at startup and run until shutdown, they are allocated on the heap only once. A 1–2 KiB heap footprint has zero impact on application performance, fragmentation, or memory leaks.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=534</id>
		<title>Use of an asynchronous runtime for the main application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=534"/>
		<updated>2026-07-11T11:48:16Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Running and Reading */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
The Aquarium Control application uses a hybrid execution model to balance responsiveness, low latency, and hardware constraints. It uses the **Tokio asynchronous runtime** for coordinating sensor logging, network communication, safety interlocks, and scheduling. Concurrently, it spawns dedicated **native operating system threads** for operations requiring strict timing or blocking I/O (such as direct GPIO sensor reading).&lt;br /&gt;
&lt;br /&gt;
== Task and Thread Inventory ==&lt;br /&gt;
&lt;br /&gt;
The table below lists all tasks and threads running in the application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Description&lt;br /&gt;
! Type (OS Thread / Async Task)&lt;br /&gt;
! Usage of &amp;lt;code&amp;gt;block_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Main Thread'''&lt;br /&gt;
| Bootstraps the application, reads configuration, establishes database pools, builds the Tokio runtime, and blocks on the execution of all tasks.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| Yes (&amp;lt;code&amp;gt;rt.block_on&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;TcpCommunication&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Mock simulator thread that handles TCP connections and commands from the GUI simulator. Runs only when &amp;lt;code&amp;gt;use_simulator&amp;lt;/code&amp;gt; is active.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;Dht&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Direct driver reader thread for the DHT22 ambient temperature and humidity sensor. Handled as an OS thread to prevent blocking the async runtime.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;i2c_interface&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages serial I2C bus communications for sensor expansion units.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Captures shutdown signals (SIGINT, SIGTERM) and coordinates the graceful, phase-based shutdown of all sub-components.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;schedule_check&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs regular database checks against the active rules database to ensure operations are authorized based on the current time.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;sensor_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Coordinates, aggregates, and caches values read from individual sensor tasks (DS18B20, DHT22, Atlas Scientific).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;atlas_scientific&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages data acquisition and calibration for the Atlas Scientific EZO pH and conductivity probes.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Acts as the central actuator gateway, processing on/off/pulse commands and sending them directly to the hardware relays.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically logs sensor values and thermal controller statuses into the MySQL database.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;temperature_gradient&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the rate of temperature changes to detect heater or ventilation inefficiencies/failures.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Orchestrates the heating loop, using hysteresis or safety controls to actuate the aquarium heater.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ventilation&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Controls the surface cooling fan loop, maintaining temperature thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;monitors&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Runs regular diagnostic checks and outputs warning/critical logs when states drift outside target thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Drives the fresh water top-off system, managing level switch states and pump runs with safety timers.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;feed&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the feeding schedule and actuates the automatic feeder.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;balling&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Oversees peristaltic mineral pumps to dose minerals into the aquarium according to schedule.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;tank_level_switch&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically reads the state of physical water level floats or simulator ports.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;watchdog&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs periodic heartbeats to ensure system safety loops are executing.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ds18b20&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically queries 1-Wire DS18B20 digital thermometers for water temperature.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;messaging&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Stub mock receiver that routes command queues when running on non-Linux platforms (like macOS).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Tokio Runtime Configuration ==&lt;br /&gt;
&lt;br /&gt;
The Tokio runtime is explicitly built at startup with a constrained configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread().worker_threads(1).max_blocking_threads(4).enable_all().build()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Why this configuration is chosen ===&lt;br /&gt;
# &amp;lt;code&amp;gt;worker_threads(1)&amp;lt;/code&amp;gt;: The application runs on Raspberry Pi. Limiting the event loop scheduler to one worker thread minimizes CPU context-switching overhead and prevents resource exhaustion.&lt;br /&gt;
# &amp;lt;code&amp;gt;max_blocking_threads(4)&amp;lt;/code&amp;gt;: Restricting the blocking thread pool prevents the runtime from spawning too many concurrent threads when database pings, file operations, or synchronous calculations are invoked (such as via &amp;lt;code&amp;gt;tokio::task::spawn_blocking&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;block_in_place&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Developer Diagnostics with Tokio Console ===&lt;br /&gt;
&lt;br /&gt;
For runtime inspection, task profiling, and detecting deadlocks or starvation, you can run the application with Tokio Console integration.&lt;br /&gt;
&lt;br /&gt;
==== Prerequisites &amp;amp; Compilation ====&lt;br /&gt;
Tokio Console requires unstable instrumentation.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;debug_tokio&amp;lt;/code&amp;gt; feature flag, which initializes the &amp;lt;code&amp;gt;console-subscriber&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;run.rs&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;RUSTFLAGS=&amp;quot;--cfg tokio_unstable&amp;quot; cargo build --features &amp;quot;debug_tokio&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
# The &amp;lt;code&amp;gt;tokio_unstable&amp;lt;/code&amp;gt; compiler flag ensures that Tokio exposes internal tracing endpoints (like the task builder names).&lt;br /&gt;
&lt;br /&gt;
==== Installing the Terminal Console ====&lt;br /&gt;
Install the client CLI on your development machine (macOS/Linux) via Cargo:&lt;br /&gt;
&amp;lt;code&amp;gt;cargo install --locked tokio-console&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Running and Reading ====&lt;br /&gt;
# Launch the compiled application:&lt;br /&gt;
# Open a separate terminal window and run &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;.&lt;br /&gt;
This presents a live dashboard of running tasks, showing busy poll times, total lifetime, and idle periods.&lt;br /&gt;
&lt;br /&gt;
[[File:tokio-console.png | center | 300px | Screenshot of tokio-console during execution of the main application.]]&lt;br /&gt;
&lt;br /&gt;
=== Interpreting Task Size Warnings ===&lt;br /&gt;
When checking with &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;, you see warnings that tasks like &amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt; are 1024 bytes or larger.&lt;br /&gt;
* These warnings can be safely ignored.&lt;br /&gt;
* The size refers to the stack footprint of the compiler-generated &amp;lt;code&amp;gt;Future&amp;lt;/code&amp;gt; state machine. In debug builds, these are larger due to lack of optimization.&lt;br /&gt;
* Because these tasks are long-lived workers spawned once at startup and run until shutdown, they are allocated on the heap only once. A 1–2 KiB heap footprint has zero impact on application performance, fragmentation, or memory leaks.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=533</id>
		<title>Use of an asynchronous runtime for the main application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=533"/>
		<updated>2026-07-11T11:47:57Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Running and Reading */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
The Aquarium Control application uses a hybrid execution model to balance responsiveness, low latency, and hardware constraints. It uses the **Tokio asynchronous runtime** for coordinating sensor logging, network communication, safety interlocks, and scheduling. Concurrently, it spawns dedicated **native operating system threads** for operations requiring strict timing or blocking I/O (such as direct GPIO sensor reading).&lt;br /&gt;
&lt;br /&gt;
== Task and Thread Inventory ==&lt;br /&gt;
&lt;br /&gt;
The table below lists all tasks and threads running in the application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Description&lt;br /&gt;
! Type (OS Thread / Async Task)&lt;br /&gt;
! Usage of &amp;lt;code&amp;gt;block_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Main Thread'''&lt;br /&gt;
| Bootstraps the application, reads configuration, establishes database pools, builds the Tokio runtime, and blocks on the execution of all tasks.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| Yes (&amp;lt;code&amp;gt;rt.block_on&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;TcpCommunication&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Mock simulator thread that handles TCP connections and commands from the GUI simulator. Runs only when &amp;lt;code&amp;gt;use_simulator&amp;lt;/code&amp;gt; is active.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;Dht&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Direct driver reader thread for the DHT22 ambient temperature and humidity sensor. Handled as an OS thread to prevent blocking the async runtime.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;i2c_interface&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages serial I2C bus communications for sensor expansion units.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Captures shutdown signals (SIGINT, SIGTERM) and coordinates the graceful, phase-based shutdown of all sub-components.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;schedule_check&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs regular database checks against the active rules database to ensure operations are authorized based on the current time.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;sensor_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Coordinates, aggregates, and caches values read from individual sensor tasks (DS18B20, DHT22, Atlas Scientific).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;atlas_scientific&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages data acquisition and calibration for the Atlas Scientific EZO pH and conductivity probes.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Acts as the central actuator gateway, processing on/off/pulse commands and sending them directly to the hardware relays.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically logs sensor values and thermal controller statuses into the MySQL database.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;temperature_gradient&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the rate of temperature changes to detect heater or ventilation inefficiencies/failures.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Orchestrates the heating loop, using hysteresis or safety controls to actuate the aquarium heater.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ventilation&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Controls the surface cooling fan loop, maintaining temperature thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;monitors&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Runs regular diagnostic checks and outputs warning/critical logs when states drift outside target thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Drives the fresh water top-off system, managing level switch states and pump runs with safety timers.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;feed&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the feeding schedule and actuates the automatic feeder.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;balling&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Oversees peristaltic mineral pumps to dose minerals into the aquarium according to schedule.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;tank_level_switch&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically reads the state of physical water level floats or simulator ports.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;watchdog&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs periodic heartbeats to ensure system safety loops are executing.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ds18b20&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically queries 1-Wire DS18B20 digital thermometers for water temperature.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;messaging&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Stub mock receiver that routes command queues when running on non-Linux platforms (like macOS).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Tokio Runtime Configuration ==&lt;br /&gt;
&lt;br /&gt;
The Tokio runtime is explicitly built at startup with a constrained configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread().worker_threads(1).max_blocking_threads(4).enable_all().build()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Why this configuration is chosen ===&lt;br /&gt;
# &amp;lt;code&amp;gt;worker_threads(1)&amp;lt;/code&amp;gt;: The application runs on Raspberry Pi. Limiting the event loop scheduler to one worker thread minimizes CPU context-switching overhead and prevents resource exhaustion.&lt;br /&gt;
# &amp;lt;code&amp;gt;max_blocking_threads(4)&amp;lt;/code&amp;gt;: Restricting the blocking thread pool prevents the runtime from spawning too many concurrent threads when database pings, file operations, or synchronous calculations are invoked (such as via &amp;lt;code&amp;gt;tokio::task::spawn_blocking&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;block_in_place&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Developer Diagnostics with Tokio Console ===&lt;br /&gt;
&lt;br /&gt;
For runtime inspection, task profiling, and detecting deadlocks or starvation, you can run the application with Tokio Console integration.&lt;br /&gt;
&lt;br /&gt;
==== Prerequisites &amp;amp; Compilation ====&lt;br /&gt;
Tokio Console requires unstable instrumentation.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;debug_tokio&amp;lt;/code&amp;gt; feature flag, which initializes the &amp;lt;code&amp;gt;console-subscriber&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;run.rs&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;RUSTFLAGS=&amp;quot;--cfg tokio_unstable&amp;quot; cargo build --features &amp;quot;debug_tokio&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
# The &amp;lt;code&amp;gt;tokio_unstable&amp;lt;/code&amp;gt; compiler flag ensures that Tokio exposes internal tracing endpoints (like the task builder names).&lt;br /&gt;
&lt;br /&gt;
==== Installing the Terminal Console ====&lt;br /&gt;
Install the client CLI on your development machine (macOS/Linux) via Cargo:&lt;br /&gt;
&amp;lt;code&amp;gt;cargo install --locked tokio-console&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Running and Reading ====&lt;br /&gt;
# Launch the compiled application:&lt;br /&gt;
# Open a separate terminal window and run &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;.&lt;br /&gt;
This presents a live dashboard of running tasks, showing busy poll times, total lifetime, and idle periods.&lt;br /&gt;
[[File:tokio-console.png | 300px | Screenshot of tokio-console during execution of the main application.]]&lt;br /&gt;
&lt;br /&gt;
=== Interpreting Task Size Warnings ===&lt;br /&gt;
When checking with &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;, you see warnings that tasks like &amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt; are 1024 bytes or larger.&lt;br /&gt;
* These warnings can be safely ignored.&lt;br /&gt;
* The size refers to the stack footprint of the compiler-generated &amp;lt;code&amp;gt;Future&amp;lt;/code&amp;gt; state machine. In debug builds, these are larger due to lack of optimization.&lt;br /&gt;
* Because these tasks are long-lived workers spawned once at startup and run until shutdown, they are allocated on the heap only once. A 1–2 KiB heap footprint has zero impact on application performance, fragmentation, or memory leaks.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=532</id>
		<title>Use of an asynchronous runtime for the main application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=532"/>
		<updated>2026-07-11T11:47:20Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Running and Reading */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
The Aquarium Control application uses a hybrid execution model to balance responsiveness, low latency, and hardware constraints. It uses the **Tokio asynchronous runtime** for coordinating sensor logging, network communication, safety interlocks, and scheduling. Concurrently, it spawns dedicated **native operating system threads** for operations requiring strict timing or blocking I/O (such as direct GPIO sensor reading).&lt;br /&gt;
&lt;br /&gt;
== Task and Thread Inventory ==&lt;br /&gt;
&lt;br /&gt;
The table below lists all tasks and threads running in the application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Description&lt;br /&gt;
! Type (OS Thread / Async Task)&lt;br /&gt;
! Usage of &amp;lt;code&amp;gt;block_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Main Thread'''&lt;br /&gt;
| Bootstraps the application, reads configuration, establishes database pools, builds the Tokio runtime, and blocks on the execution of all tasks.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| Yes (&amp;lt;code&amp;gt;rt.block_on&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;TcpCommunication&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Mock simulator thread that handles TCP connections and commands from the GUI simulator. Runs only when &amp;lt;code&amp;gt;use_simulator&amp;lt;/code&amp;gt; is active.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;Dht&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Direct driver reader thread for the DHT22 ambient temperature and humidity sensor. Handled as an OS thread to prevent blocking the async runtime.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;i2c_interface&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages serial I2C bus communications for sensor expansion units.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Captures shutdown signals (SIGINT, SIGTERM) and coordinates the graceful, phase-based shutdown of all sub-components.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;schedule_check&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs regular database checks against the active rules database to ensure operations are authorized based on the current time.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;sensor_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Coordinates, aggregates, and caches values read from individual sensor tasks (DS18B20, DHT22, Atlas Scientific).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;atlas_scientific&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages data acquisition and calibration for the Atlas Scientific EZO pH and conductivity probes.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Acts as the central actuator gateway, processing on/off/pulse commands and sending them directly to the hardware relays.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically logs sensor values and thermal controller statuses into the MySQL database.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;temperature_gradient&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the rate of temperature changes to detect heater or ventilation inefficiencies/failures.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Orchestrates the heating loop, using hysteresis or safety controls to actuate the aquarium heater.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ventilation&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Controls the surface cooling fan loop, maintaining temperature thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;monitors&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Runs regular diagnostic checks and outputs warning/critical logs when states drift outside target thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Drives the fresh water top-off system, managing level switch states and pump runs with safety timers.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;feed&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the feeding schedule and actuates the automatic feeder.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;balling&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Oversees peristaltic mineral pumps to dose minerals into the aquarium according to schedule.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;tank_level_switch&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically reads the state of physical water level floats or simulator ports.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;watchdog&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs periodic heartbeats to ensure system safety loops are executing.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ds18b20&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically queries 1-Wire DS18B20 digital thermometers for water temperature.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;messaging&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Stub mock receiver that routes command queues when running on non-Linux platforms (like macOS).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Tokio Runtime Configuration ==&lt;br /&gt;
&lt;br /&gt;
The Tokio runtime is explicitly built at startup with a constrained configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread().worker_threads(1).max_blocking_threads(4).enable_all().build()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Why this configuration is chosen ===&lt;br /&gt;
# &amp;lt;code&amp;gt;worker_threads(1)&amp;lt;/code&amp;gt;: The application runs on Raspberry Pi. Limiting the event loop scheduler to one worker thread minimizes CPU context-switching overhead and prevents resource exhaustion.&lt;br /&gt;
# &amp;lt;code&amp;gt;max_blocking_threads(4)&amp;lt;/code&amp;gt;: Restricting the blocking thread pool prevents the runtime from spawning too many concurrent threads when database pings, file operations, or synchronous calculations are invoked (such as via &amp;lt;code&amp;gt;tokio::task::spawn_blocking&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;block_in_place&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Developer Diagnostics with Tokio Console ===&lt;br /&gt;
&lt;br /&gt;
For runtime inspection, task profiling, and detecting deadlocks or starvation, you can run the application with Tokio Console integration.&lt;br /&gt;
&lt;br /&gt;
==== Prerequisites &amp;amp; Compilation ====&lt;br /&gt;
Tokio Console requires unstable instrumentation.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;debug_tokio&amp;lt;/code&amp;gt; feature flag, which initializes the &amp;lt;code&amp;gt;console-subscriber&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;run.rs&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;RUSTFLAGS=&amp;quot;--cfg tokio_unstable&amp;quot; cargo build --features &amp;quot;debug_tokio&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
# The &amp;lt;code&amp;gt;tokio_unstable&amp;lt;/code&amp;gt; compiler flag ensures that Tokio exposes internal tracing endpoints (like the task builder names).&lt;br /&gt;
&lt;br /&gt;
==== Installing the Terminal Console ====&lt;br /&gt;
Install the client CLI on your development machine (macOS/Linux) via Cargo:&lt;br /&gt;
&amp;lt;code&amp;gt;cargo install --locked tokio-console&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Running and Reading ====&lt;br /&gt;
# Launch the compiled application:&lt;br /&gt;
# Open a separate terminal window and run &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;.&lt;br /&gt;
This presents a live dashboard of running tasks, showing busy poll times, total lifetime, and idle periods.&lt;br /&gt;
[[File:tokio-console.png | Screenshot of tokio-console during execution of the main application.]]&lt;br /&gt;
&lt;br /&gt;
=== Interpreting Task Size Warnings ===&lt;br /&gt;
When checking with &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;, you see warnings that tasks like &amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt; are 1024 bytes or larger.&lt;br /&gt;
* These warnings can be safely ignored.&lt;br /&gt;
* The size refers to the stack footprint of the compiler-generated &amp;lt;code&amp;gt;Future&amp;lt;/code&amp;gt; state machine. In debug builds, these are larger due to lack of optimization.&lt;br /&gt;
* Because these tasks are long-lived workers spawned once at startup and run until shutdown, they are allocated on the heap only once. A 1–2 KiB heap footprint has zero impact on application performance, fragmentation, or memory leaks.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=531</id>
		<title>Use of an asynchronous runtime for the main application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=531"/>
		<updated>2026-07-11T11:46:53Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Running and Reading */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
The Aquarium Control application uses a hybrid execution model to balance responsiveness, low latency, and hardware constraints. It uses the **Tokio asynchronous runtime** for coordinating sensor logging, network communication, safety interlocks, and scheduling. Concurrently, it spawns dedicated **native operating system threads** for operations requiring strict timing or blocking I/O (such as direct GPIO sensor reading).&lt;br /&gt;
&lt;br /&gt;
== Task and Thread Inventory ==&lt;br /&gt;
&lt;br /&gt;
The table below lists all tasks and threads running in the application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Description&lt;br /&gt;
! Type (OS Thread / Async Task)&lt;br /&gt;
! Usage of &amp;lt;code&amp;gt;block_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Main Thread'''&lt;br /&gt;
| Bootstraps the application, reads configuration, establishes database pools, builds the Tokio runtime, and blocks on the execution of all tasks.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| Yes (&amp;lt;code&amp;gt;rt.block_on&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;TcpCommunication&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Mock simulator thread that handles TCP connections and commands from the GUI simulator. Runs only when &amp;lt;code&amp;gt;use_simulator&amp;lt;/code&amp;gt; is active.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;Dht&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Direct driver reader thread for the DHT22 ambient temperature and humidity sensor. Handled as an OS thread to prevent blocking the async runtime.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;i2c_interface&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages serial I2C bus communications for sensor expansion units.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Captures shutdown signals (SIGINT, SIGTERM) and coordinates the graceful, phase-based shutdown of all sub-components.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;schedule_check&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs regular database checks against the active rules database to ensure operations are authorized based on the current time.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;sensor_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Coordinates, aggregates, and caches values read from individual sensor tasks (DS18B20, DHT22, Atlas Scientific).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;atlas_scientific&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages data acquisition and calibration for the Atlas Scientific EZO pH and conductivity probes.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Acts as the central actuator gateway, processing on/off/pulse commands and sending them directly to the hardware relays.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically logs sensor values and thermal controller statuses into the MySQL database.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;temperature_gradient&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the rate of temperature changes to detect heater or ventilation inefficiencies/failures.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Orchestrates the heating loop, using hysteresis or safety controls to actuate the aquarium heater.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ventilation&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Controls the surface cooling fan loop, maintaining temperature thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;monitors&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Runs regular diagnostic checks and outputs warning/critical logs when states drift outside target thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Drives the fresh water top-off system, managing level switch states and pump runs with safety timers.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;feed&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the feeding schedule and actuates the automatic feeder.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;balling&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Oversees peristaltic mineral pumps to dose minerals into the aquarium according to schedule.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;tank_level_switch&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically reads the state of physical water level floats or simulator ports.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;watchdog&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs periodic heartbeats to ensure system safety loops are executing.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ds18b20&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically queries 1-Wire DS18B20 digital thermometers for water temperature.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;messaging&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Stub mock receiver that routes command queues when running on non-Linux platforms (like macOS).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Tokio Runtime Configuration ==&lt;br /&gt;
&lt;br /&gt;
The Tokio runtime is explicitly built at startup with a constrained configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread().worker_threads(1).max_blocking_threads(4).enable_all().build()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Why this configuration is chosen ===&lt;br /&gt;
# &amp;lt;code&amp;gt;worker_threads(1)&amp;lt;/code&amp;gt;: The application runs on Raspberry Pi. Limiting the event loop scheduler to one worker thread minimizes CPU context-switching overhead and prevents resource exhaustion.&lt;br /&gt;
# &amp;lt;code&amp;gt;max_blocking_threads(4)&amp;lt;/code&amp;gt;: Restricting the blocking thread pool prevents the runtime from spawning too many concurrent threads when database pings, file operations, or synchronous calculations are invoked (such as via &amp;lt;code&amp;gt;tokio::task::spawn_blocking&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;block_in_place&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Developer Diagnostics with Tokio Console ===&lt;br /&gt;
&lt;br /&gt;
For runtime inspection, task profiling, and detecting deadlocks or starvation, you can run the application with Tokio Console integration.&lt;br /&gt;
&lt;br /&gt;
==== Prerequisites &amp;amp; Compilation ====&lt;br /&gt;
Tokio Console requires unstable instrumentation.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;debug_tokio&amp;lt;/code&amp;gt; feature flag, which initializes the &amp;lt;code&amp;gt;console-subscriber&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;run.rs&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;RUSTFLAGS=&amp;quot;--cfg tokio_unstable&amp;quot; cargo build --features &amp;quot;debug_tokio&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
# The &amp;lt;code&amp;gt;tokio_unstable&amp;lt;/code&amp;gt; compiler flag ensures that Tokio exposes internal tracing endpoints (like the task builder names).&lt;br /&gt;
&lt;br /&gt;
==== Installing the Terminal Console ====&lt;br /&gt;
Install the client CLI on your development machine (macOS/Linux) via Cargo:&lt;br /&gt;
&amp;lt;code&amp;gt;cargo install --locked tokio-console&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Running and Reading ====&lt;br /&gt;
# Launch the compiled application:&lt;br /&gt;
# Open a separate terminal window and run &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;.&lt;br /&gt;
This presents a live dashboard of running tasks, showing busy poll times, total lifetime, and idle periods.&lt;br /&gt;
[[File:tokio-console.png | thumb | Screenshot of tokio-console during execution of the main application.]]&lt;br /&gt;
&lt;br /&gt;
=== Interpreting Task Size Warnings ===&lt;br /&gt;
When checking with &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;, you see warnings that tasks like &amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt; are 1024 bytes or larger.&lt;br /&gt;
* These warnings can be safely ignored.&lt;br /&gt;
* The size refers to the stack footprint of the compiler-generated &amp;lt;code&amp;gt;Future&amp;lt;/code&amp;gt; state machine. In debug builds, these are larger due to lack of optimization.&lt;br /&gt;
* Because these tasks are long-lived workers spawned once at startup and run until shutdown, they are allocated on the heap only once. A 1–2 KiB heap footprint has zero impact on application performance, fragmentation, or memory leaks.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=530</id>
		<title>Use of an asynchronous runtime for the main application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=530"/>
		<updated>2026-07-11T11:45:49Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Running and Reading */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
The Aquarium Control application uses a hybrid execution model to balance responsiveness, low latency, and hardware constraints. It uses the **Tokio asynchronous runtime** for coordinating sensor logging, network communication, safety interlocks, and scheduling. Concurrently, it spawns dedicated **native operating system threads** for operations requiring strict timing or blocking I/O (such as direct GPIO sensor reading).&lt;br /&gt;
&lt;br /&gt;
== Task and Thread Inventory ==&lt;br /&gt;
&lt;br /&gt;
The table below lists all tasks and threads running in the application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Description&lt;br /&gt;
! Type (OS Thread / Async Task)&lt;br /&gt;
! Usage of &amp;lt;code&amp;gt;block_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Main Thread'''&lt;br /&gt;
| Bootstraps the application, reads configuration, establishes database pools, builds the Tokio runtime, and blocks on the execution of all tasks.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| Yes (&amp;lt;code&amp;gt;rt.block_on&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;TcpCommunication&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Mock simulator thread that handles TCP connections and commands from the GUI simulator. Runs only when &amp;lt;code&amp;gt;use_simulator&amp;lt;/code&amp;gt; is active.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;Dht&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Direct driver reader thread for the DHT22 ambient temperature and humidity sensor. Handled as an OS thread to prevent blocking the async runtime.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;i2c_interface&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages serial I2C bus communications for sensor expansion units.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Captures shutdown signals (SIGINT, SIGTERM) and coordinates the graceful, phase-based shutdown of all sub-components.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;schedule_check&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs regular database checks against the active rules database to ensure operations are authorized based on the current time.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;sensor_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Coordinates, aggregates, and caches values read from individual sensor tasks (DS18B20, DHT22, Atlas Scientific).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;atlas_scientific&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages data acquisition and calibration for the Atlas Scientific EZO pH and conductivity probes.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Acts as the central actuator gateway, processing on/off/pulse commands and sending them directly to the hardware relays.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically logs sensor values and thermal controller statuses into the MySQL database.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;temperature_gradient&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the rate of temperature changes to detect heater or ventilation inefficiencies/failures.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Orchestrates the heating loop, using hysteresis or safety controls to actuate the aquarium heater.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ventilation&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Controls the surface cooling fan loop, maintaining temperature thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;monitors&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Runs regular diagnostic checks and outputs warning/critical logs when states drift outside target thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Drives the fresh water top-off system, managing level switch states and pump runs with safety timers.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;feed&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the feeding schedule and actuates the automatic feeder.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;balling&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Oversees peristaltic mineral pumps to dose minerals into the aquarium according to schedule.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;tank_level_switch&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically reads the state of physical water level floats or simulator ports.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;watchdog&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs periodic heartbeats to ensure system safety loops are executing.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ds18b20&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically queries 1-Wire DS18B20 digital thermometers for water temperature.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;messaging&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Stub mock receiver that routes command queues when running on non-Linux platforms (like macOS).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Tokio Runtime Configuration ==&lt;br /&gt;
&lt;br /&gt;
The Tokio runtime is explicitly built at startup with a constrained configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread().worker_threads(1).max_blocking_threads(4).enable_all().build()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Why this configuration is chosen ===&lt;br /&gt;
# &amp;lt;code&amp;gt;worker_threads(1)&amp;lt;/code&amp;gt;: The application runs on Raspberry Pi. Limiting the event loop scheduler to one worker thread minimizes CPU context-switching overhead and prevents resource exhaustion.&lt;br /&gt;
# &amp;lt;code&amp;gt;max_blocking_threads(4)&amp;lt;/code&amp;gt;: Restricting the blocking thread pool prevents the runtime from spawning too many concurrent threads when database pings, file operations, or synchronous calculations are invoked (such as via &amp;lt;code&amp;gt;tokio::task::spawn_blocking&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;block_in_place&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Developer Diagnostics with Tokio Console ===&lt;br /&gt;
&lt;br /&gt;
For runtime inspection, task profiling, and detecting deadlocks or starvation, you can run the application with Tokio Console integration.&lt;br /&gt;
&lt;br /&gt;
==== Prerequisites &amp;amp; Compilation ====&lt;br /&gt;
Tokio Console requires unstable instrumentation.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;debug_tokio&amp;lt;/code&amp;gt; feature flag, which initializes the &amp;lt;code&amp;gt;console-subscriber&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;run.rs&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;RUSTFLAGS=&amp;quot;--cfg tokio_unstable&amp;quot; cargo build --features &amp;quot;debug_tokio&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
# The &amp;lt;code&amp;gt;tokio_unstable&amp;lt;/code&amp;gt; compiler flag ensures that Tokio exposes internal tracing endpoints (like the task builder names).&lt;br /&gt;
&lt;br /&gt;
==== Installing the Terminal Console ====&lt;br /&gt;
Install the client CLI on your development machine (macOS/Linux) via Cargo:&lt;br /&gt;
&amp;lt;code&amp;gt;cargo install --locked tokio-console&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Running and Reading ====&lt;br /&gt;
1. Launch the compiled application:&lt;br /&gt;
2. Open a separate terminal window and run &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;.&lt;br /&gt;
This presents a live dashboard of running tasks, showing busy poll times, total lifetime, and idle periods.&lt;br /&gt;
[[File:tokio-terminal.png | thumb | Screenshot of tokio-terminal during execution of the main application.]]&lt;br /&gt;
&lt;br /&gt;
=== Interpreting Task Size Warnings ===&lt;br /&gt;
When checking with &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;, you see warnings that tasks like &amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt; are 1024 bytes or larger.&lt;br /&gt;
* These warnings can be safely ignored.&lt;br /&gt;
* The size refers to the stack footprint of the compiler-generated &amp;lt;code&amp;gt;Future&amp;lt;/code&amp;gt; state machine. In debug builds, these are larger due to lack of optimization.&lt;br /&gt;
* Because these tasks are long-lived workers spawned once at startup and run until shutdown, they are allocated on the heap only once. A 1–2 KiB heap footprint has zero impact on application performance, fragmentation, or memory leaks.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=529</id>
		<title>Use of an asynchronous runtime for the main application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=529"/>
		<updated>2026-07-11T11:44:33Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Developer Diagnostics with Tokio Console */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
The Aquarium Control application uses a hybrid execution model to balance responsiveness, low latency, and hardware constraints. It uses the **Tokio asynchronous runtime** for coordinating sensor logging, network communication, safety interlocks, and scheduling. Concurrently, it spawns dedicated **native operating system threads** for operations requiring strict timing or blocking I/O (such as direct GPIO sensor reading).&lt;br /&gt;
&lt;br /&gt;
== Task and Thread Inventory ==&lt;br /&gt;
&lt;br /&gt;
The table below lists all tasks and threads running in the application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Description&lt;br /&gt;
! Type (OS Thread / Async Task)&lt;br /&gt;
! Usage of &amp;lt;code&amp;gt;block_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Main Thread'''&lt;br /&gt;
| Bootstraps the application, reads configuration, establishes database pools, builds the Tokio runtime, and blocks on the execution of all tasks.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| Yes (&amp;lt;code&amp;gt;rt.block_on&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;TcpCommunication&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Mock simulator thread that handles TCP connections and commands from the GUI simulator. Runs only when &amp;lt;code&amp;gt;use_simulator&amp;lt;/code&amp;gt; is active.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;Dht&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Direct driver reader thread for the DHT22 ambient temperature and humidity sensor. Handled as an OS thread to prevent blocking the async runtime.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;i2c_interface&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages serial I2C bus communications for sensor expansion units.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Captures shutdown signals (SIGINT, SIGTERM) and coordinates the graceful, phase-based shutdown of all sub-components.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;schedule_check&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs regular database checks against the active rules database to ensure operations are authorized based on the current time.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;sensor_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Coordinates, aggregates, and caches values read from individual sensor tasks (DS18B20, DHT22, Atlas Scientific).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;atlas_scientific&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages data acquisition and calibration for the Atlas Scientific EZO pH and conductivity probes.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Acts as the central actuator gateway, processing on/off/pulse commands and sending them directly to the hardware relays.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically logs sensor values and thermal controller statuses into the MySQL database.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;temperature_gradient&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the rate of temperature changes to detect heater or ventilation inefficiencies/failures.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Orchestrates the heating loop, using hysteresis or safety controls to actuate the aquarium heater.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ventilation&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Controls the surface cooling fan loop, maintaining temperature thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;monitors&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Runs regular diagnostic checks and outputs warning/critical logs when states drift outside target thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Drives the fresh water top-off system, managing level switch states and pump runs with safety timers.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;feed&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the feeding schedule and actuates the automatic feeder.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;balling&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Oversees peristaltic mineral pumps to dose minerals into the aquarium according to schedule.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;tank_level_switch&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically reads the state of physical water level floats or simulator ports.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;watchdog&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs periodic heartbeats to ensure system safety loops are executing.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ds18b20&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically queries 1-Wire DS18B20 digital thermometers for water temperature.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;messaging&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Stub mock receiver that routes command queues when running on non-Linux platforms (like macOS).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Tokio Runtime Configuration ==&lt;br /&gt;
&lt;br /&gt;
The Tokio runtime is explicitly built at startup with a constrained configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread().worker_threads(1).max_blocking_threads(4).enable_all().build()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Why this configuration is chosen ===&lt;br /&gt;
# &amp;lt;code&amp;gt;worker_threads(1)&amp;lt;/code&amp;gt;: The application runs on Raspberry Pi. Limiting the event loop scheduler to one worker thread minimizes CPU context-switching overhead and prevents resource exhaustion.&lt;br /&gt;
# &amp;lt;code&amp;gt;max_blocking_threads(4)&amp;lt;/code&amp;gt;: Restricting the blocking thread pool prevents the runtime from spawning too many concurrent threads when database pings, file operations, or synchronous calculations are invoked (such as via &amp;lt;code&amp;gt;tokio::task::spawn_blocking&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;block_in_place&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Developer Diagnostics with Tokio Console ===&lt;br /&gt;
&lt;br /&gt;
For runtime inspection, task profiling, and detecting deadlocks or starvation, you can run the application with Tokio Console integration.&lt;br /&gt;
&lt;br /&gt;
==== Prerequisites &amp;amp; Compilation ====&lt;br /&gt;
Tokio Console requires unstable instrumentation.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;debug_tokio&amp;lt;/code&amp;gt; feature flag, which initializes the &amp;lt;code&amp;gt;console-subscriber&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;run.rs&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;RUSTFLAGS=&amp;quot;--cfg tokio_unstable&amp;quot; cargo build --features &amp;quot;debug_tokio&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
# The &amp;lt;code&amp;gt;tokio_unstable&amp;lt;/code&amp;gt; compiler flag ensures that Tokio exposes internal tracing endpoints (like the task builder names).&lt;br /&gt;
&lt;br /&gt;
==== Installing the Terminal Console ====&lt;br /&gt;
Install the client CLI on your development machine (macOS/Linux) via Cargo:&lt;br /&gt;
&amp;lt;code&amp;gt;cargo install --locked tokio-console&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Running and Reading ====&lt;br /&gt;
1. Launch the compiled application:&lt;br /&gt;
2. Open a separate terminal window and run &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;.&lt;br /&gt;
This presents a live dashboard of running tasks, showing busy poll times, total lifetime, and idle periods.&lt;br /&gt;
[[File:File.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== Interpreting Task Size Warnings ===&lt;br /&gt;
When checking with &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;, you see warnings that tasks like &amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt; are 1024 bytes or larger.&lt;br /&gt;
* These warnings can be safely ignored.&lt;br /&gt;
* The size refers to the stack footprint of the compiler-generated &amp;lt;code&amp;gt;Future&amp;lt;/code&amp;gt; state machine. In debug builds, these are larger due to lack of optimization.&lt;br /&gt;
* Because these tasks are long-lived workers spawned once at startup and run until shutdown, they are allocated on the heap only once. A 1–2 KiB heap footprint has zero impact on application performance, fragmentation, or memory leaks.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=File:Tokio-console.png&amp;diff=528</id>
		<title>File:Tokio-console.png</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=File:Tokio-console.png&amp;diff=528"/>
		<updated>2026-07-11T11:43:37Z</updated>

		<summary type="html">&lt;p&gt;Uwe: Screenshot of tokio-console during execution of main application&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Screenshot of tokio-console during execution of main application&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=527</id>
		<title>Use of an asynchronous runtime for the main application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=527"/>
		<updated>2026-07-11T11:42:15Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Overview */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
The Aquarium Control application uses a hybrid execution model to balance responsiveness, low latency, and hardware constraints. It uses the **Tokio asynchronous runtime** for coordinating sensor logging, network communication, safety interlocks, and scheduling. Concurrently, it spawns dedicated **native operating system threads** for operations requiring strict timing or blocking I/O (such as direct GPIO sensor reading).&lt;br /&gt;
&lt;br /&gt;
== Task and Thread Inventory ==&lt;br /&gt;
&lt;br /&gt;
The table below lists all tasks and threads running in the application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Description&lt;br /&gt;
! Type (OS Thread / Async Task)&lt;br /&gt;
! Usage of &amp;lt;code&amp;gt;block_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Main Thread'''&lt;br /&gt;
| Bootstraps the application, reads configuration, establishes database pools, builds the Tokio runtime, and blocks on the execution of all tasks.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| Yes (&amp;lt;code&amp;gt;rt.block_on&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;TcpCommunication&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Mock simulator thread that handles TCP connections and commands from the GUI simulator. Runs only when &amp;lt;code&amp;gt;use_simulator&amp;lt;/code&amp;gt; is active.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;Dht&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Direct driver reader thread for the DHT22 ambient temperature and humidity sensor. Handled as an OS thread to prevent blocking the async runtime.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;i2c_interface&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages serial I2C bus communications for sensor expansion units.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Captures shutdown signals (SIGINT, SIGTERM) and coordinates the graceful, phase-based shutdown of all sub-components.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;schedule_check&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs regular database checks against the active rules database to ensure operations are authorized based on the current time.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;sensor_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Coordinates, aggregates, and caches values read from individual sensor tasks (DS18B20, DHT22, Atlas Scientific).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;atlas_scientific&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages data acquisition and calibration for the Atlas Scientific EZO pH and conductivity probes.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Acts as the central actuator gateway, processing on/off/pulse commands and sending them directly to the hardware relays.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically logs sensor values and thermal controller statuses into the MySQL database.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;temperature_gradient&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the rate of temperature changes to detect heater or ventilation inefficiencies/failures.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Orchestrates the heating loop, using hysteresis or safety controls to actuate the aquarium heater.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ventilation&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Controls the surface cooling fan loop, maintaining temperature thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;monitors&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Runs regular diagnostic checks and outputs warning/critical logs when states drift outside target thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Drives the fresh water top-off system, managing level switch states and pump runs with safety timers.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;feed&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the feeding schedule and actuates the automatic feeder.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;balling&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Oversees peristaltic mineral pumps to dose minerals into the aquarium according to schedule.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;tank_level_switch&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically reads the state of physical water level floats or simulator ports.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;watchdog&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs periodic heartbeats to ensure system safety loops are executing.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ds18b20&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically queries 1-Wire DS18B20 digital thermometers for water temperature.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;messaging&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Stub mock receiver that routes command queues when running on non-Linux platforms (like macOS).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Tokio Runtime Configuration ==&lt;br /&gt;
&lt;br /&gt;
The Tokio runtime is explicitly built at startup with a constrained configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread().worker_threads(1).max_blocking_threads(4).enable_all().build()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Why this configuration is chosen ===&lt;br /&gt;
# &amp;lt;code&amp;gt;worker_threads(1)&amp;lt;/code&amp;gt;: The application runs on Raspberry Pi. Limiting the event loop scheduler to one worker thread minimizes CPU context-switching overhead and prevents resource exhaustion.&lt;br /&gt;
# &amp;lt;code&amp;gt;max_blocking_threads(4)&amp;lt;/code&amp;gt;: Restricting the blocking thread pool prevents the runtime from spawning too many concurrent threads when database pings, file operations, or synchronous calculations are invoked (such as via &amp;lt;code&amp;gt;tokio::task::spawn_blocking&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;block_in_place&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Developer Diagnostics with Tokio Console ===&lt;br /&gt;
&lt;br /&gt;
For runtime inspection, task profiling, and detecting deadlocks or starvation, you can run the application with **Tokio Console** integration.&lt;br /&gt;
&lt;br /&gt;
==== Prerequisites &amp;amp; Compilation ====&lt;br /&gt;
Tokio Console requires unstable instrumentation.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;debug_tokio&amp;lt;/code&amp;gt; feature flag, which initializes the &amp;lt;code&amp;gt;console-subscriber&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;run.rs&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;RUSTFLAGS=&amp;quot;--cfg tokio_unstable&amp;quot; cargo build --features &amp;quot;debug_tokio&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
# The &amp;lt;code&amp;gt;tokio_unstable&amp;lt;/code&amp;gt; compiler flag ensures that Tokio exposes internal tracing endpoints (like the task builder names).&lt;br /&gt;
&lt;br /&gt;
==== Installing the Terminal Console ====&lt;br /&gt;
Install the client CLI on your development machine (macOS/Linux) via Cargo:&lt;br /&gt;
&amp;lt;code&amp;gt;cargo install --locked tokio-console&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Running and Reading ====&lt;br /&gt;
1. Launch the compiled application:&lt;br /&gt;
2. Open a separate terminal window and run &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;.&lt;br /&gt;
This presents a live dashboard of running tasks, showing busy poll times, total lifetime, and idle periods.&lt;br /&gt;
&lt;br /&gt;
=== Interpreting Task Size Warnings ===&lt;br /&gt;
When checking with &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;, you see warnings that tasks like &amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt; are 1024 bytes or larger.&lt;br /&gt;
* These warnings can be safely ignored.&lt;br /&gt;
* The size refers to the stack footprint of the compiler-generated &amp;lt;code&amp;gt;Future&amp;lt;/code&amp;gt; state machine. In debug builds, these are larger due to lack of optimization.&lt;br /&gt;
* Because these tasks are long-lived workers spawned once at startup and run until shutdown, they are allocated on the heap only once. A 1–2 KiB heap footprint has zero impact on application performance, fragmentation, or memory leaks.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=526</id>
		<title>Use of an asynchronous runtime for the main application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=526"/>
		<updated>2026-07-11T11:42:00Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Interpreting Task Size Warnings */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
The Aquarium Control application uses a hybrid execution model to balance responsiveness, low latency, and hardware constraints. It uses the **Tokio asynchronous runtime** for coordinating sensor logging, network communication, safety interlocks, and scheduling. Concurrently, it spawns dedicated **native operating system threads** for operations requiring strict timing or blocking I/O (such as direct GPIO sensor reading).&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Task and Thread Inventory ==&lt;br /&gt;
&lt;br /&gt;
The table below lists all tasks and threads running in the application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Description&lt;br /&gt;
! Type (OS Thread / Async Task)&lt;br /&gt;
! Usage of &amp;lt;code&amp;gt;block_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Main Thread'''&lt;br /&gt;
| Bootstraps the application, reads configuration, establishes database pools, builds the Tokio runtime, and blocks on the execution of all tasks.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| Yes (&amp;lt;code&amp;gt;rt.block_on&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;TcpCommunication&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Mock simulator thread that handles TCP connections and commands from the GUI simulator. Runs only when &amp;lt;code&amp;gt;use_simulator&amp;lt;/code&amp;gt; is active.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;Dht&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Direct driver reader thread for the DHT22 ambient temperature and humidity sensor. Handled as an OS thread to prevent blocking the async runtime.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;i2c_interface&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages serial I2C bus communications for sensor expansion units.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Captures shutdown signals (SIGINT, SIGTERM) and coordinates the graceful, phase-based shutdown of all sub-components.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;schedule_check&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs regular database checks against the active rules database to ensure operations are authorized based on the current time.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;sensor_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Coordinates, aggregates, and caches values read from individual sensor tasks (DS18B20, DHT22, Atlas Scientific).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;atlas_scientific&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages data acquisition and calibration for the Atlas Scientific EZO pH and conductivity probes.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Acts as the central actuator gateway, processing on/off/pulse commands and sending them directly to the hardware relays.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically logs sensor values and thermal controller statuses into the MySQL database.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;temperature_gradient&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the rate of temperature changes to detect heater or ventilation inefficiencies/failures.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Orchestrates the heating loop, using hysteresis or safety controls to actuate the aquarium heater.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ventilation&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Controls the surface cooling fan loop, maintaining temperature thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;monitors&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Runs regular diagnostic checks and outputs warning/critical logs when states drift outside target thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Drives the fresh water top-off system, managing level switch states and pump runs with safety timers.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;feed&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the feeding schedule and actuates the automatic feeder.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;balling&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Oversees peristaltic mineral pumps to dose minerals into the aquarium according to schedule.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;tank_level_switch&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically reads the state of physical water level floats or simulator ports.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;watchdog&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs periodic heartbeats to ensure system safety loops are executing.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ds18b20&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically queries 1-Wire DS18B20 digital thermometers for water temperature.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;messaging&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Stub mock receiver that routes command queues when running on non-Linux platforms (like macOS).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Tokio Runtime Configuration ==&lt;br /&gt;
&lt;br /&gt;
The Tokio runtime is explicitly built at startup with a constrained configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread().worker_threads(1).max_blocking_threads(4).enable_all().build()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Why this configuration is chosen ===&lt;br /&gt;
# &amp;lt;code&amp;gt;worker_threads(1)&amp;lt;/code&amp;gt;: The application runs on Raspberry Pi. Limiting the event loop scheduler to one worker thread minimizes CPU context-switching overhead and prevents resource exhaustion.&lt;br /&gt;
# &amp;lt;code&amp;gt;max_blocking_threads(4)&amp;lt;/code&amp;gt;: Restricting the blocking thread pool prevents the runtime from spawning too many concurrent threads when database pings, file operations, or synchronous calculations are invoked (such as via &amp;lt;code&amp;gt;tokio::task::spawn_blocking&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;block_in_place&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Developer Diagnostics with Tokio Console ===&lt;br /&gt;
&lt;br /&gt;
For runtime inspection, task profiling, and detecting deadlocks or starvation, you can run the application with **Tokio Console** integration.&lt;br /&gt;
&lt;br /&gt;
==== Prerequisites &amp;amp; Compilation ====&lt;br /&gt;
Tokio Console requires unstable instrumentation.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;debug_tokio&amp;lt;/code&amp;gt; feature flag, which initializes the &amp;lt;code&amp;gt;console-subscriber&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;run.rs&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;RUSTFLAGS=&amp;quot;--cfg tokio_unstable&amp;quot; cargo build --features &amp;quot;debug_tokio&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
# The &amp;lt;code&amp;gt;tokio_unstable&amp;lt;/code&amp;gt; compiler flag ensures that Tokio exposes internal tracing endpoints (like the task builder names).&lt;br /&gt;
&lt;br /&gt;
==== Installing the Terminal Console ====&lt;br /&gt;
Install the client CLI on your development machine (macOS/Linux) via Cargo:&lt;br /&gt;
&amp;lt;code&amp;gt;cargo install --locked tokio-console&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Running and Reading ====&lt;br /&gt;
1. Launch the compiled application:&lt;br /&gt;
2. Open a separate terminal window and run &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;.&lt;br /&gt;
This presents a live dashboard of running tasks, showing busy poll times, total lifetime, and idle periods.&lt;br /&gt;
&lt;br /&gt;
=== Interpreting Task Size Warnings ===&lt;br /&gt;
When checking with &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;, you see warnings that tasks like &amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt; are 1024 bytes or larger.&lt;br /&gt;
* These warnings can be safely ignored.&lt;br /&gt;
* The size refers to the stack footprint of the compiler-generated &amp;lt;code&amp;gt;Future&amp;lt;/code&amp;gt; state machine. In debug builds, these are larger due to lack of optimization.&lt;br /&gt;
* Because these tasks are long-lived workers spawned once at startup and run until shutdown, they are allocated on the heap only once. A 1–2 KiB heap footprint has zero impact on application performance, fragmentation, or memory leaks.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=525</id>
		<title>Use of an asynchronous runtime for the main application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=525"/>
		<updated>2026-07-11T11:41:46Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Interpreting Task Size Warnings */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
The Aquarium Control application uses a hybrid execution model to balance responsiveness, low latency, and hardware constraints. It uses the **Tokio asynchronous runtime** for coordinating sensor logging, network communication, safety interlocks, and scheduling. Concurrently, it spawns dedicated **native operating system threads** for operations requiring strict timing or blocking I/O (such as direct GPIO sensor reading).&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Task and Thread Inventory ==&lt;br /&gt;
&lt;br /&gt;
The table below lists all tasks and threads running in the application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Description&lt;br /&gt;
! Type (OS Thread / Async Task)&lt;br /&gt;
! Usage of &amp;lt;code&amp;gt;block_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Main Thread'''&lt;br /&gt;
| Bootstraps the application, reads configuration, establishes database pools, builds the Tokio runtime, and blocks on the execution of all tasks.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| Yes (&amp;lt;code&amp;gt;rt.block_on&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;TcpCommunication&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Mock simulator thread that handles TCP connections and commands from the GUI simulator. Runs only when &amp;lt;code&amp;gt;use_simulator&amp;lt;/code&amp;gt; is active.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;Dht&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Direct driver reader thread for the DHT22 ambient temperature and humidity sensor. Handled as an OS thread to prevent blocking the async runtime.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;i2c_interface&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages serial I2C bus communications for sensor expansion units.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Captures shutdown signals (SIGINT, SIGTERM) and coordinates the graceful, phase-based shutdown of all sub-components.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;schedule_check&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs regular database checks against the active rules database to ensure operations are authorized based on the current time.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;sensor_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Coordinates, aggregates, and caches values read from individual sensor tasks (DS18B20, DHT22, Atlas Scientific).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;atlas_scientific&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages data acquisition and calibration for the Atlas Scientific EZO pH and conductivity probes.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Acts as the central actuator gateway, processing on/off/pulse commands and sending them directly to the hardware relays.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically logs sensor values and thermal controller statuses into the MySQL database.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;temperature_gradient&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the rate of temperature changes to detect heater or ventilation inefficiencies/failures.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Orchestrates the heating loop, using hysteresis or safety controls to actuate the aquarium heater.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ventilation&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Controls the surface cooling fan loop, maintaining temperature thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;monitors&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Runs regular diagnostic checks and outputs warning/critical logs when states drift outside target thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Drives the fresh water top-off system, managing level switch states and pump runs with safety timers.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;feed&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the feeding schedule and actuates the automatic feeder.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;balling&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Oversees peristaltic mineral pumps to dose minerals into the aquarium according to schedule.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;tank_level_switch&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically reads the state of physical water level floats or simulator ports.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;watchdog&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs periodic heartbeats to ensure system safety loops are executing.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ds18b20&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically queries 1-Wire DS18B20 digital thermometers for water temperature.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;messaging&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Stub mock receiver that routes command queues when running on non-Linux platforms (like macOS).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Tokio Runtime Configuration ==&lt;br /&gt;
&lt;br /&gt;
The Tokio runtime is explicitly built at startup with a constrained configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread().worker_threads(1).max_blocking_threads(4).enable_all().build()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Why this configuration is chosen ===&lt;br /&gt;
# &amp;lt;code&amp;gt;worker_threads(1)&amp;lt;/code&amp;gt;: The application runs on Raspberry Pi. Limiting the event loop scheduler to one worker thread minimizes CPU context-switching overhead and prevents resource exhaustion.&lt;br /&gt;
# &amp;lt;code&amp;gt;max_blocking_threads(4)&amp;lt;/code&amp;gt;: Restricting the blocking thread pool prevents the runtime from spawning too many concurrent threads when database pings, file operations, or synchronous calculations are invoked (such as via &amp;lt;code&amp;gt;tokio::task::spawn_blocking&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;block_in_place&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Developer Diagnostics with Tokio Console ===&lt;br /&gt;
&lt;br /&gt;
For runtime inspection, task profiling, and detecting deadlocks or starvation, you can run the application with **Tokio Console** integration.&lt;br /&gt;
&lt;br /&gt;
==== Prerequisites &amp;amp; Compilation ====&lt;br /&gt;
Tokio Console requires unstable instrumentation.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;debug_tokio&amp;lt;/code&amp;gt; feature flag, which initializes the &amp;lt;code&amp;gt;console-subscriber&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;run.rs&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;RUSTFLAGS=&amp;quot;--cfg tokio_unstable&amp;quot; cargo build --features &amp;quot;debug_tokio&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
# The &amp;lt;code&amp;gt;tokio_unstable&amp;lt;/code&amp;gt; compiler flag ensures that Tokio exposes internal tracing endpoints (like the task builder names).&lt;br /&gt;
&lt;br /&gt;
==== Installing the Terminal Console ====&lt;br /&gt;
Install the client CLI on your development machine (macOS/Linux) via Cargo:&lt;br /&gt;
&amp;lt;code&amp;gt;cargo install --locked tokio-console&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Running and Reading ====&lt;br /&gt;
1. Launch the compiled application:&lt;br /&gt;
2. Open a separate terminal window and run &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;.&lt;br /&gt;
This presents a live dashboard of running tasks, showing busy poll times, total lifetime, and idle periods.&lt;br /&gt;
&lt;br /&gt;
=== Interpreting Task Size Warnings ===&lt;br /&gt;
When checking with &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;, you see warnings that tasks like &amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt; are **1024 bytes or larger**.&lt;br /&gt;
* These warnings can be safely ignored.&lt;br /&gt;
* The size refers to the stack footprint of the compiler-generated &amp;lt;code&amp;gt;Future&amp;lt;/code&amp;gt; state machine. In debug builds, these are larger due to lack of optimization.&lt;br /&gt;
* Because these tasks are long-lived workers spawned once at startup and run until shutdown, they are allocated on the heap only once. A 1–2 KiB heap footprint has zero impact on application performance, fragmentation, or memory leaks.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=524</id>
		<title>Use of an asynchronous runtime for the main application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=524"/>
		<updated>2026-07-11T11:41:28Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Prerequisites &amp;amp; Compilation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
The Aquarium Control application uses a hybrid execution model to balance responsiveness, low latency, and hardware constraints. It uses the **Tokio asynchronous runtime** for coordinating sensor logging, network communication, safety interlocks, and scheduling. Concurrently, it spawns dedicated **native operating system threads** for operations requiring strict timing or blocking I/O (such as direct GPIO sensor reading).&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Task and Thread Inventory ==&lt;br /&gt;
&lt;br /&gt;
The table below lists all tasks and threads running in the application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Description&lt;br /&gt;
! Type (OS Thread / Async Task)&lt;br /&gt;
! Usage of &amp;lt;code&amp;gt;block_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Main Thread'''&lt;br /&gt;
| Bootstraps the application, reads configuration, establishes database pools, builds the Tokio runtime, and blocks on the execution of all tasks.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| Yes (&amp;lt;code&amp;gt;rt.block_on&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;TcpCommunication&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Mock simulator thread that handles TCP connections and commands from the GUI simulator. Runs only when &amp;lt;code&amp;gt;use_simulator&amp;lt;/code&amp;gt; is active.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;Dht&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Direct driver reader thread for the DHT22 ambient temperature and humidity sensor. Handled as an OS thread to prevent blocking the async runtime.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;i2c_interface&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages serial I2C bus communications for sensor expansion units.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Captures shutdown signals (SIGINT, SIGTERM) and coordinates the graceful, phase-based shutdown of all sub-components.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;schedule_check&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs regular database checks against the active rules database to ensure operations are authorized based on the current time.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;sensor_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Coordinates, aggregates, and caches values read from individual sensor tasks (DS18B20, DHT22, Atlas Scientific).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;atlas_scientific&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages data acquisition and calibration for the Atlas Scientific EZO pH and conductivity probes.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Acts as the central actuator gateway, processing on/off/pulse commands and sending them directly to the hardware relays.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically logs sensor values and thermal controller statuses into the MySQL database.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;temperature_gradient&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the rate of temperature changes to detect heater or ventilation inefficiencies/failures.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Orchestrates the heating loop, using hysteresis or safety controls to actuate the aquarium heater.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ventilation&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Controls the surface cooling fan loop, maintaining temperature thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;monitors&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Runs regular diagnostic checks and outputs warning/critical logs when states drift outside target thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Drives the fresh water top-off system, managing level switch states and pump runs with safety timers.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;feed&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the feeding schedule and actuates the automatic feeder.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;balling&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Oversees peristaltic mineral pumps to dose minerals into the aquarium according to schedule.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;tank_level_switch&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically reads the state of physical water level floats or simulator ports.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;watchdog&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs periodic heartbeats to ensure system safety loops are executing.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ds18b20&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically queries 1-Wire DS18B20 digital thermometers for water temperature.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;messaging&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Stub mock receiver that routes command queues when running on non-Linux platforms (like macOS).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Tokio Runtime Configuration ==&lt;br /&gt;
&lt;br /&gt;
The Tokio runtime is explicitly built at startup with a constrained configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread().worker_threads(1).max_blocking_threads(4).enable_all().build()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Why this configuration is chosen ===&lt;br /&gt;
# &amp;lt;code&amp;gt;worker_threads(1)&amp;lt;/code&amp;gt;: The application runs on Raspberry Pi. Limiting the event loop scheduler to one worker thread minimizes CPU context-switching overhead and prevents resource exhaustion.&lt;br /&gt;
# &amp;lt;code&amp;gt;max_blocking_threads(4)&amp;lt;/code&amp;gt;: Restricting the blocking thread pool prevents the runtime from spawning too many concurrent threads when database pings, file operations, or synchronous calculations are invoked (such as via &amp;lt;code&amp;gt;tokio::task::spawn_blocking&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;block_in_place&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Developer Diagnostics with Tokio Console ===&lt;br /&gt;
&lt;br /&gt;
For runtime inspection, task profiling, and detecting deadlocks or starvation, you can run the application with **Tokio Console** integration.&lt;br /&gt;
&lt;br /&gt;
==== Prerequisites &amp;amp; Compilation ====&lt;br /&gt;
Tokio Console requires unstable instrumentation.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;debug_tokio&amp;lt;/code&amp;gt; feature flag, which initializes the &amp;lt;code&amp;gt;console-subscriber&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;run.rs&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;RUSTFLAGS=&amp;quot;--cfg tokio_unstable&amp;quot; cargo build --features &amp;quot;debug_tokio&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
# The &amp;lt;code&amp;gt;tokio_unstable&amp;lt;/code&amp;gt; compiler flag ensures that Tokio exposes internal tracing endpoints (like the task builder names).&lt;br /&gt;
&lt;br /&gt;
==== Installing the Terminal Console ====&lt;br /&gt;
Install the client CLI on your development machine (macOS/Linux) via Cargo:&lt;br /&gt;
&amp;lt;code&amp;gt;cargo install --locked tokio-console&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Running and Reading ====&lt;br /&gt;
1. Launch the compiled application:&lt;br /&gt;
2. Open a separate terminal window and run &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;.&lt;br /&gt;
This presents a live dashboard of running tasks, showing busy poll times, total lifetime, and idle periods.&lt;br /&gt;
&lt;br /&gt;
=== Interpreting Task Size Warnings ===&lt;br /&gt;
When checking with &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;, you see warnings that tasks like &amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt; are **1024 bytes or larger**.&lt;br /&gt;
* **These warnings can be safely ignored.**&lt;br /&gt;
* The size refers to the stack footprint of the compiler-generated &amp;lt;code&amp;gt;Future&amp;lt;/code&amp;gt; state machine. In debug builds, these are larger due to lack of optimization.&lt;br /&gt;
* Because these tasks are long-lived workers spawned once at startup and run until shutdown, they are allocated on the heap only once. A 1–2 KiB heap footprint has zero impact on application performance, fragmentation, or memory leaks.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=523</id>
		<title>Use of an asynchronous runtime for the main application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=523"/>
		<updated>2026-07-11T11:41:08Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Prerequisites &amp;amp; Compilation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
The Aquarium Control application uses a hybrid execution model to balance responsiveness, low latency, and hardware constraints. It uses the **Tokio asynchronous runtime** for coordinating sensor logging, network communication, safety interlocks, and scheduling. Concurrently, it spawns dedicated **native operating system threads** for operations requiring strict timing or blocking I/O (such as direct GPIO sensor reading).&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Task and Thread Inventory ==&lt;br /&gt;
&lt;br /&gt;
The table below lists all tasks and threads running in the application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Description&lt;br /&gt;
! Type (OS Thread / Async Task)&lt;br /&gt;
! Usage of &amp;lt;code&amp;gt;block_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Main Thread'''&lt;br /&gt;
| Bootstraps the application, reads configuration, establishes database pools, builds the Tokio runtime, and blocks on the execution of all tasks.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| Yes (&amp;lt;code&amp;gt;rt.block_on&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;TcpCommunication&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Mock simulator thread that handles TCP connections and commands from the GUI simulator. Runs only when &amp;lt;code&amp;gt;use_simulator&amp;lt;/code&amp;gt; is active.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;Dht&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Direct driver reader thread for the DHT22 ambient temperature and humidity sensor. Handled as an OS thread to prevent blocking the async runtime.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;i2c_interface&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages serial I2C bus communications for sensor expansion units.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Captures shutdown signals (SIGINT, SIGTERM) and coordinates the graceful, phase-based shutdown of all sub-components.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;schedule_check&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs regular database checks against the active rules database to ensure operations are authorized based on the current time.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;sensor_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Coordinates, aggregates, and caches values read from individual sensor tasks (DS18B20, DHT22, Atlas Scientific).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;atlas_scientific&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages data acquisition and calibration for the Atlas Scientific EZO pH and conductivity probes.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Acts as the central actuator gateway, processing on/off/pulse commands and sending them directly to the hardware relays.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically logs sensor values and thermal controller statuses into the MySQL database.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;temperature_gradient&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the rate of temperature changes to detect heater or ventilation inefficiencies/failures.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Orchestrates the heating loop, using hysteresis or safety controls to actuate the aquarium heater.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ventilation&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Controls the surface cooling fan loop, maintaining temperature thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;monitors&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Runs regular diagnostic checks and outputs warning/critical logs when states drift outside target thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Drives the fresh water top-off system, managing level switch states and pump runs with safety timers.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;feed&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the feeding schedule and actuates the automatic feeder.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;balling&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Oversees peristaltic mineral pumps to dose minerals into the aquarium according to schedule.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;tank_level_switch&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically reads the state of physical water level floats or simulator ports.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;watchdog&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs periodic heartbeats to ensure system safety loops are executing.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ds18b20&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically queries 1-Wire DS18B20 digital thermometers for water temperature.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;messaging&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Stub mock receiver that routes command queues when running on non-Linux platforms (like macOS).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Tokio Runtime Configuration ==&lt;br /&gt;
&lt;br /&gt;
The Tokio runtime is explicitly built at startup with a constrained configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread().worker_threads(1).max_blocking_threads(4).enable_all().build()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Why this configuration is chosen ===&lt;br /&gt;
# &amp;lt;code&amp;gt;worker_threads(1)&amp;lt;/code&amp;gt;: The application runs on Raspberry Pi. Limiting the event loop scheduler to one worker thread minimizes CPU context-switching overhead and prevents resource exhaustion.&lt;br /&gt;
# &amp;lt;code&amp;gt;max_blocking_threads(4)&amp;lt;/code&amp;gt;: Restricting the blocking thread pool prevents the runtime from spawning too many concurrent threads when database pings, file operations, or synchronous calculations are invoked (such as via &amp;lt;code&amp;gt;tokio::task::spawn_blocking&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;block_in_place&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Developer Diagnostics with Tokio Console ===&lt;br /&gt;
&lt;br /&gt;
For runtime inspection, task profiling, and detecting deadlocks or starvation, you can run the application with **Tokio Console** integration.&lt;br /&gt;
&lt;br /&gt;
==== Prerequisites &amp;amp; Compilation ====&lt;br /&gt;
Tokio Console requires unstable instrumentation.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;debug_tokio&amp;lt;/code&amp;gt; feature flag, which initializes the &amp;lt;code&amp;gt;console-subscriber&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;run.rs&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;code&amp;gt;RUSTFLAGS=&amp;quot;--cfg tokio_unstable&amp;quot; cargo build --features &amp;quot;debug_tokio&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
# The &amp;lt;code&amp;gt;tokio_unstable&amp;lt;/code&amp;gt; compiler flag ensures that Tokio exposes internal tracing endpoints (like the task builder names).&lt;br /&gt;
&lt;br /&gt;
==== Installing the Terminal Console ====&lt;br /&gt;
Install the client CLI on your development machine (macOS/Linux) via Cargo:&lt;br /&gt;
&amp;lt;code&amp;gt;cargo install --locked tokio-console&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Running and Reading ====&lt;br /&gt;
1. Launch the compiled application:&lt;br /&gt;
2. Open a separate terminal window and run &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;.&lt;br /&gt;
This presents a live dashboard of running tasks, showing busy poll times, total lifetime, and idle periods.&lt;br /&gt;
&lt;br /&gt;
=== Interpreting Task Size Warnings ===&lt;br /&gt;
When checking with &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;, you see warnings that tasks like &amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt; are **1024 bytes or larger**.&lt;br /&gt;
* **These warnings can be safely ignored.**&lt;br /&gt;
* The size refers to the stack footprint of the compiler-generated &amp;lt;code&amp;gt;Future&amp;lt;/code&amp;gt; state machine. In debug builds, these are larger due to lack of optimization.&lt;br /&gt;
* Because these tasks are long-lived workers spawned once at startup and run until shutdown, they are allocated on the heap only once. A 1–2 KiB heap footprint has zero impact on application performance, fragmentation, or memory leaks.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=522</id>
		<title>Use of an asynchronous runtime for the main application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=522"/>
		<updated>2026-07-11T11:40:50Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Why this configuration is chosen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
The Aquarium Control application uses a hybrid execution model to balance responsiveness, low latency, and hardware constraints. It uses the **Tokio asynchronous runtime** for coordinating sensor logging, network communication, safety interlocks, and scheduling. Concurrently, it spawns dedicated **native operating system threads** for operations requiring strict timing or blocking I/O (such as direct GPIO sensor reading).&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Task and Thread Inventory ==&lt;br /&gt;
&lt;br /&gt;
The table below lists all tasks and threads running in the application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Description&lt;br /&gt;
! Type (OS Thread / Async Task)&lt;br /&gt;
! Usage of &amp;lt;code&amp;gt;block_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Main Thread'''&lt;br /&gt;
| Bootstraps the application, reads configuration, establishes database pools, builds the Tokio runtime, and blocks on the execution of all tasks.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| Yes (&amp;lt;code&amp;gt;rt.block_on&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;TcpCommunication&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Mock simulator thread that handles TCP connections and commands from the GUI simulator. Runs only when &amp;lt;code&amp;gt;use_simulator&amp;lt;/code&amp;gt; is active.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;Dht&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Direct driver reader thread for the DHT22 ambient temperature and humidity sensor. Handled as an OS thread to prevent blocking the async runtime.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;i2c_interface&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages serial I2C bus communications for sensor expansion units.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Captures shutdown signals (SIGINT, SIGTERM) and coordinates the graceful, phase-based shutdown of all sub-components.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;schedule_check&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs regular database checks against the active rules database to ensure operations are authorized based on the current time.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;sensor_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Coordinates, aggregates, and caches values read from individual sensor tasks (DS18B20, DHT22, Atlas Scientific).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;atlas_scientific&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages data acquisition and calibration for the Atlas Scientific EZO pH and conductivity probes.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Acts as the central actuator gateway, processing on/off/pulse commands and sending them directly to the hardware relays.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically logs sensor values and thermal controller statuses into the MySQL database.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;temperature_gradient&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the rate of temperature changes to detect heater or ventilation inefficiencies/failures.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Orchestrates the heating loop, using hysteresis or safety controls to actuate the aquarium heater.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ventilation&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Controls the surface cooling fan loop, maintaining temperature thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;monitors&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Runs regular diagnostic checks and outputs warning/critical logs when states drift outside target thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Drives the fresh water top-off system, managing level switch states and pump runs with safety timers.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;feed&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the feeding schedule and actuates the automatic feeder.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;balling&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Oversees peristaltic mineral pumps to dose minerals into the aquarium according to schedule.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;tank_level_switch&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically reads the state of physical water level floats or simulator ports.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;watchdog&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs periodic heartbeats to ensure system safety loops are executing.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ds18b20&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically queries 1-Wire DS18B20 digital thermometers for water temperature.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;messaging&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Stub mock receiver that routes command queues when running on non-Linux platforms (like macOS).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Tokio Runtime Configuration ==&lt;br /&gt;
&lt;br /&gt;
The Tokio runtime is explicitly built at startup with a constrained configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread().worker_threads(1).max_blocking_threads(4).enable_all().build()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Why this configuration is chosen ===&lt;br /&gt;
# &amp;lt;code&amp;gt;worker_threads(1)&amp;lt;/code&amp;gt;: The application runs on Raspberry Pi. Limiting the event loop scheduler to one worker thread minimizes CPU context-switching overhead and prevents resource exhaustion.&lt;br /&gt;
# &amp;lt;code&amp;gt;max_blocking_threads(4)&amp;lt;/code&amp;gt;: Restricting the blocking thread pool prevents the runtime from spawning too many concurrent threads when database pings, file operations, or synchronous calculations are invoked (such as via &amp;lt;code&amp;gt;tokio::task::spawn_blocking&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;block_in_place&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Developer Diagnostics with Tokio Console ===&lt;br /&gt;
&lt;br /&gt;
For runtime inspection, task profiling, and detecting deadlocks or starvation, you can run the application with **Tokio Console** integration.&lt;br /&gt;
&lt;br /&gt;
==== Prerequisites &amp;amp; Compilation ====&lt;br /&gt;
Tokio Console requires unstable instrumentation.&lt;br /&gt;
1. Use the &amp;lt;code&amp;gt;debug_tokio&amp;lt;/code&amp;gt; feature flag, which initializes the &amp;lt;code&amp;gt;console-subscriber&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;run.rs&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;code&amp;gt;RUSTFLAGS=&amp;quot;--cfg tokio_unstable&amp;quot; cargo build --features &amp;quot;debug_tokio&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
2. The &amp;lt;code&amp;gt;tokio_unstable&amp;lt;/code&amp;gt; compiler flag ensures that Tokio exposes internal tracing endpoints (like the task builder names).&lt;br /&gt;
&lt;br /&gt;
==== Installing the Terminal Console ====&lt;br /&gt;
Install the client CLI on your development machine (macOS/Linux) via Cargo:&lt;br /&gt;
&amp;lt;code&amp;gt;cargo install --locked tokio-console&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Running and Reading ====&lt;br /&gt;
1. Launch the compiled application:&lt;br /&gt;
2. Open a separate terminal window and run &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;.&lt;br /&gt;
This presents a live dashboard of running tasks, showing busy poll times, total lifetime, and idle periods.&lt;br /&gt;
&lt;br /&gt;
=== Interpreting Task Size Warnings ===&lt;br /&gt;
When checking with &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;, you see warnings that tasks like &amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt; are **1024 bytes or larger**.&lt;br /&gt;
* **These warnings can be safely ignored.**&lt;br /&gt;
* The size refers to the stack footprint of the compiler-generated &amp;lt;code&amp;gt;Future&amp;lt;/code&amp;gt; state machine. In debug builds, these are larger due to lack of optimization.&lt;br /&gt;
* Because these tasks are long-lived workers spawned once at startup and run until shutdown, they are allocated on the heap only once. A 1–2 KiB heap footprint has zero impact on application performance, fragmentation, or memory leaks.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=521</id>
		<title>Use of an asynchronous runtime for the main application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=521"/>
		<updated>2026-07-11T11:40:04Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Tokio Runtime Configuration */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
The Aquarium Control application uses a hybrid execution model to balance responsiveness, low latency, and hardware constraints. It uses the **Tokio asynchronous runtime** for coordinating sensor logging, network communication, safety interlocks, and scheduling. Concurrently, it spawns dedicated **native operating system threads** for operations requiring strict timing or blocking I/O (such as direct GPIO sensor reading).&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Task and Thread Inventory ==&lt;br /&gt;
&lt;br /&gt;
The table below lists all tasks and threads running in the application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Description&lt;br /&gt;
! Type (OS Thread / Async Task)&lt;br /&gt;
! Usage of &amp;lt;code&amp;gt;block_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Main Thread'''&lt;br /&gt;
| Bootstraps the application, reads configuration, establishes database pools, builds the Tokio runtime, and blocks on the execution of all tasks.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| Yes (&amp;lt;code&amp;gt;rt.block_on&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;TcpCommunication&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Mock simulator thread that handles TCP connections and commands from the GUI simulator. Runs only when &amp;lt;code&amp;gt;use_simulator&amp;lt;/code&amp;gt; is active.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;Dht&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Direct driver reader thread for the DHT22 ambient temperature and humidity sensor. Handled as an OS thread to prevent blocking the async runtime.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;i2c_interface&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages serial I2C bus communications for sensor expansion units.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Captures shutdown signals (SIGINT, SIGTERM) and coordinates the graceful, phase-based shutdown of all sub-components.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;schedule_check&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs regular database checks against the active rules database to ensure operations are authorized based on the current time.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;sensor_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Coordinates, aggregates, and caches values read from individual sensor tasks (DS18B20, DHT22, Atlas Scientific).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;atlas_scientific&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages data acquisition and calibration for the Atlas Scientific EZO pH and conductivity probes.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Acts as the central actuator gateway, processing on/off/pulse commands and sending them directly to the hardware relays.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically logs sensor values and thermal controller statuses into the MySQL database.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;temperature_gradient&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the rate of temperature changes to detect heater or ventilation inefficiencies/failures.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Orchestrates the heating loop, using hysteresis or safety controls to actuate the aquarium heater.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ventilation&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Controls the surface cooling fan loop, maintaining temperature thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;monitors&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Runs regular diagnostic checks and outputs warning/critical logs when states drift outside target thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Drives the fresh water top-off system, managing level switch states and pump runs with safety timers.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;feed&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the feeding schedule and actuates the automatic feeder.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;balling&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Oversees peristaltic mineral pumps to dose minerals into the aquarium according to schedule.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;tank_level_switch&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically reads the state of physical water level floats or simulator ports.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;watchdog&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs periodic heartbeats to ensure system safety loops are executing.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ds18b20&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically queries 1-Wire DS18B20 digital thermometers for water temperature.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;messaging&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Stub mock receiver that routes command queues when running on non-Linux platforms (like macOS).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Tokio Runtime Configuration ==&lt;br /&gt;
&lt;br /&gt;
The Tokio runtime is explicitly built at startup with a constrained configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread().worker_threads(1).max_blocking_threads(4).enable_all().build()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Why this configuration is chosen ===&lt;br /&gt;
1. &amp;lt;code&amp;gt;worker_threads(1)&amp;lt;/code&amp;gt;: The application runs on Raspberry Pi. Limiting the event loop scheduler to one worker thread minimizes CPU context-switching overhead and prevents resource exhaustion.&lt;br /&gt;
2. &amp;lt;code&amp;gt;max_blocking_threads(4)&amp;lt;/code&amp;gt;: Restricting the blocking thread pool prevents the runtime from spawning too many concurrent threads when database pings, file operations, or synchronous calculations are invoked (such as via &amp;lt;code&amp;gt;tokio::task::spawn_blocking&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;block_in_place&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Developer Diagnostics with Tokio Console ===&lt;br /&gt;
&lt;br /&gt;
For runtime inspection, task profiling, and detecting deadlocks or starvation, you can run the application with **Tokio Console** integration.&lt;br /&gt;
&lt;br /&gt;
==== Prerequisites &amp;amp; Compilation ====&lt;br /&gt;
Tokio Console requires unstable instrumentation.&lt;br /&gt;
1. Use the &amp;lt;code&amp;gt;debug_tokio&amp;lt;/code&amp;gt; feature flag, which initializes the &amp;lt;code&amp;gt;console-subscriber&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;run.rs&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;code&amp;gt;RUSTFLAGS=&amp;quot;--cfg tokio_unstable&amp;quot; cargo build --features &amp;quot;debug_tokio&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
2. The &amp;lt;code&amp;gt;tokio_unstable&amp;lt;/code&amp;gt; compiler flag ensures that Tokio exposes internal tracing endpoints (like the task builder names).&lt;br /&gt;
&lt;br /&gt;
==== Installing the Terminal Console ====&lt;br /&gt;
Install the client CLI on your development machine (macOS/Linux) via Cargo:&lt;br /&gt;
&amp;lt;code&amp;gt;cargo install --locked tokio-console&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Running and Reading ====&lt;br /&gt;
1. Launch the compiled application:&lt;br /&gt;
2. Open a separate terminal window and run &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;.&lt;br /&gt;
This presents a live dashboard of running tasks, showing busy poll times, total lifetime, and idle periods.&lt;br /&gt;
&lt;br /&gt;
=== Interpreting Task Size Warnings ===&lt;br /&gt;
When checking with &amp;lt;code&amp;gt;tokio-console&amp;lt;/code&amp;gt;, you see warnings that tasks like &amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt; are **1024 bytes or larger**.&lt;br /&gt;
* **These warnings can be safely ignored.**&lt;br /&gt;
* The size refers to the stack footprint of the compiler-generated &amp;lt;code&amp;gt;Future&amp;lt;/code&amp;gt; state machine. In debug builds, these are larger due to lack of optimization.&lt;br /&gt;
* Because these tasks are long-lived workers spawned once at startup and run until shutdown, they are allocated on the heap only once. A 1–2 KiB heap footprint has zero impact on application performance, fragmentation, or memory leaks.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=520</id>
		<title>Use of an asynchronous runtime for the main application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=520"/>
		<updated>2026-07-11T11:36:31Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Tokio Runtime Configuration */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
The Aquarium Control application uses a hybrid execution model to balance responsiveness, low latency, and hardware constraints. It uses the **Tokio asynchronous runtime** for coordinating sensor logging, network communication, safety interlocks, and scheduling. Concurrently, it spawns dedicated **native operating system threads** for operations requiring strict timing or blocking I/O (such as direct GPIO sensor reading).&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Task and Thread Inventory ==&lt;br /&gt;
&lt;br /&gt;
The table below lists all tasks and threads running in the application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Description&lt;br /&gt;
! Type (OS Thread / Async Task)&lt;br /&gt;
! Usage of &amp;lt;code&amp;gt;block_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Main Thread'''&lt;br /&gt;
| Bootstraps the application, reads configuration, establishes database pools, builds the Tokio runtime, and blocks on the execution of all tasks.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| Yes (&amp;lt;code&amp;gt;rt.block_on&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;TcpCommunication&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Mock simulator thread that handles TCP connections and commands from the GUI simulator. Runs only when &amp;lt;code&amp;gt;use_simulator&amp;lt;/code&amp;gt; is active.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;Dht&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Direct driver reader thread for the DHT22 ambient temperature and humidity sensor. Handled as an OS thread to prevent blocking the async runtime.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;i2c_interface&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages serial I2C bus communications for sensor expansion units.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Captures shutdown signals (SIGINT, SIGTERM) and coordinates the graceful, phase-based shutdown of all sub-components.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;schedule_check&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs regular database checks against the active rules database to ensure operations are authorized based on the current time.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;sensor_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Coordinates, aggregates, and caches values read from individual sensor tasks (DS18B20, DHT22, Atlas Scientific).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;atlas_scientific&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages data acquisition and calibration for the Atlas Scientific EZO pH and conductivity probes.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Acts as the central actuator gateway, processing on/off/pulse commands and sending them directly to the hardware relays.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically logs sensor values and thermal controller statuses into the MySQL database.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;temperature_gradient&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the rate of temperature changes to detect heater or ventilation inefficiencies/failures.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Orchestrates the heating loop, using hysteresis or safety controls to actuate the aquarium heater.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ventilation&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Controls the surface cooling fan loop, maintaining temperature thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;monitors&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Runs regular diagnostic checks and outputs warning/critical logs when states drift outside target thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Drives the fresh water top-off system, managing level switch states and pump runs with safety timers.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;feed&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the feeding schedule and actuates the automatic feeder.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;balling&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Oversees peristaltic mineral pumps to dose minerals into the aquarium according to schedule.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;tank_level_switch&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically reads the state of physical water level floats or simulator ports.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;watchdog&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs periodic heartbeats to ensure system safety loops are executing.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ds18b20&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically queries 1-Wire DS18B20 digital thermometers for water temperature.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;messaging&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Stub mock receiver that routes command queues when running on non-Linux platforms (like macOS).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Tokio Runtime Configuration ==&lt;br /&gt;
&lt;br /&gt;
The Tokio runtime is explicitly built at startup with a constrained configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;rust&amp;quot;&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread()&lt;br /&gt;
    .worker_threads(1)&lt;br /&gt;
    .max_blocking_threads(4)&lt;br /&gt;
    .enable_all()&lt;br /&gt;
    .build()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread().worker_threads(1).max_blocking_threads(4).enable_all().build()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Why this configuration is chosen ===&lt;br /&gt;
1. **`worker_threads(1)`**: The application runs on Raspberry Pi. Limiting the event loop scheduler to one worker thread minimizes CPU context-switching overhead and prevents resource exhaustion.&lt;br /&gt;
2. **`max_blocking_threads(4)`**: Restricting the blocking thread pool prevents the runtime from spawning too many concurrent threads when database pings, file operations, or synchronous calculations are invoked (such as via `tokio::task::spawn_blocking` or `block_in_place`).&lt;br /&gt;
&lt;br /&gt;
=== Developer Diagnostics with Tokio Console ===&lt;br /&gt;
&lt;br /&gt;
For runtime inspection, task profiling, and detecting deadlocks or starvation, you can run the application with **Tokio Console** integration.&lt;br /&gt;
&lt;br /&gt;
==== Prerequisites &amp;amp; Compilation ====&lt;br /&gt;
Tokio Console requires unstable instrumentation.&lt;br /&gt;
1. Use the `debug_tokio` feature flag, which initializes the `console-subscriber` in `run.rs`:&lt;br /&gt;
   ```bash&lt;br /&gt;
   RUSTFLAGS=&amp;quot;--cfg tokio_unstable&amp;quot; cargo build --features &amp;quot;debug_tokio&amp;quot;&lt;br /&gt;
   ```&lt;br /&gt;
2. The `tokio_unstable` compiler flag ensures that Tokio exposes internal tracing endpoints (like the task builder names).&lt;br /&gt;
&lt;br /&gt;
==== Installing the Terminal Console ====&lt;br /&gt;
Install the client CLI on your development machine (macOS/Linux) via Cargo:&lt;br /&gt;
```bash&lt;br /&gt;
cargo install --locked tokio-console&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
==== Running and Reading ====&lt;br /&gt;
1. Launch the compiled application:&lt;br /&gt;
2. Open a separate terminal window and run:&lt;br /&gt;
   ```bash&lt;br /&gt;
   tokio-console&lt;br /&gt;
   ```&lt;br /&gt;
This presents a live dashboard of running tasks, showing busy poll times, total lifetime, and idle periods.&lt;br /&gt;
&lt;br /&gt;
=== Interpreting Task Size Warnings ===&lt;br /&gt;
When checking with `tokio-console`, you see warnings that tasks like `signal_handler`, `relay_manager`, `data_logger`, or `heating` are **1024 bytes or larger**.&lt;br /&gt;
* **These warnings can be safely ignored.**&lt;br /&gt;
* The size refers to the stack footprint of the compiler-generated `Future` state machine. In debug builds, these are larger due to lack of optimization.&lt;br /&gt;
* Because these tasks are long-lived workers spawned once at startup and run until shutdown, they are allocated on the heap only once. A 1–2 KiB heap footprint has zero impact on application performance, fragmentation, or memory leaks.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=519</id>
		<title>Use of an asynchronous runtime for the main application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=519"/>
		<updated>2026-07-11T11:36:20Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Tokio Runtime Configuration */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
The Aquarium Control application uses a hybrid execution model to balance responsiveness, low latency, and hardware constraints. It uses the **Tokio asynchronous runtime** for coordinating sensor logging, network communication, safety interlocks, and scheduling. Concurrently, it spawns dedicated **native operating system threads** for operations requiring strict timing or blocking I/O (such as direct GPIO sensor reading).&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Task and Thread Inventory ==&lt;br /&gt;
&lt;br /&gt;
The table below lists all tasks and threads running in the application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Description&lt;br /&gt;
! Type (OS Thread / Async Task)&lt;br /&gt;
! Usage of &amp;lt;code&amp;gt;block_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Main Thread'''&lt;br /&gt;
| Bootstraps the application, reads configuration, establishes database pools, builds the Tokio runtime, and blocks on the execution of all tasks.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| Yes (&amp;lt;code&amp;gt;rt.block_on&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;TcpCommunication&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Mock simulator thread that handles TCP connections and commands from the GUI simulator. Runs only when &amp;lt;code&amp;gt;use_simulator&amp;lt;/code&amp;gt; is active.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;Dht&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Direct driver reader thread for the DHT22 ambient temperature and humidity sensor. Handled as an OS thread to prevent blocking the async runtime.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;i2c_interface&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages serial I2C bus communications for sensor expansion units.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Captures shutdown signals (SIGINT, SIGTERM) and coordinates the graceful, phase-based shutdown of all sub-components.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;schedule_check&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs regular database checks against the active rules database to ensure operations are authorized based on the current time.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;sensor_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Coordinates, aggregates, and caches values read from individual sensor tasks (DS18B20, DHT22, Atlas Scientific).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;atlas_scientific&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages data acquisition and calibration for the Atlas Scientific EZO pH and conductivity probes.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Acts as the central actuator gateway, processing on/off/pulse commands and sending them directly to the hardware relays.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically logs sensor values and thermal controller statuses into the MySQL database.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;temperature_gradient&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the rate of temperature changes to detect heater or ventilation inefficiencies/failures.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Orchestrates the heating loop, using hysteresis or safety controls to actuate the aquarium heater.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ventilation&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Controls the surface cooling fan loop, maintaining temperature thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;monitors&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Runs regular diagnostic checks and outputs warning/critical logs when states drift outside target thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Drives the fresh water top-off system, managing level switch states and pump runs with safety timers.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;feed&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the feeding schedule and actuates the automatic feeder.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;balling&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Oversees peristaltic mineral pumps to dose minerals into the aquarium according to schedule.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;tank_level_switch&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically reads the state of physical water level floats or simulator ports.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;watchdog&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs periodic heartbeats to ensure system safety loops are executing.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ds18b20&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically queries 1-Wire DS18B20 digital thermometers for water temperature.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;messaging&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Stub mock receiver that routes command queues when running on non-Linux platforms (like macOS).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Tokio Runtime Configuration ==&lt;br /&gt;
&lt;br /&gt;
The Tokio runtime is explicitly built at startup with a constrained configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;rust&amp;quot; line&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread()&lt;br /&gt;
    .worker_threads(1)&lt;br /&gt;
    .max_blocking_threads(4)&lt;br /&gt;
    .enable_all()&lt;br /&gt;
    .build()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread().worker_threads(1).max_blocking_threads(4).enable_all().build()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Why this configuration is chosen ===&lt;br /&gt;
1. **`worker_threads(1)`**: The application runs on Raspberry Pi. Limiting the event loop scheduler to one worker thread minimizes CPU context-switching overhead and prevents resource exhaustion.&lt;br /&gt;
2. **`max_blocking_threads(4)`**: Restricting the blocking thread pool prevents the runtime from spawning too many concurrent threads when database pings, file operations, or synchronous calculations are invoked (such as via `tokio::task::spawn_blocking` or `block_in_place`).&lt;br /&gt;
&lt;br /&gt;
=== Developer Diagnostics with Tokio Console ===&lt;br /&gt;
&lt;br /&gt;
For runtime inspection, task profiling, and detecting deadlocks or starvation, you can run the application with **Tokio Console** integration.&lt;br /&gt;
&lt;br /&gt;
==== Prerequisites &amp;amp; Compilation ====&lt;br /&gt;
Tokio Console requires unstable instrumentation.&lt;br /&gt;
1. Use the `debug_tokio` feature flag, which initializes the `console-subscriber` in `run.rs`:&lt;br /&gt;
   ```bash&lt;br /&gt;
   RUSTFLAGS=&amp;quot;--cfg tokio_unstable&amp;quot; cargo build --features &amp;quot;debug_tokio&amp;quot;&lt;br /&gt;
   ```&lt;br /&gt;
2. The `tokio_unstable` compiler flag ensures that Tokio exposes internal tracing endpoints (like the task builder names).&lt;br /&gt;
&lt;br /&gt;
==== Installing the Terminal Console ====&lt;br /&gt;
Install the client CLI on your development machine (macOS/Linux) via Cargo:&lt;br /&gt;
```bash&lt;br /&gt;
cargo install --locked tokio-console&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
==== Running and Reading ====&lt;br /&gt;
1. Launch the compiled application:&lt;br /&gt;
2. Open a separate terminal window and run:&lt;br /&gt;
   ```bash&lt;br /&gt;
   tokio-console&lt;br /&gt;
   ```&lt;br /&gt;
This presents a live dashboard of running tasks, showing busy poll times, total lifetime, and idle periods.&lt;br /&gt;
&lt;br /&gt;
=== Interpreting Task Size Warnings ===&lt;br /&gt;
When checking with `tokio-console`, you see warnings that tasks like `signal_handler`, `relay_manager`, `data_logger`, or `heating` are **1024 bytes or larger**.&lt;br /&gt;
* **These warnings can be safely ignored.**&lt;br /&gt;
* The size refers to the stack footprint of the compiler-generated `Future` state machine. In debug builds, these are larger due to lack of optimization.&lt;br /&gt;
* Because these tasks are long-lived workers spawned once at startup and run until shutdown, they are allocated on the heap only once. A 1–2 KiB heap footprint has zero impact on application performance, fragmentation, or memory leaks.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=518</id>
		<title>Use of an asynchronous runtime for the main application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=518"/>
		<updated>2026-07-11T11:36:04Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Tokio Runtime Configuration */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
The Aquarium Control application uses a hybrid execution model to balance responsiveness, low latency, and hardware constraints. It uses the **Tokio asynchronous runtime** for coordinating sensor logging, network communication, safety interlocks, and scheduling. Concurrently, it spawns dedicated **native operating system threads** for operations requiring strict timing or blocking I/O (such as direct GPIO sensor reading).&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Task and Thread Inventory ==&lt;br /&gt;
&lt;br /&gt;
The table below lists all tasks and threads running in the application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Description&lt;br /&gt;
! Type (OS Thread / Async Task)&lt;br /&gt;
! Usage of &amp;lt;code&amp;gt;block_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Main Thread'''&lt;br /&gt;
| Bootstraps the application, reads configuration, establishes database pools, builds the Tokio runtime, and blocks on the execution of all tasks.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| Yes (&amp;lt;code&amp;gt;rt.block_on&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;TcpCommunication&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Mock simulator thread that handles TCP connections and commands from the GUI simulator. Runs only when &amp;lt;code&amp;gt;use_simulator&amp;lt;/code&amp;gt; is active.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;Dht&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Direct driver reader thread for the DHT22 ambient temperature and humidity sensor. Handled as an OS thread to prevent blocking the async runtime.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;i2c_interface&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages serial I2C bus communications for sensor expansion units.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Captures shutdown signals (SIGINT, SIGTERM) and coordinates the graceful, phase-based shutdown of all sub-components.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;schedule_check&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs regular database checks against the active rules database to ensure operations are authorized based on the current time.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;sensor_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Coordinates, aggregates, and caches values read from individual sensor tasks (DS18B20, DHT22, Atlas Scientific).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;atlas_scientific&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages data acquisition and calibration for the Atlas Scientific EZO pH and conductivity probes.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Acts as the central actuator gateway, processing on/off/pulse commands and sending them directly to the hardware relays.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically logs sensor values and thermal controller statuses into the MySQL database.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;temperature_gradient&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the rate of temperature changes to detect heater or ventilation inefficiencies/failures.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Orchestrates the heating loop, using hysteresis or safety controls to actuate the aquarium heater.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ventilation&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Controls the surface cooling fan loop, maintaining temperature thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;monitors&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Runs regular diagnostic checks and outputs warning/critical logs when states drift outside target thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Drives the fresh water top-off system, managing level switch states and pump runs with safety timers.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;feed&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the feeding schedule and actuates the automatic feeder.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;balling&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Oversees peristaltic mineral pumps to dose minerals into the aquarium according to schedule.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;tank_level_switch&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically reads the state of physical water level floats or simulator ports.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;watchdog&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs periodic heartbeats to ensure system safety loops are executing.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ds18b20&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically queries 1-Wire DS18B20 digital thermometers for water temperature.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;messaging&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Stub mock receiver that routes command queues when running on non-Linux platforms (like macOS).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Tokio Runtime Configuration ==&lt;br /&gt;
&lt;br /&gt;
The Tokio runtime is explicitly built at startup with a constrained configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;rust&amp;quot; line&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread()&lt;br /&gt;
    .worker_threads(1)&lt;br /&gt;
    .max_blocking_threads(4)&lt;br /&gt;
    .enable_all()&lt;br /&gt;
    .build()&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread().worker_threads(1).max_blocking_threads(4).enable_all().build()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== Why this configuration is chosen ===&lt;br /&gt;
1. **`worker_threads(1)`**: The application runs on Raspberry Pi. Limiting the event loop scheduler to one worker thread minimizes CPU context-switching overhead and prevents resource exhaustion.&lt;br /&gt;
2. **`max_blocking_threads(4)`**: Restricting the blocking thread pool prevents the runtime from spawning too many concurrent threads when database pings, file operations, or synchronous calculations are invoked (such as via `tokio::task::spawn_blocking` or `block_in_place`).&lt;br /&gt;
&lt;br /&gt;
=== Developer Diagnostics with Tokio Console ===&lt;br /&gt;
&lt;br /&gt;
For runtime inspection, task profiling, and detecting deadlocks or starvation, you can run the application with **Tokio Console** integration.&lt;br /&gt;
&lt;br /&gt;
==== Prerequisites &amp;amp; Compilation ====&lt;br /&gt;
Tokio Console requires unstable instrumentation.&lt;br /&gt;
1. Use the `debug_tokio` feature flag, which initializes the `console-subscriber` in `run.rs`:&lt;br /&gt;
   ```bash&lt;br /&gt;
   RUSTFLAGS=&amp;quot;--cfg tokio_unstable&amp;quot; cargo build --features &amp;quot;debug_tokio&amp;quot;&lt;br /&gt;
   ```&lt;br /&gt;
2. The `tokio_unstable` compiler flag ensures that Tokio exposes internal tracing endpoints (like the task builder names).&lt;br /&gt;
&lt;br /&gt;
==== Installing the Terminal Console ====&lt;br /&gt;
Install the client CLI on your development machine (macOS/Linux) via Cargo:&lt;br /&gt;
```bash&lt;br /&gt;
cargo install --locked tokio-console&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
==== Running and Reading ====&lt;br /&gt;
1. Launch the compiled application:&lt;br /&gt;
2. Open a separate terminal window and run:&lt;br /&gt;
   ```bash&lt;br /&gt;
   tokio-console&lt;br /&gt;
   ```&lt;br /&gt;
This presents a live dashboard of running tasks, showing busy poll times, total lifetime, and idle periods.&lt;br /&gt;
&lt;br /&gt;
=== Interpreting Task Size Warnings ===&lt;br /&gt;
When checking with `tokio-console`, you see warnings that tasks like `signal_handler`, `relay_manager`, `data_logger`, or `heating` are **1024 bytes or larger**.&lt;br /&gt;
* **These warnings can be safely ignored.**&lt;br /&gt;
* The size refers to the stack footprint of the compiler-generated `Future` state machine. In debug builds, these are larger due to lack of optimization.&lt;br /&gt;
* Because these tasks are long-lived workers spawned once at startup and run until shutdown, they are allocated on the heap only once. A 1–2 KiB heap footprint has zero impact on application performance, fragmentation, or memory leaks.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
	<entry>
		<id>http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=517</id>
		<title>Use of an asynchronous runtime for the main application</title>
		<link rel="alternate" type="text/html" href="http://217.79.180.177/mediawiki/index.php?title=Use_of_an_asynchronous_runtime_for_the_main_application&amp;diff=517"/>
		<updated>2026-07-11T11:35:48Z</updated>

		<summary type="html">&lt;p&gt;Uwe: /* Tokio Runtime Configuration */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
The Aquarium Control application uses a hybrid execution model to balance responsiveness, low latency, and hardware constraints. It uses the **Tokio asynchronous runtime** for coordinating sensor logging, network communication, safety interlocks, and scheduling. Concurrently, it spawns dedicated **native operating system threads** for operations requiring strict timing or blocking I/O (such as direct GPIO sensor reading).&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Task and Thread Inventory ==&lt;br /&gt;
&lt;br /&gt;
The table below lists all tasks and threads running in the application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Description&lt;br /&gt;
! Type (OS Thread / Async Task)&lt;br /&gt;
! Usage of &amp;lt;code&amp;gt;block_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Main Thread'''&lt;br /&gt;
| Bootstraps the application, reads configuration, establishes database pools, builds the Tokio runtime, and blocks on the execution of all tasks.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| Yes (&amp;lt;code&amp;gt;rt.block_on&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;TcpCommunication&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Mock simulator thread that handles TCP connections and commands from the GUI simulator. Runs only when &amp;lt;code&amp;gt;use_simulator&amp;lt;/code&amp;gt; is active.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;Dht&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Direct driver reader thread for the DHT22 ambient temperature and humidity sensor. Handled as an OS thread to prevent blocking the async runtime.&lt;br /&gt;
| OS Thread&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;i2c_interface&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages serial I2C bus communications for sensor expansion units.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;signal_handler&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Captures shutdown signals (SIGINT, SIGTERM) and coordinates the graceful, phase-based shutdown of all sub-components.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;schedule_check&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs regular database checks against the active rules database to ensure operations are authorized based on the current time.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;sensor_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Coordinates, aggregates, and caches values read from individual sensor tasks (DS18B20, DHT22, Atlas Scientific).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;atlas_scientific&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Manages data acquisition and calibration for the Atlas Scientific EZO pH and conductivity probes.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;relay_manager&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Acts as the central actuator gateway, processing on/off/pulse commands and sending them directly to the hardware relays.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;data_logger&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically logs sensor values and thermal controller statuses into the MySQL database.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;temperature_gradient&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the rate of temperature changes to detect heater or ventilation inefficiencies/failures.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;heating&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Orchestrates the heating loop, using hysteresis or safety controls to actuate the aquarium heater.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ventilation&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Controls the surface cooling fan loop, maintaining temperature thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;monitors&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Runs regular diagnostic checks and outputs warning/critical logs when states drift outside target thresholds.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;refill&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Drives the fresh water top-off system, managing level switch states and pump runs with safety timers.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;feed&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Monitors the feeding schedule and actuates the automatic feeder.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;balling&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Oversees peristaltic mineral pumps to dose minerals into the aquarium according to schedule.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;tank_level_switch&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically reads the state of physical water level floats or simulator ports.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;watchdog&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Performs periodic heartbeats to ensure system safety loops are executing.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;ds18b20&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Periodically queries 1-Wire DS18B20 digital thermometers for water temperature.&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| '''&amp;lt;code&amp;gt;messaging&amp;lt;/code&amp;gt;'''&lt;br /&gt;
| Stub mock receiver that routes command queues when running on non-Linux platforms (like macOS).&lt;br /&gt;
| Async Task&lt;br /&gt;
| No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Tokio Runtime Configuration ==&lt;br /&gt;
&lt;br /&gt;
The Tokio runtime is explicitly built at startup with a constrained configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;rust&amp;quot; line&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread()&lt;br /&gt;
    .worker_threads(1)&lt;br /&gt;
    .max_blocking_threads(4)&lt;br /&gt;
    .enable_all()&lt;br /&gt;
    .build()&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;rust&amp;quot; line&amp;gt;&lt;br /&gt;
let rt = tokio::runtime::Builder::new_multi_thread().worker_threads(1).max_blocking_threads(4).enable_all().build()&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
=== Why this configuration is chosen ===&lt;br /&gt;
1. **`worker_threads(1)`**: The application runs on Raspberry Pi. Limiting the event loop scheduler to one worker thread minimizes CPU context-switching overhead and prevents resource exhaustion.&lt;br /&gt;
2. **`max_blocking_threads(4)`**: Restricting the blocking thread pool prevents the runtime from spawning too many concurrent threads when database pings, file operations, or synchronous calculations are invoked (such as via `tokio::task::spawn_blocking` or `block_in_place`).&lt;br /&gt;
&lt;br /&gt;
=== Developer Diagnostics with Tokio Console ===&lt;br /&gt;
&lt;br /&gt;
For runtime inspection, task profiling, and detecting deadlocks or starvation, you can run the application with **Tokio Console** integration.&lt;br /&gt;
&lt;br /&gt;
==== Prerequisites &amp;amp; Compilation ====&lt;br /&gt;
Tokio Console requires unstable instrumentation.&lt;br /&gt;
1. Use the `debug_tokio` feature flag, which initializes the `console-subscriber` in `run.rs`:&lt;br /&gt;
   ```bash&lt;br /&gt;
   RUSTFLAGS=&amp;quot;--cfg tokio_unstable&amp;quot; cargo build --features &amp;quot;debug_tokio&amp;quot;&lt;br /&gt;
   ```&lt;br /&gt;
2. The `tokio_unstable` compiler flag ensures that Tokio exposes internal tracing endpoints (like the task builder names).&lt;br /&gt;
&lt;br /&gt;
==== Installing the Terminal Console ====&lt;br /&gt;
Install the client CLI on your development machine (macOS/Linux) via Cargo:&lt;br /&gt;
```bash&lt;br /&gt;
cargo install --locked tokio-console&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
==== Running and Reading ====&lt;br /&gt;
1. Launch the compiled application:&lt;br /&gt;
2. Open a separate terminal window and run:&lt;br /&gt;
   ```bash&lt;br /&gt;
   tokio-console&lt;br /&gt;
   ```&lt;br /&gt;
This presents a live dashboard of running tasks, showing busy poll times, total lifetime, and idle periods.&lt;br /&gt;
&lt;br /&gt;
=== Interpreting Task Size Warnings ===&lt;br /&gt;
When checking with `tokio-console`, you see warnings that tasks like `signal_handler`, `relay_manager`, `data_logger`, or `heating` are **1024 bytes or larger**.&lt;br /&gt;
* **These warnings can be safely ignored.**&lt;br /&gt;
* The size refers to the stack footprint of the compiler-generated `Future` state machine. In debug builds, these are larger due to lack of optimization.&lt;br /&gt;
* Because these tasks are long-lived workers spawned once at startup and run until shutdown, they are allocated on the heap only once. A 1–2 KiB heap footprint has zero impact on application performance, fragmentation, or memory leaks.&lt;/div&gt;</summary>
		<author><name>Uwe</name></author>
	</entry>
</feed>