docs(reports): add ui + db

Signed-off-by: Klagarge <remi@heredero.ch>
This commit is contained in:
2026-06-10 13:33:45 +02:00
parent 09ee22ff50
commit 5902a47605
32 changed files with 239 additions and 28 deletions

View File

@@ -1,11 +1,11 @@
#import "../../resources/helper.typ": *
#let b = actor("broker",
disp_name: [@mqtt\ broker],
disp_name: [@mqtt:short\ broker],
shape: "queue",
show-bottom:false
)
#let mg = actor("mqtt",
disp_name: [@mqtt\ gateway],
disp_name: [@mqtt:short\ gateway],
show-bottom:false
)
#let ig = actor("influx",

View File

@@ -5,6 +5,64 @@
#import "/resources/slides.typ": *
#import "/main/database/sequence.typ": *
// server
// mqtt->db
// db->rest
// db->rest
#let toDB = {
import chronos: *
b.display
mg.display
main.display
ig.display
db.display
_col(b.name, mg.name, width: 5cm)
_col(mg.name, main.name, width: 5cm)
_col(main.name, ig.name, width: 5cm)
_col(ig.name, db.name, width: 5cm)
sync(main, mg, "New", create-dst: true)
sync(mg, b, "subscribe")
sync(main, ig, "New", create-dst: true)
async(b, mg, "message")
sync(mg, main, "DataPoint")
sync(main, main, "map topics")
sync(main, ig, "DataPoint")
async(ig, db, "flush")
}
#let fromDB = {
import chronos: *
db.display
ig.display
main.display
rg.display
u.display
_col(db.name, ig.name, width: 5cm)
_col(ig.name, main.name, width: 5cm)
_col(main.name, rg.name, width: 5cm)
_col(rg.name, u.name, width: 5cm)
async(u, rg, "Request")
sync(rg, main, "getNodes")
sync(main, rg, "", dashed: true)
sync(rg, ig, "Query")
sync(ig, db, "")
sync(db, ig, "", dashed: true)
sync(ig, rg, "", dashed: true)
sync(rg, u, "", dashed: true)
}
---
#figure(
chronos.diagram(toDB, width: 90%),
caption: [Sequence from broker to DB]
) <fig:seq:toDB>
---
#figure(
chronos.diagram(fromDB, width: 90%),
caption: [Sequence to REST from DB]
) <fig:seq:fromDB>

View File

@@ -14,13 +14,13 @@
[
*Architecture*
- Raspberry Pi 4 Python
- Passive BLE scan (bleak)
- Passive @ble:short scan (bleak)
- MQTTS publisher (paho-mqtt)
- systemd service auto-restart
- Remote access via Tailscale
],
[
*Data flow*
*Data flow*\
Thingy:52
-> BLE advertising
Raspberry Pi
@@ -62,6 +62,9 @@
#slide[
#align(center)[
#image("/resources/img/sequence_data_collection.svg", height: 85%)
#figure(
image("/resources/img/gateway_overview.svg", height: 40%),
caption: [Gateway communication chain from @ble:short advertising to database storage]
)
]
]

View File

@@ -2,20 +2,18 @@
#import "/tail/bibliography.typ": *
#import "/tail/glossary.typ": *
#import "/main/architecture/description.typ": *
#import "/resources/slides.typ": *
---
Input:
- Provence classrooms specifications - Space A #pause
- Open data #pause
- Ventilation standard formula #pause
- User parameters : Room name, number of students, initial @co2 concentration #pause
- User parameters : Room name, number of students, initial @co2:short concentration #pause
Output:
- Evolution of @co2 concentration without considering air ventilation #pause
- Evolution of @co2:short concentration without considering air ventilation #pause
- Time required to reach a threshold of 1400 ppm #pause
- Evolution of @co2 concentration decrease under natural ventilation #pause
- Evolution of @co2:short concentration decrease under natural ventilation #pause
- Time required to reach outdoor-equivalent concentration level
---

View File

@@ -21,7 +21,7 @@
) <fig:nodes_sequence_diagram>
]
#nodes_sequence_diagram
== Nodes | Nodes_interface
== Nodes | BLE data
#figure(
table(
columns: (auto, auto, auto),
@@ -30,14 +30,14 @@
[Window opening status],[0x01],[1B],
[Humidity],[0x02],[1B],
[Temperature],[0x03],[2B],
[CO2 level],[0x04],[4B],
[@co2:short level],[0x04],[4B],
[Battery percent of charge],[0x05],[1B],
),
caption: [Data communicated in the nodes_interface],
)<tab:nodes_interface_content>
---
=== Nodes | Takeaways
== Nodes | Takeaways
- Breadboard validation #pause
- 28 days later #pause
- Improve nodes_interface reliability
- Improve @ble:short reliability

