Vai al contenuto

Flussi Architetturali Principali

Panoramica

Questa sezione descrive nel dettaglio l'implementazione dei tre flussi centrali dell'applicazione mobile HapGree:

  1. Calcolo e salvataggio delle emissioni di CO2;
  2. Pipeline di tracking automatico della mobilità;
  3. Integrazione con Health Connect e Apple Health per il contapassi.

L'architettura adotta un principio guida fondamentale: il client mobile ha la responsabilità di raccogliere, validare, bufferizzare e sincronizzare i dati grezzi, mentre il backend gestisce il calcolo canonico dei fattori di emissione e la storicizzazione delle metriche aggregate.


1. Flusso di calcolo e gestione della CO2

Principio architetturale

Il calcolo definitivo delle emissioni avviene lato backend. Il client mobile:

  • costruisce e invia DTO strutturati per ciascuna categoria (viaggi, consumi energetici, prodotti alimentari);
  • riceve dal backend gli aggregati storici e le metriche di breakdown pronte per il rendering;
  • esegue localmente solo conversioni accessorie di presentazione (es. calcolo percentuali e indicatori grafici).

Raccolta dati per Categoria

sequenceDiagram
    participant U as Utente / Sensori
    participant V as View & ViewModel
    participant A as HapGreeApiContainer
    participant B as Backend HapGree

    U->>V: Inserimento viaggio / bolletta / cibo
    V->>A: Costruzione DTO di creazione
    A->>B: Chiamata POST con payload
    B->>B: Calcolo emissioni e aggiornamento metriche
    V->>A: Richiesta Dashboard / Categoria
    A->>B: GET aggregati (giorno, mese, anno)
    B-->>A: DTO con stats e breakdown
    A-->>V: Notifica stato ViewModel
    V-->>U: Rendering dati e grafici

Mobilità

Il client compone il DTO CreateTravel includendo: - distanza totale percorsa; - tipologia di veicolo (vehicleType); - motivazione dello spostamento (travelReasonType); - velocità media e tempi (tempo totale, tempo in idle); - driving score; - coordinate geografiche di inizio/fine e segmenti della polyline del percorso.

I dati vengono inviati tramite ViaggiApi.createTrip.

Energia

L'utente registra consumi o bollette inviando: - amountConsumed (quantità consumata); - type (water, gas, electricity); - periodo di riferimento (date inizio e fine bolletta).

I dati vengono trasmessi tramite EnergyApi.

Cibo e Alimentazione

Supportiamo tre modalità di inserimento: 1. Manuale: selezione di prodotti e quantità; 2. Barcode: scansione del codice a barre tramite mobile_scanner con ricerca metadati su OpenFoodFacts; 3. Scontrino con OCR: scansione dello scontrino con Google ML Kit Text Recognition per estrarre gli articoli acquistati.

I singoli prodotti includono il fattore di emissione (co2KgForProductKg) e la classe ecologica (a-e).

Attività Fisica e Compensazioni

I passi e le distanze sincronizzate dai servizi salute contribuiscono al profilo delle attività dell'utente, mentre le compensazioni effettuate riducono l'impronta complessiva nei totali stats.saved.

Consumo dei dati aggregati nella UI

La HomePageViewModel consuma l'endpoint aggregato UserDashboard, che fornisce:

  • stats.emitted: emissioni suddivise per periodo (giorno, settimana, mese, anno);
  • stats.saved: CO2 compensata o risparmiata;
  • weekBreakdown: ripartizione giornaliera tra le categorie (vehicle, food, energy, receipt).

2. Pipeline del Tracking Automatico della Mobilità

Panoramica

Il tracciamento automatico dei viaggi è implementato come una pipeline continua in background composta da:

  • gestione permessi runtime;
  • foreground service permanente (flutter_background_service);
  • riconoscimento dell'attività e filtraggio GPS;
  • macchina a stati (TrackingStateMachine);
  • buffer locale su SharedPreferences (driving_history) per garantire resilienza offline;
  • sincronizzazione asincrona verso il backend.
sequenceDiagram
    participant S as Background Service
    participant G as Geolocator + Activity Recognition
    participant M as TrackingStateMachine
    participant P as SharedPreferences (driving_history)
    participant API as Backend (ViaggiApi)

    S->>G: Avvio stream GPS e rilevamento attività
    G-->>M: Posizioni e velocità filtrate
    M->>M: Valutazione transizione di stato
    alt Viaggio completato
        M->>P: Bufferizzazione sessione in driving_history
        S->>P: Lettura sessioni pendenti
        S->>API: POST createTrip(CreateTravel)
        API-->>S: Risposta 200 OK
        S->>P: Rimozione sessione sincronizzata dal buffer
    end

