Acest document este manualul tehnic complet al platformei IOANAD-WX (cunoscuta si ca SPARTAN-MET) — sub-componenta web a sistemului de suport-decizie meteo pentru avionul de transport tactic C-27J Spartan, dezvoltat ca proiect academic la Academia Tehnica Militara (ATM) in 2026. Documentul este destinat autoarei (pentru redactarea memoriului de licenta), comisiei (pentru sustinere) si operatorilor (instructori/cursanti) care folosesc platforma in briefing-uri.
📑 Cuprins
- Mission & obiective
- Arhitectura sistem
- Stack tehnologic
- Model de date · scenariu WX
- Tipuri fenomene meteo
- Evolutie temporala & overrides
- Mission Planner (Editor WX)
- Briefing Replay
- API tactical endpoint
- Autentificare & securitate
- Workflow operational
- Integrare cu C-27J Radar (APK)
- Integrare cu MATLAB decision engine
- Integrare cu ESP32 cabin gateway
- Deploy & ops
- Structura fisiere proiect
- Runbook operator
- Troubleshooting
- Anexa pentru memoriu licenta
- Glosar termeni
🎯 1. Mission & Obiective
Aplicatia IOANAD-WX este o platforma web pentru generarea, distributia si replay-ul scenariilor meteo folosite ca input in simularea zborurilor tactice C-27J. Misiunea ei este sa asigure un "single source of truth" pentru un scenariu meteo, accesibil in mod identic de:
C-27J Radar (APK)
Aplicatia Android pe tableta in cabina — descarca scenariul, evalueaza vremea la pozitia GPS curenta, trimite output catre ESP32 si MATLAB.
MATLAB Decision Engine
Engine-ul impus de profesor — citeste GPS de la ESP32, evalueaza scenariul si recomandarea de zbor (NORMAL/DEV/DIVERT/GO-AROUND).
Operator (instructor)
Editeaza scenariul in browser pe pagina /editor/, salveaza pe server. Modificarea este vizibila imediat de toate consumerele.
Briefing Replay
Player TV-style pentru proiector in sala de briefing si AAR (After Action Review).
Obiective masurabile
| Obiectiv | Metrica | Target |
|---|---|---|
| Latenta editor → APK | secunde de la save la fetch reusit | < 3 s |
| Capacitate scenarii | numar fenomene WX simultane intr-un scenariu | ≥ 30 |
| Durata scenariu | secunde simulate | 60 ÷ 14 400 s (4 h) |
| Auth API endpoint | token hex 32 caractere min | ≥ 32 |
| Rate limit | request-uri pe minut per token+IP | 120/min |
| Persistenta | JSON pe disc, atomic write | 0 corruptie la crash |
📡 2. Arhitectura sistem
Sistemul are trei niveluri: web (IOANAD-WX), tactical edge (APK + ESP32 + GPS), decision (MATLAB).
Principii arhitecturale
- Decuplaj prin JSON: toate componentele schimba structuri JSON identice. Schimbarea modelului afecteaza un singur fisier (
schema_version). - Read-only consumer, write-only producer: doar editorul scrie, restul citesc. Reduce risc de coruptie.
- Atomic write: scriere JSON cu fisier temporar + rename — nu exista stare partiala vizibila la consumer.
- Token-uri per consumer: APK si MATLAB au token-uri separate → revocare granulara.
- Audit log obligatoriu: orice operatie reusita sau esuata e logata cu timestamp + IP + alias token.
⚙ 3. Stack tehnologic
| Layer | Tehnologie | Rol |
|---|---|---|
| Hosting | Apache 2.4 + mod_rewrite + mod_auth_basic | HTTPS terminator, autentificare Basic, rewrites |
| Backend | PHP 7.1+ (compatibil pana la 8.2) | Endpoint API, editor, replay player |
| Persistenta | Fisiere JSON pe disc | Niciun database server — portabil, backup simplu (tar) |
| Frontend editor | Leaflet 1.9 + Leaflet.draw + Vanilla JS | Harta interactiva, desenare obiecte WX |
| Frontend replay | Leaflet 1.9 + Vanilla JS (read-only) | Animatie scenariu, time-bar, viteza variabila |
| Auth (editor) | HTTP Basic + bcrypt (.ht_ioanad) | Producer-only zone |
| Auth (API) | Token fix (32+ hex) in config.php | Consumer (APK/MATLAB) |
| Audit | Append-only log file | Conformitate & debugging |
De ce nu MySQL/PostgreSQL
Pentru un proiect academic cu < 100 scenarii pe disc si concurenta minima (un singur instructor scrie odata), JSON pe disc + atomic rename ofera:
- Zero dependinte de DB server (deploy =
scp -r+chmod) - Backup trivial:
tar czf snapshot.tgz scenarios/ - Versiune-control friendly:
diffpe JSON-uri - Inspectie umana: deschizi fisierul cu orice editor
- Recuperare la crash: rename atomic = stare consistenta garantata
Trade-off: nu se scaleaza la > 1000 scenarii sau editari concurente. Pentru un proiect didactic e ideal.
📋 4. Model de date — scenariu WX
Un scenariu meteo este un fisier JSON in /scenarios/wx/<id>.json. Schema (schema_version: 2):
{
"id": "wx_real_ro_20240613", // unic, [A-Za-z0-9_-]{1,64}
"type": "wx",
"name": "Eveniment real 13/06/2024 — Outbreak supercele V→E",
"schema": 2,
"version": 1, // bump la fiecare save
"created": "2026-05-03T10:00:00+00:00",
"modified": "2026-05-03T10:00:00+00:00",
"modified_by": "ioana",
"scenario": {
"duration_s": 28800, // 8h simulate
"wx_objects": [
{
"id": "cb_drobeta_mehedinti", // unic in scenariu
"type": "CB", // vezi sectiunea 5
"label": "Supercell Drobeta-T.Severin",
"t_start": 600, // s de la t=0
"t_end": 5400, // null = pana la final
"geom": {
"center": [44.63, 22.66], // [lat, lon] WGS84
"radius_nm": 7,
"top_ft": 45000,
"intensity_pct": 95
},
"evolution": {
"motion": {"dir_deg": 80, "speed_kt": 26},
"rates": {"radius_nm": 1, "top_ft": -500, "intensity_pct": -8}
},
"overrides": [
{"t": 1800, "geom": {"intensity_pct": 98}},
{"t": 4200, "geom": {"intensity_pct": 70}}
]
}
// … alte fenomene
]
}
}
Camp cu camp
| Camp | Tip | Mandatory | Semnificatie |
|---|---|---|---|
id | string | DA | Identificator unic, regex ^[A-Za-z0-9_-]{1,64}$. Deveine numele fisierului. |
type | "wx" | DA | Discriminator pentru API (alaturi de flight, mission). |
name | string | recomandat | Eticheta umana (max 100 caractere). |
schema | int | DA | Versiune model — actual 2. Cresterea = breaking change. |
version | int | DA | Bumped la fiecare save. Util pentru detect "scenariu schimbat" la consumer. |
scenario.duration_s | int | DA | Total secunde simulate. Actuall 60 ÷ 14400 (4h max). |
scenario.wx_objects[] | array | DA | Lista de fenomene. Vezi sectiunea 5. |
⛈ 5. Tipuri fenomene meteo
Definitiile sunt in assets/wx_types.json. Pentru fiecare tip se specifica
shape (cell / polygon / line), geom_defaults, evolution_defaults si rank (prioritate vizuala).
| Cod | Nume | Shape | Icon | Geom default | Default motion | Rank |
|---|---|---|---|---|---|---|
CB | Cumulonimbus / Thunderstorm | cell | ⛈ | r=8nm, top=35kft, 100% | 270°/25kt, intens -15%/h | 100 |
SQUALL | Squall line | line | 🌬 | w=2nm, 90% | 90°/30kt | 90 |
FRONT_C | Cold front | line | ❄ | w=5nm, 80% | 90°/18kt | 80 |
FRONT_W | Warm front | line | ♨ | w=8nm, 60% | 60°/12kt | 40 |
ICE | Icing area | polygon | 🧊 | top=18kft, base=6kft, 70% | 270°/20kt | 70 |
TURB | Turbulence | polygon | 🌀 | top/base/intens | variabil | 60 |
WS | Wind shear | cell | ↯ | r=3nm, 80% | variabil | 50 |
FOG | Ceata / vizibilitate redusa | polygon | 🌫 | top=800ft, 70% | 0°/0kt (statica) | 30 |
Shape-uri si campuri geometrice
cell (CB, WS)
"geom": {
"center": [lat, lon],
"radius_nm": <float>,
"top_ft": <int>,
"intensity_pct":<0..100>
}
polygon (ICE, TURB, FOG)
"geom": {
"points": [[lat,lon], …],
"top_ft": <int>,
"base_ft": <int>,
"intensity_pct":<0..100>
}
line (SQUALL, FRONT_*)
"geom": {
"points": [[lat,lon], …],
"width_nm": <float>,
"intensity_pct":<0..100>
}
⏳ 6. Evolutie temporala & overrides
Un fenomen are 3 surse de evolutie aplicate in ordine (composition rule):
- Geometrie initiala (
geom) — lat = t_start. - Rates auto (
evolution.motion+evolution.rates) — interpolare liniara intre snap-uri. - Overrides (
overrides[]) — snap-uri forțate la momentetspecifice; sterg rate-urile pana la urmatorul snap.
Algoritm de evaluare la moment t
function evaluate(obj, t) {
if (t < obj.t_start) return null; // inca nu exista
if (obj.t_end !== null && t > obj.t_end) return null; // a expirat
// 1) start cu geometria initiala
let geom = clone(obj.geom);
// 2) determina ultimul override aplicabil
let prev_t = obj.t_start;
let prev_geom = clone(obj.geom);
for (const ov of (obj.overrides || [])) {
if (ov.t <= t) { prev_t = ov.t; prev_geom = merge(prev_geom, ov.geom); }
else break;
}
// 3) aplica rates intre prev_t si t
const dt_h = (t - prev_t) / 3600;
if (geom.center) geom.center = move(geom.center, motion, dt_h);
if (geom.points) geom.points = geom.points.map(p => move(p, motion, dt_h));
if (geom.radius_nm) geom.radius_nm += rates.radius_nm * dt_h;
if (geom.top_ft) geom.top_ft += rates.top_ft * dt_h;
if (geom.intensity_pct) geom.intensity_pct = clamp(geom.intensity_pct + rates.intensity_pct * dt_h, 0, 100);
return geom;
}
De ce overrides in plus fata de rates
Rates capteaza evolutia "naturala" continua (front se misca constant, intensitatea scade). Overrides capteaza evenimente discrete: "la t=1800 supercelula explodează la 98%" sau "la t=4200 se disipează la 70%". Combinatia permite scenarii bogate fara cod custom per scenariu.
Exemplu vizual de evolutie
t=0 t=600 t=1800 t=4200 t=5400
│ │ │ │ │
○ creat ●● 95% ●●● 98% ●● 70% × disipat
↑ ↑ ↑ ↑
t_start override #1 override #2 t_end
radius=7 intensity=98 intensity=70 end
(din geom) (forced) (forced) (cleanup)
↓ rates intre snap-uri:
radius +1 nm/h, top -500 ft/h, intensity -8%/h
🛠 7. Mission Planner (Editor WX)
Editor accesibil la https://ioanad.mensoft.ro/editor/ dupa autentificare HTTP Basic
(user ioana, parola in .ht_ioanad bcrypt).
Layout pagina
┌─────────────────────────────────────────────────────────────┐
│ TOPBAR: nume scenariu | id | [Nou] [Save] [Load] [JSON] │
├──────────────┬──────────────────────────────────────────────┤
│ PANEL STG. │ │
│ │ │
│ Durata sim. │ HARTA LEAFLET │
│ [_____] s │ (RO + vecini) │
│ │ │
│ Tipuri WX │ - desenare cell (click) │
│ ⛈ CB │ - desenare polygon (Leaflet draw) │
│ 🌬 SQUALL │ - desenare line │
│ ❄ FRONT_C │ │
│ 🌫 FOG │ │
│ … │ │
│ │ │
│ Lista WX │ │
│ ☐ obj1 ⛈ │ │
│ ☐ obj2 🌫 ├──────────────────────────────────────────────┤
│ … │ TIME-BAR: ▶ [====•=========] t=600s │
│ │ viteza ×10 ▼ │
└──────────────┴──────────────────────────────────────────────┘
Flow operator: creeaza scenariu nou
- Click 📄 Nou — se goleste canvas-ul si lista.
- In topbar, set id (ex:
WX_TS_BUC_DEMO) si nume descriptiv. - In panel stanga, set durata simulare (ex: 1800 s = 30 min).
- Click pe un tip WX (ex: ⛈ CB) → cursorul devine "+", click pe harta unde plasezi cella.
- Pe lista din stanga apare obiectul. Click pe el deschide panel-ul de editare:
- Geometrie: raza, varf, intensitate
- Evolutie: directie + viteza miscare, rate intensitate, rate raza
- Overrides: + adauga override la moment t cu modificari forțate
- t_start, t_end
- Repeti pentru toate fenomenele.
- Apesi ▶ Play in time-bar pentru preview animat — vezi cum se misca/intensifica fenomenele.
- Cand esti multumita, click 💾 Save → POST
/api/save.php→ JSON pe disc.
API folosit de editor (intern)
| Endpoint | Method | Auth | Body / Query | Response |
|---|---|---|---|---|
/api/save.php | POST | Basic | JSON scenariu complet | {ok:true, version:N} |
/api/load.php?id=X&type=wx | GET | Basic | — | JSON scenariu |
/api/list.php?type=wx | GET | Basic | — | {scenarios:[…]} |
/api/delete.php | POST | Basic | {id:"X",type:"wx"} | {ok:true} |
📺 8. Briefing Replay
Replay player accesibil la https://ioanad.mensoft.ro/editor/play.php
sau direct cu parametru ?id=<scenario>. Scopul:
prezentare in sala de briefing sau AAR (After Action Review) dupa misiune.
Caracteristici
- Read-only — nu modifica scenariul, doar citeste.
- Time-bar: Play / Pause / Rewind / Speed select (×1, ×5, ×10, ×30, ×60).
- Animatie continua: la fiecare frame (~250 ms) recalculeaza pozitia/intensitatea fiecarui fenomen.
- Auto-fit map la bounding box al tuturor obiectelor scenariu.
- Optimizat proiector: contraste mari, font legibil de la 5 m.
Caz de utilizare 1 — Briefing pre-misiune
- Instructorul deschide
play.php?id=WX_REAL_RO_20240613pe laptop conectat la proiector. - Apesi ▶ la viteza ×30 → 8h simulate in ~16 min reale.
- Cursantii observa cum se dezvolta supercelele V→E peste tara.
- Pauza la momente cheie: "aici ce decizie iei daca esti FL080 deasupra Olteniei?"
- La final: deschidem APK pe tableta cu acelasi scenariu — verificam ca calculele coincid.
Caz de utilizare 2 — AAR debrief
- Dupa misiunea simulata in MATLAB, MATLAB exporta log GPS + decisii.
- Replay player rulează scenariul WX in paralel cu track-ul GPS suprapus.
- Comisia vede unde a deviat avionul si compara cu pozitia fenomenelor.
- Discutie: "de ce a virat la dreapta in loc de stanga?" — raspuns vizibil pe harta.
🛰 9. API tactical endpoint
Endpoint-ul public pentru consumere (APK + MATLAB):
GET https://ioanad.mensoft.ro/api/endpoint.php
Parametri comuni
| Parametru | Sursa | Mandatory | Exemplu |
|---|---|---|---|
token | query string sau header X-Auth-Token | DA | a1b2c3... |
action | query | DA | ping|list|get |
type | query | la list/get | wx |
id | query | la get | wx_real_ro_20240613 |
Action: ping
GET /api/endpoint.php?token=<APK_TOKEN_HEX>&action=ping
200 OK
{
"ok": true,
"server_time": "2026-05-03T13:45:12+00:00",
"who": "apk_c27j_v1",
"rate_used": 3,
"rate_max": 120
}
Action: list
GET /api/endpoint.php?token=<APK_TOKEN_HEX>&action=list&type=wx
200 OK
{
"count": 2,
"scenarios": [
{
"id": "wx_real_ro_20240613",
"type": "wx",
"name": "Outbreak supercele V→E Romania",
"version": 1,
"modified": "2026-05-03T10:00:00+00:00",
"duration_s": 28800,
"wx_count": 12
},
{
"id": "wx_real_derecho_20220817",
"type": "wx",
"name": "Derecho Europa Centrala 17/08/2022",
"version": 1,
"modified": "2026-05-03T10:30:00+00:00",
"duration_s": 21600,
"wx_count": 8
}
]
}
Action: get
GET /api/endpoint.php?token=<APK_TOKEN_HEX>&action=get&type=wx&id=wx_real_ro_20240613
200 OK
{
"id": "wx_real_ro_20240613",
"type": "wx",
"name": "…",
"schema": 2,
"version": 1,
"scenario": { "duration_s": 28800, "wx_objects": [...] }
}
Coduri eroare
| HTTP | error | Cauza | Remedy |
|---|---|---|---|
| 400 | invalid type | type ≠ wx|flight|mission | verifica parametru |
| 400 | invalid id | id contine caractere ne-permise | regex ^[A-Za-z0-9_-]{1,64}$ |
| 400 | unknown action | action ≠ ping|list|get | vezi sectiunea 9 |
| 401 | missing token | nu s-a trimis token | adauga ?token=… sau header |
| 403 | invalid token | token nu se potriveste | cere unul nou de la admin |
| 404 | not found | scenariu inexistent | verifica list mai intai |
| 405 | method not allowed | POST in loc de GET | foloseste GET |
| 429 | rate limit exceeded | >120 cereri/min/IP | asteapta 60 s |
| 500 | server misconfigured | config.php lipsa/corupt | contact admin |
🔐 10. Autentificare & securitate
Doua zone, doua mecanisme
| Zona | URL | Mecanism | Storage credentiale |
|---|---|---|---|
| Producer (operator) | /editor/* + /api/save|load|list|delete.php | HTTP Basic + bcrypt | .ht_ioanad |
| Consumer (APK/MATLAB) | /api/endpoint.php | Token fix in query/header | config.php (NU in git) |
| Public viewer | / + /editor/play.php | None | — |
Storage tokeni — config.php
<?php
return array(
'tokens' => array(
'apk_c27j_v1' => '<APK_TOKEN_HEX>', // 32+ hex chars
'matlab_c27j_v1' => '<MATLAB_TOKEN_HEX>',
),
'rate_limit_per_min' => 120,
);
Reguli stricte tokens
- Generare:
openssl rand -hex 32(64 hex chars). Minim 32. - Storage: doar pe server, fisier
config.phpcuchmod 0640 root:apache. - Distributie: niciodata prin email/Slack/SMS. Doar transferat manual la cursant pe USB sau scris pe foaie.
- Compare: cu
hash_equals()(constant-time) — NU===(vulnerabil la timing attack). - Revocare: stergere linie din
config.php. Token-urile sunt independente, revocarea unuia nu afecteaza celelalte. - Rotire: la fiecare schimb de operator sau la 6 luni, generezi token nou si actualizezi APK + MATLAB.
Rate limiting
Implementat in endpoint.php cu fisiere temporare per (token_alias, IP) in sys_get_temp_dir()/ioanad_rl/.
Fereastra fixa de 60 s, contor incremental, reset cand timestamp-ul depaseste 60 s.
La depasire: HTTP 429.
Audit log
/scenarios/_audit.log
[2026-05-03T13:45:12+00:00] ? | 192.168.168.103 | endpoint_list | apk_c27j_v1 type=wx count=2
[2026-05-03T13:45:18+00:00] ? | 192.168.168.103 | endpoint_get | apk_c27j_v1 type=wx id=wx_real_ro_20240613 v=1
[2026-05-03T13:46:01+00:00] ? | 5.14.163.55 | endpoint_auth_fail | token_prefix=deadbe... ip=5.14.163.55
[2026-05-03T13:50:22+00:00] ioana | 192.168.168.103 | scenario_save | id=WX_TS_BUC_DEMO type=wx version=3
Headers HTTP de securitate
Cache-Control: no-store— nu se cache-uieste raspunsul (mai ales tokenii in URL ar putea ajunge in cache).X-Robots-Tag: noindex,nofollow— nu indexa platforma in motoare de cautare.Access-Control-Allow-Origin: *— APK + MATLAB pot fi pe orice IP.Access-Control-Allow-Methods: GETpe endpoint,GET, POSTpe save.
HTTPS obligatoriu
.htaccess redirectioneaza orice HTTP → HTTPS cu 301:
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
🪖 11. Workflow operational
De la "vreau sa fac un briefing" la "avionul a aterizat in MATLAB":
Cine atinge ce
| Rol | Atinge | Nu atinge |
|---|---|---|
| Instructor | Editor (creeaza/modifica), Replay player | API tokens (le primeste, nu le genereaza) |
| Cursant operator | APK pe tableta | Editor (read-only via Replay) |
| Pilot simulator | MATLAB UI | API direct (MATLAB face apelurile) |
| Admin sistem | config.php, deploy, certificate | Continut scenarii (nu intervine) |
| Comisia | Replay player la AAR | Nimic altceva |
📱 12. Integrare cu C-27J Radar (APK)
C-27J Radar este aplicatia Android (numele real al pachetului — anonimizat in document) care ruleaza pe tableta in cabina. Ea consuma scenariile WX din IOANAD-WX si trimite output catre ESP32 + MATLAB.
Sectiunea Replay → Remote in APK
In APK exista un panou Replay cu doua taburi:
- Local — scenarii pre-incarcate in
assets/sau cache descarcate anterior. - Remote — fetch live de la
ioanad.mensoft.ro.
Pseudocod fetch in Kotlin (anonimizat)
// package ro.aviation.c27jradar.debug
// (numele real al pachetului — anonimizat in document)
class IoanadWxClient(private val baseUrl: String, private val token: String) {
fun listScenarios(): List<ScenarioInfo> {
val url = "$baseUrl/api/endpoint.php?token=$token&action=list&type=wx"
val resp = httpGet(url, headers = mapOf("X-Auth-Token" to token))
if (resp.code == 429) throw RateLimitException(resp.body)
if (resp.code in 400..499) throw AuthException("API: ${resp.body}")
val json = JSONObject(resp.body)
return json.getJSONArray("scenarios").mapToList { ScenarioInfo(it) }
}
fun getScenario(id: String): WxScenario {
val url = "$baseUrl/api/endpoint.php?token=$token&action=get&type=wx&id=$id"
val resp = httpGet(url)
return WxScenario.parse(resp.body)
}
}
// Apoi in flow-ul Apply/Run:
val scenario = client.getScenario("wx_real_ro_20240613")
val timer = scheduleAtFixedRate(period = 5_000L) {
val gps = espClient.fetchGps() // GET /gps de la ESP32
val wxNow = scenario.evaluate(gps.t, gps.lat, gps.lon)
val advice = AdvicePayload(gps, wxNow)
espClient.postAdvice(advice) // POST /advice la ESP32
}
Caching in APK
- Dupa
getreusit, JSON-ul e salvat infilesDir/cache/wx/<id>.json. - La urmatoarea pornire, daca
listarata acelasiversion, foloseste cache. - Daca server-ul nu raspunde, foloseste cache (modul "offline tactical").
🖥 13. Integrare cu MATLAB decision engine
MATLAB este "creierul" sistemului (impus de profesor) — citeste GPS de la ESP32, evalueaza scenariul si ia decizii.
Cod MATLAB pentru fetch scenariu
% +io/fetchWxScenario.m
function sc = fetchWxScenario(id)
base = 'https://ioanad.mensoft.ro/api/endpoint.php';
token = getenv('IOANAD_MATLAB_TOKEN'); % NU hardcoded
if isempty(token)
error('Missing env IOANAD_MATLAB_TOKEN');
end
opts = weboptions('HeaderFields', {'X-Auth-Token', token}, ...
'Timeout', 10, ...
'ContentType', 'json');
url = sprintf('%s?action=get&type=wx&id=%s', base, id);
sc = webread(url, opts);
end
% +io/listWxScenarios.m
function out = listWxScenarios()
base = 'https://ioanad.mensoft.ro/api/endpoint.php';
token = getenv('IOANAD_MATLAB_TOKEN');
opts = weboptions('HeaderFields', {'X-Auth-Token', token});
j = webread([base '?action=list&type=wx'], opts);
out = j.scenarios;
end
Decision tree dupa fetch WX
% main.m (pseudo)
sc = io.fetchWxScenario('wx_real_ro_20240613');
while running
gps = io.getEspGps(); % webread ESP32 /gps
[wx_now, severity] = calc.evaluateAt(sc, gps.t, gps.lat, gps.lon);
metrics = calc.computeMetrics(gps, wx_now); % XWC, F-factor, CIP, EDR
[scenarioId, recommendation] = rules.decide(metrics);
io.sendEsp(scenarioId, recommendation); % TCP la ESP32 display
pause(0.2);
end
Cache local + fallback offline
MATLAB salveaza fiecare get in data/wx_cache/<id>.json cu timestamp.
Daca webread da timeout (sala fara internet), incarca din cache si afiseaza warning vizibil
in App Designer.
🪛 14. Integrare cu ESP32 cabin gateway
ESP32 este relay-ul intre APK si MATLAB. Important de inteles: ESP32 NU acceseaza
direct ioanad.mensoft.ro (resursa de RAM limitata, certificat HTTPS pe device complica viata).
APK face fetch din IOANAD-WX si trimite output procesat catre ESP32 prin /advice JSON local.
Endpoint-uri ESP32 (recap din spec proiect)
| Endpoint | Method | Sursa | Continut |
|---|---|---|---|
/gps | GET | oricine in WiFi local | JSON cu lat,lon,t,fix |
/advice | POST | APK | JSON cu OWM + METAR + WX-now (din scenariu IOANAD) |
/display | POST | MATLAB | JSON cu scenariu de afisat pe TFT (severity, recomandare) |
/setgpsstart | POST | MATLAB | fake GPS pentru replay (lat,lon,heading,speed) |
De ce APK e intermediar in fluxul WX
- APK are certificate Android trust store — HTTPS la ioanad e plug-and-play.
- APK are ecran si UI — operator vede ce scenariu e activ, cu vizualizare.
- ESP32 are buget mic RAM — nu ne permitem json cu 30+ obiecte parsat in C++.
- APK cache-uieste scenariul → ESP32 primeste doar evaluarea curenta (mai compact).
🛬 15. Deploy & ops
Cerinte server
- Apache 2.4 +
mod_rewrite+mod_auth_basic - PHP 7.1+ (testat 7.4 si 8.2)
- Permisiune scriere pe
/scenarios/pentru user-ul Apache (apachesauwww-data) - Certificat HTTPS valid (Let's Encrypt sau cumparat)
- Sistem fisiere cu suport
rename()atomic (orice ext4/xfs/ntfs)
Procedura deploy initial
# 1) Upload fisiere
scp -r ioanad_mensoft/ user@host:/var/www/
# 2) Permisiuni
ssh user@host
cd /var/www/ioanad_mensoft
sudo chown -R apache:apache scenarios/
sudo chmod 750 scenarios/
sudo chmod 750 scenarios/wx scenarios/flight scenarios/mission
sudo chmod 644 .htaccess .ht_ioanad
# 3) Config tokeni
cd api/
cp config.example.php config.php
nano config.php
# inlocuieste <APK_TOKEN_HEX> cu output-ul lui:
# openssl rand -hex 32
sudo chmod 640 config.php
sudo chown root:apache config.php
# 4) VirtualHost Apache
sudo nano /etc/httpd/conf.d/ioanad.conf
<VirtualHost *:443>
ServerName ioanad.mensoft.ro
DocumentRoot /var/www/ioanad_mensoft
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/ioanad.mensoft.ro/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/ioanad.mensoft.ro/privkey.pem
<Directory /var/www/ioanad_mensoft>
AllowOverride All
Require all granted
</Directory>
# Editor: Basic Auth
<Directory /var/www/ioanad_mensoft/editor>
AuthType Basic
AuthName "IOANAD-WX Operator"
AuthUserFile /var/www/ioanad_mensoft/.ht_ioanad
Require valid-user
</Directory>
# API write endpoints: Basic Auth
<FilesMatch "^(save|load|list|delete)\.php$">
AuthType Basic
AuthName "IOANAD-WX Operator"
AuthUserFile /var/www/ioanad_mensoft/.ht_ioanad
Require valid-user
</FilesMatch>
# endpoint.php: NU cere Basic Auth (token in query)
<Files endpoint.php>
Satisfy Any
Allow from all
</Files>
</VirtualHost>
# 5) Reload
sudo apachectl configtest && sudo systemctl reload httpd
# 6) Test rapid
curl -k https://ioanad.mensoft.ro/api/endpoint.php?token=<APK_TOKEN>&action=ping
# expect: {"ok":true,"server_time":"...","who":"apk_c27j_v1",...}
Schimbare parola operator
htpasswd -B /var/www/ioanad_mensoft/.ht_ioanad ioana
# (-B = bcrypt; introduce parola noua de doua ori)
Backup
# cron pe server (zilnic la 03:00)
0 3 * * * tar czf /backup/ioanad-$(date +\%Y\%m\%d).tgz \
/var/www/ioanad_mensoft/scenarios \
/var/www/ioanad_mensoft/api/config.php \
/var/www/ioanad_mensoft/.ht_ioanad
Monitoring
- Health check: cron 5 min —
curl pingla endpoint, alerta daca ne-200. - Audit growth: log
/scenarios/_audit.logrotated la 50 MB. - Disk usage: alerta daca
/var/www/ioanad_mensoft> 500 MB (suspect).
📂 16. Structura fisiere proiect
ioanad_mensoft/
├── index.php landing page (tema militara, dashboard module)
├── .htaccess HTTPS redirect, blocheaza .md/.log/.bak
├── .ht_ioanad bcrypt hash pentru "ioana"
│
├── assets/
│ ├── theme_military.css tema vizuala comuna
│ ├── airports.json 29 aerodromuri RO + vecini (LR* / LB* / LU* / LH* …)
│ └── wx_types.json 8 tipuri WX cu defaults
│
├── api/
│ ├── _lib.php helpers comun: resp_json, scenario_path, atomic_write
│ ├── config.example.php sablon tokeni
│ ├── config.php (NU in git) tokeni reali
│ ├── endpoint.php PUBLIC — read-only consumer (APK/MATLAB)
│ ├── list.php Basic Auth — listare pentru editor
│ ├── load.php Basic Auth — load scenariu
│ ├── save.php Basic Auth — save (atomic write)
│ └── delete.php Basic Auth — delete scenariu
│
├── editor/
│ ├── index.php dashboard editor cu carduri module
│ ├── weather.php Mission Planner — editor WX
│ ├── flight.php Route Editor (WIP)
│ ├── play.php Briefing Replay player
│ ├── editor.css stiluri editor (panel-uri, time-bar)
│ ├── play.js logica animatie replay
│ └── weather.js logica editor WX (Leaflet draw, evolutie)
│
├── scenarios/
│ ├── wx/ *.json scenarii meteo
│ │ ├── wx_real_ro_20240613.json
│ │ └── wx_real_derecho_20220817.json
│ ├── flight/ *.json rute zbor (WIP)
│ ├── mission/ *.json scenarii compuse (wx + flight + tasks)
│ └── _audit.log audit trail (append-only)
│
├── docs/
│ └── index.html ACEST DOCUMENT (manual tehnic)
│
└── lib/ placeholder pentru librarii viitoare
📕 17. Runbook operator
Sarcina A: creezi scenariu nou pentru briefing
- Browser →
https://ioanad.mensoft.ro/editor/ - Login:
ioana/ parola - Click ⛈ WX Editor
- In topbar: 📄 Nou; set
id+nume - Stanga: set durata simulare
- Click pe tip WX (ex: ⛈ CB) → click pe harta → set proprietati
- Repeti pentru fiecare fenomen
- ▶ Play in time-bar pentru preview
- 💾 Save
- Verificare: deschide alta fila →
/editor/play.php?id=NUME→ vezi scenariul live
Sarcina B: distribui token APK la cursant
- SSH ca admin pe server
cat /var/www/ioanad_mensoft/api/config.php | grep apk_c27j- Notezi token-ul pe foaie sau USB
- Pe tableta cursant, deschide APK → Settings → IOANAD endpoint → paste token
- Test: in APK tap Replay → Remote → Refresh → daca apare lista, OK
- Niciodata token prin email/Slack
Sarcina C: revoca un token compromis
- SSH server
sudo nano /var/www/ioanad_mensoft/api/config.php- Sterge linia cu token-ul compromis sau inlocuieste cu valoare random noua
- Salveaza
- Notifica APK + MATLAB cu noul token (manual)
- Verifica audit log:
grep auth_fail /var/www/ioanad_mensoft/scenarios/_audit.log | tail -50
Sarcina D: import scenariu existent (din JSON brut)
- Editor → 📂 Load nu e ce vrei (cere ID existent)
- Solutie:
scp scenariul.json server:/var/www/ioanad_mensoft/scenarios/wx/ - Set
chown apache:apache+chmod 640 - Reincarca editor → apare in lista
- Sau direct: foloseste in API
getdupa ce-a fost copiat manual
🚨 18. Troubleshooting
| Simptom | Cauza probabila | Rezolvare |
|---|---|---|
| HTTP 401 la endpoint cu token corect | Apache adaugă Basic Auth peste endpoint.php | Verifica VHost: endpoint.php trebuie cu Satisfy Any sau in afara FilesMatch |
| HTTP 500 la save | Permisiune scriere pe /scenarios/wx/ | chown -R apache:apache scenarios; chmod 750 |
| JSON corupt dupa save | Disc plin — atomic write esueaza | df -h; sterge backup-uri vechi; reincearca |
| APK primeste 429 frecvent | Polling prea agresiv | In APK: cresterea intervalului fetch list la 60 s; get doar la schimb scenariu |
| Editor nu salveaza, console JS arata 401 | Sesiune Basic expirata | Refresh pagina, reintroducere parola |
| Replay player gol pentru un scenariu | Schema scenariu < 2 sau JSON invalid | jq . /var/www/.../scenariu.json — verificare sintaxa |
| Time-bar nu se misca in preview | JS eroare in weather.js | F12 console → fixezi obiect cu geom incomplet |
MATLAB webread timeout | WiFi sala blocheaza HTTPS sau proxy | Alternativ hotspot mobil; sau MATLAB cu weboptions('Proxy',...) |
| Audit log creste prea repede | Bot scaneaza endpoint | Log rotation + fail2ban regula pe HTTP 403/401 repetat |
| Scenariul "dispare" din lista dupa save | id duplicat, suprascris | Verifica scenario.modified; backup zilnic recupereaza |
Comenzi diagnostic rapide
# API health
curl -k "https://ioanad.mensoft.ro/api/endpoint.php?token=XXX&action=ping"
# Listare scenarii din shell
curl -k "https://ioanad.mensoft.ro/api/endpoint.php?token=XXX&action=list&type=wx" | jq .
# Verifica permisiuni
ls -la /var/www/ioanad_mensoft/scenarios/wx/
# Ultimele eroari API
tail -100 /var/www/ioanad_mensoft/scenarios/_audit.log | grep -E "fail|err"
# Apache error log
sudo tail -200 /var/log/httpd/error_log | grep ioanad
# Stress test endpoint (verificare rate limit)
ab -n 200 -c 10 "https://ioanad.mensoft.ro/api/endpoint.php?token=XXX&action=ping"
🎖 19. Anexa pentru memoriul de licenta
Aceasta sectiune este destinata explicit pentru citarea in memoriu si maparea pe cerintele profesorului.
Maparea pe capitole 2.x / 3.x cerute
| Capitol cerut | Acoperire IOANAD-WX | Sectiuni din acest doc |
|---|---|---|
| 2.1 Rolul informatiilor meteo | WX scenario ca date primare; ARINC-661 conformity (intentie) | §1, §11 |
| 2.3.2 Surse meteo | IOANAD-WX = sursa simulata cu evolutie deterministica | §4, §5, §6 |
| 2.3.3 Algoritm | Evaluation function: rate-uri liniare + overrides; rule engine MATLAB consuma | §6, §13 |
| 2.3.4 Cele 5 scenarii canonice | Salvate ca scenarii distincte in /scenarios/wx/ (S0..S4) | §4, §17 |
| 2.3.5 Interfata | 2 TFT pe ESP32 = consumer; APK = producer pentru ESP32 | §14 |
| 2.4 Erori si limitari | Latenta API (<3 s observat), schema versionare, rate limit | §1, §10 |
| 2.5 Concluzii | Sistem suport-decizie, audit log, separare producer/consumer | §1, §11, §15 |
| 3.1-3.4 Lucrari grafice | Diagrame ASCII din §2, screenshots editor + replay player | §2, §7, §8 |
Contributii originale (claim pentru sustinere)
- Model de date scenariu WX cu schema versionata — separare clara intre geometria initiala, evolutie continua si overrides discrete (vezi §6).
- Decuplare producer/consumer cu doua mecanisme de auth — Basic Auth pentru editor, token fix pentru API, audit log comun.
- Atomic write pe disc ca alternativa la DB pentru proiect didactic — zero corruptie la crash, backup trivial.
- Tactical workflow capturat in 6 pasi de la pre-mission la AAR (vezi §11).
- Integrare end-to-end demonstrata intre platforma web + APK Android + MATLAB + ESP32 (vezi §12-§14).
Limitari recunoscute (declarat in memoriu, NU ascuns)
- Persistenta JSON pe disc: nu se scaleaza la > 1000 scenarii sau editari concurente.
- Rate limit naive (file-based): un atacator poate sterge fisierul de stare; pentru productie ar trebui Redis.
- Scenariile sunt 2D (lat,lon + top_ft/base_ft) — nu modelam profilul vertical complet (cape, jet stream).
- Modelul de evolutie e liniar; in realitate fenomenele au evolutie ne-liniara (development supercele).
- Lipseste autentificare 2FA pe editor (acceptabil pentru proiect didactic, NU pentru productie reala).
Bibliografie sugerata pentru citare
- NASA Bowles, R. (1990). F-factor formulation for low-altitude wind shear. NASA Ref. Pub.
- Bernstein, B. C. et al. (2005). Current Icing Potential (CIP). Weather and Forecasting, 20(3).
- ICAO Annex 3 (2018). Meteorological Service for International Air Navigation.
- STANAG 3596. Standardized Aeronautical Charts and Information. NATO.
- Honeywell Primus Epic system description (publicly available datasheet).
- AviationWeather.gov API documentation (NWS, ultima accesare 2026-04).
🛫 20. Glosar termeni
- AAR (After Action Review)
- Sedinta de debrief post-misiune in care echipa analizeaza decisii si invata din execuție.
- APK
- Pachet de instalare Android (Android Package). In contextul nostru = aplicatia C-27J Radar pe tableta.
- ARINC 661
- Standard ARINC pentru sistemele de display in cockpit comercial. Reglementeaza format CDS (Cockpit Display System).
- ATM
- Academia Tehnica Militara din Bucuresti (institutia de invatamant a autoarei).
- Basic Auth
- Mecanism HTTP de autentificare cu user+parola in header (RFC 7617). Folosit pentru zona producer.
- bcrypt
- Functie de hash pentru parole, rezistent la brute-force prin cost factor adjustabil. Folosit in
.ht_ioanad. - CB
- Cumulonimbus — nor de furtuna cu dezvoltare verticala mare; risc grindina, fulgere, turbulenta severa.
- CIP (Current Icing Potential)
- Algoritm Bernstein 2005 pentru evaluare risc giurgiu pe categorie 0..4.
- EDR (Eddy Dissipation Rate)
- Metrica turbulentei standardizata ICAO; m^(2/3)/s.
- F-factor
- Metrica NASA Bowles pentru wind shear pe glide slope;
F = (1/g)·(dUx/dt) − w/Va; threshold 0.105. - FRONT_C / FRONT_W
- Front rece / front cald in modelul de scenariu.
- HTTP Basic Auth
- vezi Basic Auth.
- ICAO
- International Civil Aviation Organization. Reglementeaza standarde aviatie civila.
- IOANAD-WX
- Codename platformei web (acest sistem). Compus din "Ioana D." + "WX". Alias: SPARTAN-MET.
- METAR
- Meteorological Aerodrome Report. Raport meteo standardizat la sol, format ICAO.
- NMEA
- Standard pentru formate sentence GPS (GPRMC, GPGGA etc.). Folosit pentru replay GPS pe ESP32.
- OWM
- OpenWeatherMap — sursa optionala de meteo real, alternativa la AviationWeather.gov.
- SIGMET
- Significant Meteorological Information. Avertizari meteo critice pentru aviatie.
- SPARTAN-MET
- vezi IOANAD-WX. Codename folosit in
index.phppentru landing. - TAF
- Terminal Aerodrome Forecast. Prognoza meteo pentru aerodrom.
- Token
- Sir hex de 32+ caractere folosit pentru autentificare API. Comparat cu
hash_equals()(constant-time). - WX
- Abreviere standard "Weather" in aviatie. Folosit ca prefix in tot codul (
wx_objects,wx_types). - XWC (Crosswind Component)
- Componenta vant transversala fata de pista.
XWC = |V·sin(wind_dir − rwy_hdg)|.
🎯 Note finale pentru autoare
- Acest document este standalone — il poti deschide local fara conexiune (cu exceptia fonturilor Google care cad gracios pe fallback).
- Pentru export PDF: Ctrl+P in browser → Save as PDF. CSS
@media printe configurat sa scoata camo, sa puna text negru pe alb, paginare curata. - Daca modifici JSON-ul scenariu, schimba si
schemabumpat la 3 si actualizeaza §4 in document. - Pentru memoriu, foloseste sectiunea §19 ca punct de plecare — extinde fiecare claim cu detalii din implementare.
- Diagramele ASCII sunt OK pentru document tehnic, dar pentru memoriu inlocuieste cu diagrame vector (draw.io export PNG sau Mermaid).
✅ Definition of Done
- [x] Landing page
/tema militara → DEPLOYED - [x] Editor
/editor/functional cu Basic Auth → DEPLOYED - [x] API endpoint cu token + rate limit + audit → DEPLOYED
- [x] Doua scenarii reale 2024-06-13 si 2022-08-17 → SEEDED
- [x] Manual tehnic complet (acest document) → DELIVERED
- [ ] Replay player polished pentru proiector → REFINEMENT
- [ ] Route Editor (flight plans) → WIP
- [ ] Scenario type "mission" (wx + flight + tasks) → PLANNED
- [ ] Integrare APK validata end-to-end pe tableta reala → TEST PENDING
- [ ] Integrare MATLAB validata cu 5 scenarii canonice → TEST PENDING