@@ -8,7 +8,7 @@ use rocket::{get, post, delete, routes, State, response::Redirect, catch, catche
88use rocket:: serde:: json:: Json ;
99use rocket:: fs:: { NamedFile , FileServer , relative} ;
1010use models:: { Device as ModelDevice , SensorType } ;
11- use log:: { info, error, debug } ;
11+ use log:: { info, error} ;
1212use sensors:: { monitor_ping, monitor_http} ;
1313use std:: fs:: { self , OpenOptions } ;
1414use std:: io:: { Read , Write } ;
@@ -60,17 +60,34 @@ async fn process_logs(start_date_parsed: Option<NaiveDate>, end_date_parsed: Opt
6060// Define a struct to track device status.
6161#[ derive( Debug , Clone ) ]
6262struct DeviceStatus {
63- failed_attempts : u32 ,
64- last_status : bool ,
63+ ping_status : Option < bool > ,
64+ http_status : Option < bool > ,
65+ bandwidth_usage : Option < f64 > ,
66+ last_update : DateTime < Local > ,
67+ changed_at : DateTime < Local > ,
6568}
6669
6770impl DeviceStatus {
6871 fn new ( ) -> Self {
69- DeviceStatus {
70- failed_attempts : 0 ,
71- last_status : true , // Assume initially up
72+ let now = Local :: now ( ) ;
73+ Self {
74+ ping_status : None ,
75+ http_status : None ,
76+ bandwidth_usage : None ,
77+ last_update : now,
78+ changed_at : now,
7279 }
7380 }
81+
82+ fn update_ping ( & mut self , new_status : bool ) -> bool {
83+ let changed = self . ping_status != Some ( new_status) ;
84+ if changed {
85+ self . ping_status = Some ( new_status) ;
86+ self . changed_at = Local :: now ( ) ;
87+ }
88+ self . last_update = Local :: now ( ) ;
89+ changed
90+ }
7491}
7592
7693// Use the new DeviceStatus struct in the type alias.
@@ -567,82 +584,107 @@ async fn main() {
567584 let status_map_clone = device_status_map. clone ( ) ;
568585
569586 tokio:: spawn ( async move {
587+ let mut device_statuses: HashMap < String , DeviceStatus > = HashMap :: new ( ) ;
588+
570589 loop {
571590 let devices_to_monitor: Vec < ModelDevice > = {
572591 let locked = devices_clone. lock ( ) . await ;
573592 locked. clone ( )
574593 } ;
575594
595+ let mut status_changed = false ;
596+
576597 for dev in devices_to_monitor {
577- let mut updated_dev = dev. clone ( ) ;
598+ let status = device_statuses. entry ( dev. ip . clone ( ) )
599+ . or_insert_with ( DeviceStatus :: new) ;
578600
579- // Always perform ping check regardless of sensor configuration
580- match monitor_ping ( & updated_dev. ip ) . await {
581- true => {
582- debug ! ( "Ping successful for {} ({})" , updated_dev. name, updated_dev. ip) ;
583- updated_dev. ping_status = Some ( true ) ;
584- } ,
585- false => {
586- error ! ( "Ping failed for {} ({})" , updated_dev. name, updated_dev. ip) ;
587- updated_dev. ping_status = Some ( false ) ;
588- }
589- }
590-
591- // HTTP and bandwidth checks only if configured
592- if updated_dev. sensors . contains ( & SensorType :: Http ) || updated_dev. sensors . contains ( & SensorType :: Https ) {
593- if let Some ( ref url) = updated_dev. http_path {
594- updated_dev. http_status = Some ( monitor_http ( url) . await ) ;
595- updated_dev. bandwidth_usage = Some ( rand:: thread_rng ( ) . gen_range ( 10.0 ..1000.0 ) ) ;
596- }
601+ // First check ping
602+ let ping_result = monitor_ping ( & dev. ip ) . await ;
603+ if status. update_ping ( ping_result) {
604+ status_changed = true ;
597605 }
598606
599607 // Update device in shared state
600608 let mut devices_locked = devices_clone. lock ( ) . await ;
601- if let Some ( existing_dev) = devices_locked. iter_mut ( ) . find ( |d| d. ip == updated_dev. ip ) {
602- existing_dev. ping_status = updated_dev. ping_status ;
603- existing_dev. http_status = updated_dev. http_status ;
604- existing_dev. bandwidth_usage = updated_dev. bandwidth_usage ;
605- }
606- }
607-
608- // Write to log file
609- if let Ok ( mut file) = OpenOptions :: new ( ) . append ( true ) . create ( true ) . open ( LOG_FILE ) {
610- let now: DateTime < Local > = Local :: now ( ) ;
611- let timestamp = now. format ( "%Y-%m-%d %H:%M:%S" ) . to_string ( ) ;
612- let today = now. format ( "%Y-%m-%d" ) . to_string ( ) ;
613-
614- // Write header if needed
615- if last_log_date != today {
616- if let Err ( e) = writeln ! ( file, "// {}" , today) {
617- error ! ( "Failed to write log header: {}" , e) ;
609+ if let Some ( device) = devices_locked. iter_mut ( ) . find ( |d| d. ip == dev. ip ) {
610+ device. ping_status = status. ping_status ;
611+
612+ // Check HTTP and bandwidth if configured and ping is successful
613+ if device. ping_status == Some ( true ) {
614+ if device. sensors . contains ( & SensorType :: Http ) ||
615+ device. sensors . contains ( & SensorType :: Https ) {
616+ if let Some ( ref url) = device. http_path {
617+ match monitor_http ( url) . await {
618+ true => {
619+ device. http_status = Some ( true ) ;
620+ // Simulate bandwidth measurement only for successful HTTP connections
621+ device. bandwidth_usage = Some ( rand:: thread_rng ( ) . gen_range ( 10.0 ..1000.0 ) ) ;
622+ status_changed = true ;
623+ } ,
624+ false => {
625+ device. http_status = Some ( false ) ;
626+ device. bandwidth_usage = None ;
627+ status_changed = true ;
628+ }
629+ }
630+ }
631+ }
632+ } else {
633+ // If ping fails, mark HTTP as down and clear bandwidth
634+ if device. sensors . contains ( & SensorType :: Http ) ||
635+ device. sensors . contains ( & SensorType :: Https ) {
636+ device. http_status = Some ( false ) ;
637+ device. bandwidth_usage = None ;
638+ status_changed = true ;
639+ }
618640 }
619- last_log_date = today;
620641 }
642+ }
621643
622- // Log each device status
623- let devices_locked = devices_clone. lock ( ) . await ;
624- for dev in devices_locked. iter ( ) {
625- let log_entry = format ! (
626- "{} - {} ({}): Ping: {}, HTTP: {}, Bandwidth: {}\n " ,
627- timestamp,
628- dev. name,
629- dev. ip,
630- dev. ping_status. map_or( "N/A" , |s| if s { "OK" } else { "FAIL" } ) ,
631- dev. http_status. map_or( "N/A" , |s| if s { "OK" } else { "FAIL" } ) ,
632- dev. bandwidth_usage. map_or( "N/A" . to_string( ) , |b| format!( "{:.2} Mbps" , b) )
633- ) ;
634-
635- if let Err ( e) = file. write_all ( log_entry. as_bytes ( ) ) {
636- error ! ( "Failed to write log entry: {}" , e) ;
644+ // Write to log file when status changes
645+ if status_changed {
646+ if let Ok ( mut file) = OpenOptions :: new ( ) . append ( true ) . create ( true ) . open ( LOG_FILE ) {
647+ let now = Local :: now ( ) ;
648+ let devices_locked = devices_clone. lock ( ) . await ;
649+
650+ for dev in devices_locked. iter ( ) {
651+ let status = device_statuses. get ( & dev. ip )
652+ . cloned ( )
653+ . unwrap_or_else ( DeviceStatus :: new) ;
654+
655+ // Format HTTP status and bandwidth based on sensor configuration
656+ let http_status = if dev. sensors . contains ( & SensorType :: Http ) ||
657+ dev. sensors . contains ( & SensorType :: Https ) {
658+ dev. http_status . map_or ( "FAIL" , |s| if s { "OK" } else { "FAIL" } )
659+ } else {
660+ "N/A"
661+ } ;
662+
663+ let bandwidth = if ( dev. sensors . contains ( & SensorType :: Http ) ||
664+ dev. sensors . contains ( & SensorType :: Https ) ) &&
665+ dev. http_status == Some ( true ) {
666+ dev. bandwidth_usage . map_or ( "N/A" . to_string ( ) , |b| format ! ( "{:.2} Mbps" , b) )
667+ } else {
668+ "N/A" . to_string ( )
669+ } ;
670+
671+ let log_entry = format ! (
672+ "{} - {} ({}): Ping: {}, HTTP: {}, Bandwidth: {}\n " ,
673+ now. format( "%Y-%m-%d %H:%M:%S" ) ,
674+ dev. name,
675+ dev. ip,
676+ status. ping_status. map_or( "N/A" , |s| if s { "OK" } else { "FAIL" } ) ,
677+ http_status,
678+ bandwidth
679+ ) ;
680+
681+ if let Err ( e) = file. write_all ( log_entry. as_bytes ( ) ) {
682+ error ! ( "Failed to write log entry: {}" , e) ;
683+ }
637684 }
638685 }
639-
640- if let Err ( e) = file. flush ( ) {
641- error ! ( "Failed to flush log file: {}" , e) ;
642- }
643686 }
644687
645- // Wait before next monitoring cycle
646688 sleep ( Duration :: from_secs ( 5 ) ) . await ;
647689 }
648690 } ) ;
0 commit comments