Dettaglio delle Fasi

1. Inizializzazione del servizio

Al login dell'utente, main.dart invoca InitializeAutomaticTrackingBackgroudProcess(context). AutomaticTrackingViewModel richiede i permessi necessari: - localizzazione foreground (locationWhenInUse) e background (locationAlways); - notifiche (notification); - riconoscimento dell'attività (activityRecognition).

Quindi configura FlutterBackgroundService impostando autoStart e autoStartOnBoot.

2. Entrypoint di Background

Il servizio esegue la funzione onStart(ServiceInstance service): - crea la notifica persistente in foreground richiesta da Android; - avvia lo stream di coordinate tramite Geolocator.

3. Macchina a Stati e Filtraggio GPS

Il flusso degli eventi è gestito da TrackingStateMachine e GpsFilterUtils:

  • Stati gestiti: idle, detecting, tracking, saving, error.
  • Soglia di avvio movimento: velocità minima impostata a 20.0 km/h per distinguere gli spostamenti veicolari dai passi a piedi.
  • Filtri di pulizia del segnale:
  • _maxJitterDistance = 100.0 m (scarto di coordinate anomale o balzi di rete);
  • _maxAcceleration = 8.0 m/s² (scarto di accelerazioni irrealistiche);
  • _minMovementSpeed = 1.5 m/s.

4. Bufferizzazione e Invio a Backend

Al termine della sessione di guida: 1. La sessione completa (coordinate, tempi, velocità media, driving score) viene salvata in SharedPreferences sotto la chiave driving_history. 2. Il metodo _loadAllSavedSessionsToDatabase() cicla le sessioni salvate ed esegue il flush via viaggiApi.createTrip. 3. Le sessioni confermate con successo dal backend vengono rimosse dal buffer, garantendo che nessun viaggio vada perso in caso di disconnessione o riavvio improvviso del dispositivo.


3. Integrazione con Health Connect e Apple Health

Obiettivo del flusso

Rilevare i passi e la distanza percorsa quotidianamente tramite le API native del sistema operativo e sincronizzarli con il backend HapGree.

Libreria e Tipi di Dato

Utilizziamo il plugin health ^12.0.1:

  • Android (Health Connect):
  • HealthDataType.STEPS
  • HealthDataType.DISTANCE_DELTA
  • iOS (Apple Health / HealthKit):
  • HealthDataType.STEPS
  • HealthDataType.DISTANCE_WALKING_RUNNING

Logica di Aggregazione e Deduplicazione

La logica centrale è implementata in lib/utils/TodayHealthValues.dart tramite la funzione fetchHealthData():

  1. Inizializza Health e richiede i permessi di lettura per le metriche supportate.
  2. Recupera i datapoint registrati dalla mezzanotte del giorno corrente fino a DateTime.now().
  3. Algoritmo di deduplicazione passi (_calculateStepsFromSources):
  4. Raggruppa i dati per sorgente (sourceName);
  5. Calcola i passi per singola fonte (valutando valori aggregati vs parziali);
  6. Prende il valore massimo tra le diverse fonti per evitare doppie registrazioni da app/tracker multipli.
  7. Calcolo della distanza (_calculateTotalDistanceKm):
  8. Raggruppa e somma i chilometri rilevati per la giornata.
  9. Restituisce l'oggetto DTO UpdatePhysicalActivity.
sequenceDiagram
    participant APP as Flutter App
    participant H as Health Connect / Apple Health
    participant API as Backend HapGree

    APP->>H: Richiesta autorizzazioni di lettura
    H-->>APP: Permessi concessi
    APP->>H: Query datapoint da mezzanotte a ora
    H-->>APP: HealthDataPoint[]
    APP->>APP: Deduplica e aggregazione (passi + km)
    APP->>API: POST /physicalActivity (UpdatePhysicalActivity)
    API-->>APP: Conferma sincronizzazione

Punti di integrazione nell'app

  • Home Screen (HomePageViewModel): recupera i dati della giornata per mostrare il widget riepilogativo di passi e chilometri percorsi.
  • Schermata Connessione App (ConnectableAppViewModel): consente all'utente di connettere/disconnettere l'integrazione e monitorare lo stato di sincronizzazione.
  • Sync al Login (main.dart): invia automaticamente al backend un aggiornamento dell'attività fisica all'apertura dell'app.