View File

@@ -62,17 +62,20 @@ TECHNIQUE
= Intro // (50s) Rémi
// Context of the project
// Dimitri missing
---
@co2
#speaker-note[
This is a personal note
here also @co2
]
---
== Architecture // (50s) Ibrahima
TODO
#let top_level_architecture = [
#figure(
image("../../resources/img/ui_images/architecture.png"),
caption: [Top level architecture]
) <fig:top_level_architecture>
]
#top_level_architecture
== Organisation & Task Management // (50s) Djelal
@@ -118,7 +121,7 @@ TODO
= Conclusion
== Whole project's takeaways // (50s) Adrien (Regard critique)
- @trl 4 #pause
- @trl:short 4 #pause
- Forecasting and Teams notifications
== Future perspectives // (50s) Alison
@@ -141,8 +144,7 @@ TODO
// show all term even if they are not referenced, default to true
show-all: false,
// disable the back ref at the end of the descriptions
disable-back-references: false,
deduplicate-back-references: true
disable-back-references: true
)
@@ -152,9 +154,6 @@ TODO
= Annexes
== Backup slide example
This slide is an example for create a backup slide.
== Description of the model
#grid(

View File

@@ -3,4 +3,114 @@
#import "/tail/glossary.typ": *
#import "/main/architecture/description.typ": *
#import "/resources/slides.typ": *
#import "/resources/slides.typ": *
#let c-dark = rgb("#0F172A")
#let c-teal = rgb("#0EA5E9")
#let c-text = rgb("#1E293B")
#let c-muted = rgb("#64748B")
#let c-white = rgb("#FFFFFF")
#let c-border = rgb("#E2E8F0")
---
─────────────────────────────────────────────────────────────────────────────
// SLIDE 2 — Cycle DevSecOps
// ─────────────────────────────────────────────────────────────────────────────
#let slide-devsecops() = {
// Header custom (subtitle à droite)
block(width: 100%, height: 43.9pt, fill: c-dark, inset: 0pt)[
#pad(x: 25.5pt)[
#align(horizon)[
#grid(columns: (1fr, auto),
align(left + horizon)[
#text(size: 16pt, weight: "bold", fill: rgb("#FFFFFF"))[Cycle DevSecOps - PI E2EEDA]
],
align(right + horizon)[
#text(size: 8pt, fill: rgb("#94A3B8"))[Shift-left · Security by design]
],
)
]
]
]
pad(x: 17pt, top: 11pt, bottom: 9pt)[
#grid(columns: (1fr, 1fr, 1fr, 1fr), gutter: 10pt,
// ① Code & PR Gate
// image10 = TypeScript (TS logo), image11 = Angular (grisé)
// image19 = GitHub Actions (GHCR/GitHub logo)
devsec-col("① Code & PR Gate", (
(icon: "../../resources/img/ui_images/images/image10.png", bg: rgb("#3178C6"), name: "TypeScript",
extra: [#text(size: 7pt, fill: c-muted)[tsc / ESLint]]),
(icon: "../../resources/img/ui_images/images/image11.png", bg: rgb("#DD0031"), name: "Angular CI",
extra: [#text(size: 7pt, fill: c-muted)[Build check]]),
)),
// ② SAST · SCA
// image13 = GitHub octocat (SpotBugs hébergé GitHub), image13 aussi CodeQL (GitHub)
// image14 = checklist hexagone (Dep. Check / OWASP DC)
devsec-col("② SAST · SCA", (
(icon: "../../resources/img/ui_images/images/image13.png", bg: rgb("#1F2328"), name: "SpotBugs",
extra: [#badge("BLOCKING")]),
(icon: "../../resources/img/ui_images/images/image13.png", bg: rgb("#1F2328"), name: "CodeQL",
extra: [#badge("BLOCKING")]),
(icon: "../../resources/img/ui_images/images/image14.png", bg: rgb("#F97316"), name: "Dep. Check",
extra: [#badge("NON-BLOCK", fill: rgb("#F97316"))]),
)),
// ③ DAST · Tests
// image15 = libellule OWASP ZAP, image16 = Karma/Jasmine, image17 = Tux Linux
devsec-col("③ DAST · Tests", (
(icon: "../../resources/img/ui_images/images/image15.png", bg: rgb("#00549E"), name: "OWASP ZAP",
extra: [#badge("BLOCKING")]),
(icon: "../../resources/img/ui_images/images/image16.png", bg: rgb("#DD0031"), name: "Karma Tests",
extra: [#badge("BLOCKING")]),
(icon: "../../resources/img/ui_images/images/image17.png", bg: rgb("#64748B"), name: "Runtime check",
extra: [#text(size: 7pt, fill: c-muted)[HTTP headers]]),
)),
// ④ Build · Deploy
// image18 = Docker, image19 = GHCR, image17 = Tux Linux (SSH)
devsec-col("④ Build · Deploy", (
(icon: "../../resources/img/ui_images/images/image18.png", bg: rgb("#0DB7ED"), name: "Docker",
extra: [#text(size: 7pt, fill: c-muted)[SHA-tagged]]),
(icon: "../../resources/img/ui_images/images/image19.png", bg: rgb("#1F2328"), name: "GHCR Push",
extra: [#text(size: 7pt, fill: c-muted)[main only]]),
(icon: "../../resources/img/ui_images/images/image17.png", bg: rgb("#10B981"), name: "SSH Deploy",
extra: [#text(size: 7pt, fill: c-muted)[cert-auth]]),
)),
)
#v(9pt)
// Barre résumé
#rect(width: 100%, height: 30pt, fill: c-dark, radius: 5pt, inset: 0pt)[
#pad(x: 30pt)[
#align(horizon)[
#grid(columns: (1fr, 1fr, 1fr),
align(center + horizon)[#text(size: 9pt, fill: rgb("#FFFFFF"))[🔐 #h(3pt) Shift-left security]],
align(center + horizon)[#text(size: 9pt, fill: rgb("#FFFFFF"))[🔑 #h(3pt) Zero secret in code]],
align(center + horizon)[#text(size: 9pt, fill: rgb("#FFFFFF"))[🔄 #h(3pt) Automated Deployment]],
)
]
]
]
#v(8pt)
// GitHub Actions — image19 (GitHub/GHCR logo)
#align(center)[
#grid(columns: (auto, auto, auto), gutter: 7pt,
align(horizon)[#icon-circle("../../resources/img/ui_images/images/image19.png", size: 24pt, bg: rgb("#1F2328"))],
align(horizon)[#text(size: 8.5pt, weight: "bold", fill: c-text)[GitHub Actions]],
align(horizon)[#text(size: 8.5pt, fill: c-muted)[Coverage]],
)
]
]
v(1fr)
}
//

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="238px" preserveAspectRatio="none" style="width:1106px;height:238px;" version="1.1" viewBox="0 0 1106 238" width="1106px" zoomAndPan="magnify"><defs><filter height="300%" id="fdgkvirjllmq" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><!--MD5=[d638d2e8639369a4c8d274dd926c65e9]
entity thingy--><rect fill="#ADD8E6" filter="url(#fdgkvirjllmq)" height="117.7813" style="stroke: #A80036; stroke-width: 1.5;" width="100" x="6" y="11.5"/><rect fill="#ADD8E6" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="1" y="16.5"/><rect fill="#ADD8E6" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="1" y="119.2813"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="69" x="16" y="34.4951">Thingy:52</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="80" x="16" y="50.792">──────────</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="78" x="16" y="67.0889">CO2, Temp</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="59" x="16" y="83.3857">Humidity</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="49" x="16" y="99.6826">Battery</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="54" x="16" y="115.9795">Window</text><!--MD5=[7b30460008fc71f9986330c33ee2d290]
entity gateway--><rect fill="#90EE90" filter="url(#fdgkvirjllmq)" height="101.4844" style="stroke: #A80036; stroke-width: 1.5;" width="151" x="310" y="20"/><rect fill="#90EE90" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="305" y="25"/><rect fill="#90EE90" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="305" y="111.4844"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="99" x="320" y="42.9951">Raspberry Pi 4</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="80" x="320" y="59.292">──────────</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="77" x="320" y="75.5889">gateway.py</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="129" x="320" y="91.8857">bleak + paho-mqtt</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="131" x="320" y="108.1826">asyncio + systemd</text><!--MD5=[e701bbac852ce3c7f1c3d2193e52d58f]
entity broker--><rect fill="#FFFFE0" filter="url(#fdgkvirjllmq)" height="85.1875" style="stroke: #A80036; stroke-width: 1.5;" width="119" x="710" y="28"/><rect fill="#FFFFE0" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="705" y="33"/><rect fill="#FFFFE0" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="705" y="103.1875"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="67" x="720" y="50.9951">RabbitMQ</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="80" x="720" y="67.292">──────────</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="99" x="720" y="83.5889">MQTTS broker</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="76" x="720" y="99.8857">TLS + auth</text><!--MD5=[7468467f3b6b392f1d176d828c753b77]
entity db--><rect fill="#FFA07A" filter="url(#fdgkvirjllmq)" height="36.2969" style="stroke: #A80036; stroke-width: 1.5;" width="76" x="1019" y="52.5"/><rect fill="#FFA07A" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="1014" y="57.5"/><rect fill="#FFA07A" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="1014" y="78.7969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="56" x="1029" y="75.4951">InfluxDB</text><path d="M307,156 L307,226.5313 A0,0 0 0 0 307,226.5313 L464,226.5313 A0,0 0 0 0 464,226.5313 L464,166 L454,156 L389.5,156 L385.5,121.02 L381.5,156 L307,156 A0,0 0 0 0 307,156 " fill="#FBFB77" filter="url(#fdgkvirjllmq)" style="stroke: #A80036; stroke-width: 1.0;"/><path d="M454,156 L454,166 L464,166 L454,156 " fill="#FBFB77" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="42" x="313" y="173.0669">Filters:</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="136" x="313" y="188.1997">- company_id = 0xffff</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="133" x="313" y="203.3325">- payload = 14 bytes</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="134" x="313" y="218.4653">- dedup 10s per MAC</text><!--MD5=[72a710db11e8fbeffe0299f3833be780]
link thingy to gateway--><path d="M106.03,70.5 C158.53,70.5 242.65,70.5 304.72,70.5 " fill="none" id="thingy-&gt;gateway" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="309.9,70.5,300.9,66.5,304.9,70.5,300.9,74.5,309.9,70.5" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="98" x="157.5" y="21.5669">BLE advertising</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="112" x="150.5" y="36.6997">company_id 0xffff</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="55" x="179" y="51.8325">14 bytes</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="139" x="137" y="66.9653">3 channels (37/38/39)</text><!--MD5=[d5fc4fe9659f4c010806cb130a744287]
link gateway to broker--><path d="M461.06,70.5 C531.95,70.5 637.45,70.5 704.59,70.5 " fill="none" id="gateway-&gt;broker" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="709.76,70.5,700.76,66.5,704.76,70.5,700.76,74.5,709.76,70.5" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="94" x="540" y="36.5669">MQTTS port 80</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="86" x="544" y="51.6997">JSON payload</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="184" x="495" y="66.8325">{gateway_id}/{mac}/update</text><!--MD5=[b33725c11cd6fb0cd801e7a81b9a0575]
link broker to db--><path d="M829.31,70.5 C884.12,70.5 964.53,70.5 1013.4,70.5 " fill="none" id="broker-&gt;db" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="1018.61,70.5,1009.61,66.5,1013.61,70.5,1009.61,74.5,1018.61,70.5" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="128" x="860" y="66.5669">store measurement</text><!--MD5=[9109db2b88df5a8c8bdd4041f3fe8ffc]
@startuml
skinparam linestyle ortho
skinparam componentStyle rectangle
skinparam backgroundColor white
left to right direction
component "Thingy:52\n──────────\nCO2, Temp\nHumidity\nBattery\nWindow" as thingy #LightBlue
component "Raspberry Pi 4\n──────────\ngateway.py\nbleak + paho-mqtt\nasyncio + systemd" as gateway #LightGreen
component "RabbitMQ\n──────────\nMQTTS broker\nTLS + auth" as broker #LightYellow
component "InfluxDB" as db #LightSalmon
thingy - -> gateway : BLE advertising\ncompany_id 0xffff\n14 bytes\n3 channels (37/38/39)
gateway - -> broker : MQTTS port 80\nJSON payload\n{gateway_id}/{mac}/update
broker - -> db : store measurement
note bottom of gateway
Filters:
- company_id = 0xffff
- payload = 14 bytes
- dedup 10s per MAC
end note
@enduml
PlantUML version 1.2020.02(Sun Mar 01 11:22:07 CET 2020)
(GPL source distribution)
Java Runtime: OpenJDK Runtime Environment
JVM: OpenJDK 64-Bit Server VM
Java Version: 21.0.11+10-1-deb13u2-Debian
Operating System: Linux
Default Encoding: UTF-8
Language: en
Country: GB
--></g></svg>

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 638 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB