@@ -381,6 +381,7 @@ def __init__(self, port, is_simulated, config):
381381 self .server .register_request_handler ('get_service_info' , self .on_get_service_info )
382382 self .server .register_request_handler ('register_service' , self .on_register_service )
383383 self .server .register_request_handler ('shut_down' , self .on_shut_down )
384+ self .server .register_request_handler ('reload_config' , self .on_reload_config )
384385
385386 self .is_running = False
386387 self .shutdown_requested = threading .Event ()
@@ -668,6 +669,16 @@ def on_register_service(self, data):
668669
669670 return reply .SerializeToString ()
670671
672+ def on_reload_config (self , data ):
673+ request = testbed_proto .ReloadConfigRequest ()
674+ request .ParseFromString (data )
675+
676+ new_config = json .loads (request .config )
677+ self .reload_config (new_config )
678+
679+ reply = testbed_proto .ReloadConfigReply ()
680+ return reply .SerializeToString ()
681+
671682 def on_shut_down (self , data ):
672683 self .shutdown_requested .set ()
673684
@@ -686,6 +697,67 @@ def register_service_type(self, service_type, path):
686697 '''
687698 self .service_type_paths [service_type ] = path
688699
700+ def reload_config (self , new_config ):
701+ '''Reload the testbed configuration.
702+
703+ This updates the configuration without restarting the testbed.
704+ Services must be restarted to pick up their new configuration.
705+
706+ Parameters
707+ ----------
708+ new_config : dict
709+ The new configuration dictionary.
710+ '''
711+ self .log .info ('Reloading configuration...' )
712+
713+ # Update the config
714+ old_config = self .config
715+ self .config = new_config
716+
717+ # Update simulation mode if it changed
718+ if 'testbed' in new_config and 'simulated' in new_config ['testbed' ]:
719+ self .is_simulated = new_config ['testbed' ]['simulated' ]
720+
721+ # Check for added/removed services
722+ old_services = set (old_config .get ('services' , {}).keys ())
723+ new_services = set (new_config .get ('services' , {}).keys ())
724+
725+ added = new_services - old_services
726+ removed = old_services - new_services
727+
728+ if added :
729+ self .log .info (f'New services in config: { list (added )} ' )
730+ # Add new service references
731+ for service_id in added :
732+ service_info = new_config ['services' ][service_id ]
733+ service_type = service_info ['service_type' ]
734+
735+ if self .is_simulated and 'simulated_service_type' in service_info :
736+ service_type = service_info ['simulated_service_type' ]
737+
738+ dependencies = service_info .get ('depends_on' , [])
739+ self .services [service_id ] = ServiceReference (service_id , service_type , ServiceState .CLOSED , dependencies , self .message_broker )
740+
741+ if removed :
742+ self .log .warning (f'Services removed from config: { list (removed )} ' )
743+ # Check if any removed services are running
744+ for service_id in removed :
745+ if service_id in self .services and self .services [service_id ].is_alive :
746+ self .log .warning (f'Service "{ service_id } " is running but removed from config. Consider stopping it.' )
747+
748+ # Update startup services list
749+ self .startup_services = []
750+ if 'safety' in self .config .get ('testbed' , {}):
751+ self .startup_services .append (self .config ['testbed' ]['safety' ]['service_id' ])
752+
753+ if 'startup_services' in self .config .get ('testbed' , {}):
754+ self .startup_services .extend (self .config ['testbed' ]['startup_services' ])
755+
756+ if self .is_simulated and 'simulator' not in self .startup_services :
757+ self .startup_services .append ('simulator' )
758+
759+ self .log .info ('Configuration reloaded successfully.' )
760+
689761 def start_service (self , service_id ):
690762 '''Start a service.
691763
0 commit comments