Files
MSE-PI-E2EEDA-Plein-de-eeee…/report/main/06-validation.typ

209 lines
16 KiB
Typst

#import "/metadata.typ": *
#pagebreak()
= #i18n("validation-title", lang:option.lang) <sec:validation>
#add-chapter()[
// == Complete application
The application as a whole allows to retrieve the environmental data in the A2 and A3 room at the Provence campus.
These data are then formatted by the gateway and published on @mqtt topics.
Said topics are listened by the database, ultimately storing them and making them available through an @api.
This @api enable the live display on the user interface alongside letting the physical model validate the data and forecast when the room should be aired.
The major difference with the intended design lies in the notifications being delivered in Telegram instead of Teams.
This choice has been forced due to the school's network restrictions.
Since the goal for this project is to demonstrate the feature, the effort were redirected.
== Nodes
The implicit goal for the nodes was to have devices retrieving the environmental values in a room.
Furthermore, this devices were expected to work during several weeks without the need to manipulate them.
The first objective is fulfilled, as the temperature, humidity and @co2 level are periodically advertised over @ble.\
However the nodes as-is can hardly be considered as "low-power" since they should run for many month without recharge.
The definitive reason could not be found, even after talk with nordic tech support.
Said guys pointed out that the problem may lie within the thingy52 hardware itself as it was not designed to be actually low-power.
Hence, the firmware itself has been properly implemented to be low-power, but requires the design of a dedicated hardware.
This topic shall be part of the project's next iteration.
Regrettably, the effort available for the project did not allow to implement automated tests.
As such, the only test conducted were quick ones prior to the various integration.
Considering that the nodes are stable enough, automated test shall be amongst the first tasks for the
next project's iteration.
== Gateway
DeepL Pro was used to translate this section from French to English, followed by @llm assistance to improve the academic style.
The gateway successfully fulfills its role environmental data from
the Thingy:52 nodes is received, decoded and forwarded to the database
in real conditions at school with multiple nodes running simultaneously.
The systemd service proved reliable in production. Two unexpected
disconnections due to network instability were detected and recovered
automatically overnight without manual intervention, confirming that
the crash-and-restart mechanism works as intended.
Several limitations were identified but not addressed within the
available time. Duplicate packets a consequence of @ble broadcasting
on three channels are now filtered by the 10-second deduplication
window. Invalid @co2 values during sensor warm-up are silently discarded
without error reporting to the database; a proper error protocol would
require agreement across all components and is left for the next
iteration.
== Database & @api:short
#include "database/validation.typ"
== User interface
=== Positive Outcomes
*Separation of concerns is well maintained.* Components hold no business logic; all data fetching and transformation is isolated in services. Mock fallbacks have been deliberately removed from `SensorService` so the UI reflects real @api state rather than hiding errors with synthetic data.
*Responsive design covers all target breakpoints.* The three-breakpoint strategy (1100 px / 768 px / 480 px) produces a usable layout on desktop, tablet, and mobile without separate component trees.
*Polling teardown is correct.* The use of `takeUntilDestroyed` and explicit `ngOnDestroy` cleanup eliminates the two most common Angular memory-leak patterns (forgotten subscriptions, orphaned timers).
*The proxy-based CORS workaround is transparent.* No component or service is aware of whether it is running against a local or remote backend.
*Credentials are never stored in source.* @api credentials are injected at build time by the CI/CD pipeline via GitHub Secrets and replaced by empty strings in the dev environment. A `git filter-repo` pass was applied to remove a previously committed plaintext password from the full repository history.
*A full CI/CD pipeline is in place.* Every pull request runs TypeScript compilation, linting, Prettier formatting, both dev and production builds, and headless unit tests before merge. Pushes to `main` additionally build and push a Docker image to GHCR and deploy it to the physical server over SSH, with no manual intervention required.
=== Negative Outcomes and Gaps
*No deduplication of polling on tab switch.* When the browser tab is hidden, polling continues at the same interval. Using the Page Visibility @api to pause polling when the tab is inactive would reduce unnecessary network traffic.
*`bypassSecurityTrustHtml` introduces an XSS surface.* The SVG is fetched from the same origin (`/public/plan.svg`) which mitigates the risk, but the Angular security model is explicitly bypassed. A Content Security Policy header on the server would reduce residual risk.
*Room IDs are hardcoded in `rooms-layout.config.ts`.* The mapping between physical room identifiers (as stored in InfluxDB) and layout coordinates is static. Any room addition on the hardware side requires a manual code change and redeployment of the UI.
*@co2 thresholds are partially duplicated.* The same threshold values appear in `co2-levels.config.ts` (UI), `notification_service/application.yml` (Telegram notifier), and the Go service configuration. The UI spec is now config-driven, reducing the risk of intra-UI divergence, but cross-service consistency still relies on manual coordination. A single source of truth, either a shared config endpoint or a well-documented constant, would eliminate the remaining risk.
== Physical model
The results of the physical models are given below.
@llm has been only used for some cases to help debugging the models.
=== Results of no window-opening model
As described in the Design part, the first model reads the data from open data files and plot the time at which the threshold of 1400 @ppm is reached for a given air volume per person as illustrated @fig:time_reaching_threshold_v1.
#figure(
image("../resources/img/Time_reaching_threshold.png", width: 90%),
caption: [Time reaching threshold]
) <fig:time_reaching_threshold_v1>
#pagebreak()
As can be seen in the graph, the open data from #cite(<twardella_effect_2012-1>), plotted as dark blue crosses, do not allow a clear trend, since the air volume per person is a mean value and therefore identical for all classrooms.
On the other hand, the data from the simulator of air quality #cite(<Simaria>) are highly useful, as different cases can be tested and a general trend emerges. This leads to an affine function enabling the determination of the time reaching the threshold (1400 @ppm) as a function of room volume and number of occupants.
$
t_"threshold" = frac((frac(V,N) - 0.101), 0.305)
$
where V, represents the room volume and N, the number of occupants.
In the data from #cite(<INRS_Study>), only one scenario reaches the 1400 @ppm threshold (represented by an orange point on the graph). When comparing with the affine function, a relative deviation of approximately 30% can be observed. As mentioned in the Design part, this model considers an initial @co2 concentration ranging from 400 to 600 @ppm and is thus valid only following air renewal.
A second physical model has been developed to take into account the initial @co2 concentration and give the time evolution of @co2 level. The formula mentioned in the Design part is applied to model the @co2 concentration over time. The model is then compared with the open data and adjusted to the data as illustrated @fig:CO2_concentration_simaria, @fig:CO2_concentration_INRS_secondary_school_classroom and @fig:CO2_concentration_INRS_office_room.
#figure(
image("../resources/img/Physical model/CO2 concentration over time Simaria.png", width: 100%),
caption: [Comparison of @co2:short concentration over time between open data from Simaria #cite(<Simaria>) (continuous) and the model (dashed)]
) <fig:CO2_concentration_simaria>
#figure(
image("../resources/img/Physical model/CO2 concentration over time INRS secondary school.png", width: 100%),
caption: [Comparison of @co2:short concentration over time between open data from the secondary school classroom #cite(<INRS_Study>) (blue) and the model (orange dashed)]
) <fig:CO2_concentration_INRS_secondary_school_classroom>
#figure(
image("../resources/img/Physical model/CO2 concentration over time INRS office room.png", width: 100%),
caption: [Comparison of @co2:short concentration over time between open data from the office room #cite(<INRS_Study>) (blue) and the model (orange dashed)]
) <fig:CO2_concentration_INRS_office_room>
#pagebreak()
It is therefore possible for the user to input a room volume, a number of occupants, and an initial @co2 concentration in order to obtain the evolution of @co2 concentration over time and the time at which the 1400 @ppm threshold is reached as illustrated @fig:CO2_concentration_user_parameters and @fig:CO2_concentration_user.
#figure(
image("../resources/img/Physical model/Window user no window opening model.png", width: 80%),
caption: [User input parameters and result using no window-opening model]
) <fig:CO2_concentration_user_parameters>
#figure(
image("../resources/img/Physical model/CO2 concentration over time user.png", width: 100%),
caption: [@co2:short concentration over time with user input parameters (V = 308 $m^3$, N = 40, initial @co2:short concentration = 500 @ppm)]
) <fig:CO2_concentration_user>
#pagebreak()
=== Results of window-opening model
The window-opening model calculates the @co2:short concentration at each time step after the 1400 @ppm threshold is reached for a considered room. It is assumed that all windows of the considered classroom are fully opened. This also provides an estimation of the required window-opening duration to renew indoor air. @fig:CO2_concentration_window_opening_model_all_rooms represents an overview of the evolution of @co2:short concentration over time for 5 classrooms of Space A. It can be noticed that the window-opening duration varies significantly from one classroom to another, depending on the ratio between the room volume and the total windows surface. Indeed, the window-opening time for indoor air renewal is approximately 3 minutes in classroom A3 and A5, whereas it can reach 18 minutes for classrooms A2 and A6.
In contrast to the increasing evolution of @co2:short concentration, the decrease in @co2:short level after window opening is difficult to compare with the open data, as they do not specify the total windows surface. Therefore, only experimental sensor data from the Provence classrooms can be used to adjust the model. It was decided to prioritise instrumentation in Room A2 due to its slower evolution of @co2:short concentration decrease, which provides more data points.
#figure(
image("../resources/img/Physical model/CO2 concentration window opening A2 to A6.png", width: 90%),
caption: [Overview of @co2:short concentration over time for Provence classrooms, starting from 1400 @ppm]
) <fig:CO2_concentration_window_opening_model_all_rooms>
#pagebreak()
The user's input parameters are given @fig:CO2_concentration_window_opening_user_parameters. He no longer needs to enter the room volume, but only the room name, the number of students and the initial @co2:short concentration. The physical model then outputs the time to reach the threshold and the corresponding window-opening time. It can also display the corresponding @co2:short concentration over time graph as illustrated @fig:CO2_concentration_window_opening_model_user.
#figure(
image("../resources/img/Physical model/Window user window opening model.png", width: 80%),
caption: [User input parameters and result using window-opening model]
) <fig:CO2_concentration_window_opening_user_parameters>
#figure(
image("../resources/img/Physical model/CO2 concentration over time user window opening model.png", width: 100%),
caption: [@co2:short concentration over time with user input parameters (classroom number = A2, N = 30, initial @co2:short concentration = 700 @ppm)]
) <fig:CO2_concentration_window_opening_model_user>
#pagebreak()
=== Comparison of experimental data and the physical model
@fig:CO2_concentration_comparison_exp_model presents a comparison between the measured data and the model for classroom A2 during a lecture. Six sensors have been installed. Two are located near the windows to monitor their status (blue and green curves on the graph), and four on walls away from the windows (purple, red, orange, brown curves). Data are missing from 45 to 90 minutes. However the measurements continue afterwards which makes it possible to analyse the data.
The window open/ closed status is indicated by the blue vertical dotted lines. It is therefore assumed that the windows are closed before reaching the status open (= 1) after approximately 90 minutes. A student present in the room confirmed that the windows not equipped with the sensors were closed when the instrumented ones were closed, ensuring consistency with the model conditions. Eleven people were present in the room.
A trend curve is determined for each experimental data using a python program and is identified by dashed lines on the graph.
The black line represents the model result which takes into account the classroom name, the number of students and the initial @co2:short level.
#figure(
image("../resources/img/Physical model/Comparison_expdata_model.png", width: 90%),
caption: [@co2:short concentration over time of acquired data from classroom A2 and comparison with the model]
) <fig:CO2_concentration_comparison_exp_model>
It is observed that all data except the purple one do not show significant increase in @co2:short over time. These data are therefore not used to adjust the model. At first sight, the data from the purple curve seem to be correct as an increase up to 800 @ppm can be observed. However, when this data is compared with the model the value is found to be approximately two times lower than expected.
Only this set of data was available and they are not sufficient to adjust the model. Moreover, when comparing the model with open data, the gap is not as significant.
It appears that the sensors output data are most likely not accurate. It would be relevant to compare the sensor data with a calibrated reference sensor.
// #pagebreak()
=== Recommendations for ensuring good indoor air quality
The time reaching threshold of 1400 @ppm and windows-opening duration are given in @tab:Provence_recommandations for each classroom, assuming maximum number of students. These data are conservative, as the maximum number of students is rarely reached.
It is therefore recommended to :
- Open windows after one-hour lecture
- Fully open windows when airing the classroom in order to optimise ventilation and reduce the required duration. The duration highly depends on the classroom. An average of 5 minutes can be considered. It is also the recommended duration mentioned in the literature.
#figure(
table(
columns: (auto, auto, auto, auto),
align: center,
table.header("Classroom [-]", "Maximal student capacity [-]", "time reaching 1400 ppm threshold [min]","windows-opening duration [min]"),
[A2],[40],[30],[17],
[A3],[78],[24],[4],
[A4],[48],[27],[18],
[A5],[32],[34],[2],
[A6],[58],[22],[18],
[A7],[36],[30],[9],
),
caption: [Recommendations for ensuring good indoor air quality in Provence - Space A, assuming full ventilation prior to the start of the lecture (i.e. approximately 400 @ppm:short)],
)<tab:Provence_recommandations>
== Conclusion
The @mvp for the project stores the environmental value in a database and delivers a notification when the room needs to be aired.
This @mvp also planned forecasting, but has been cut from the planning following the team reduction.
Some effort should still be done to provide the complete set of expected features, mostly to have "low-power" nodes and notifications
in Teams.
However, the project as delivered at it's end can serve as a good basis for a second iteration, which could add the aforementioned features.
]