Skip to content

Commit 967a3a3

Browse files
committed
implemented reload conifg - only applies to service when restarted. Gives user a mechanism to load confiugrations without restarting testbed.
1 parent 98028d6 commit 967a3a3

6 files changed

Lines changed: 154 additions & 0 deletions

File tree

catkit2/bindings.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,7 @@ PYBIND11_MODULE(catkit_bindings, m)
684684
.def("interrupt_service", &TestbedProxy::InterruptService)
685685
.def("terminate_service", &TestbedProxy::TerminateService)
686686
.def("shut_down", &TestbedProxy::ShutDown)
687+
.def("reload_config", &TestbedProxy::ReloadConfig)
687688
.def_property_readonly("message_broker", &TestbedProxy::GetMessageBroker)
688689
.def_property_readonly("is_simulated", &TestbedProxy::IsSimulated)
689690
.def_property_readonly("is_alive", &TestbedProxy::IsAlive)

catkit2/testbed/testbed.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

catkit_core/TestbedProxy.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,26 @@ void TestbedProxy::ShutDown()
197197
}
198198
}
199199

200+
void TestbedProxy::ReloadConfig(const json &new_config)
201+
{
202+
catkit_proto::testbed::ReloadConfigRequest request;
203+
request.set_config(new_config.dump());
204+
205+
catkit_proto::testbed::ReloadConfigReply reply;
206+
207+
try
208+
{
209+
reply.ParseFromString(MakeRequest("reload_config", Serialize(request)));
210+
}
211+
catch (...)
212+
{
213+
throw std::runtime_error("Unable to reload config.");
214+
}
215+
216+
// Invalidate cached config so it gets refetched on next access
217+
m_HasGottenInfo = false;
218+
}
219+
200220
std::shared_ptr<DataStream> TestbedProxy::GetHeartbeat()
201221
{
202222
GetTestbedInfo();

catkit_core/TestbedProxy.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class TestbedProxy : public Client, public std::enable_shared_from_this<TestbedP
4949
bool IsAlive();
5050

5151
void ShutDown();
52+
void ReloadConfig(const nlohmann::json &new_config);
5253

5354
std::shared_ptr<DataStream> GetHeartbeat();
5455
std::shared_ptr<MessageBroker> GetMessageBroker();

proto/service.proto

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,57 @@ import "core.proto";
44

55
package catkit_proto.service;
66

7+
message GetInfoRequest
8+
{
9+
}
10+
11+
message GetInfoReply
12+
{
13+
string service_id = 1;
14+
string service_type = 2;
15+
string config = 3;
16+
17+
repeated string property_names = 4;
18+
repeated string command_names = 5;
19+
map<string, string> datastream_ids = 6;
20+
21+
map<string, string> property_datastream_links = 8;
22+
23+
string heartbeat_stream_id = 7;
24+
}
25+
26+
message GetPropertyRequest
27+
{
28+
string property_name = 1;
29+
}
30+
31+
message GetPropertyReply
32+
{
33+
Value property_value = 1;
34+
}
35+
36+
message SetPropertyRequest
37+
{
38+
string property_name = 1;
39+
Value property_value = 2;
40+
}
41+
42+
message SetPropertyReply
43+
{
44+
Value property_value = 1;
45+
}
46+
47+
message ExecuteCommandRequest
48+
{
49+
string command_name = 1;
50+
Dict arguments = 2;
51+
}
52+
53+
message ExecuteCommandReply
54+
{
55+
Value result = 1;
56+
}
57+
758
message ShutDownRequest
859
{
960
}

proto/testbed.proto

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,12 @@ message ShutDownRequest
120120
message ShutDownReply
121121
{
122122
}
123+
124+
message ReloadConfigRequest
125+
{
126+
string config = 1;
127+
}
128+
129+
message ReloadConfigReply
130+
{
131+
}

0 commit comments

Comments
 (0)