nost-tools 2.0.0__py3-none-any.whl → 2.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of nost-tools might be problematic. Click here for more details.

@@ -1,261 +1,261 @@
1
- """
2
- Provides a base application that manages communication between a simulator and broker.
3
- """
4
-
5
- import logging
6
- import threading
7
- import traceback
8
- from datetime import datetime, timedelta
9
-
10
- from .application import Application
11
- from .application_utils import ConnectionConfig
12
- from .schemas import InitCommand, StartCommand, StopCommand, UpdateCommand
13
-
14
- logger = logging.getLogger(__name__)
15
-
16
-
17
- class ManagedApplication(Application):
18
- """
19
- Managed NOS-T Application.
20
-
21
- This object class defines the basic functionality for a NOS-T application
22
- that utilizes an external Manager to command simulator execution.
23
-
24
- Attributes:
25
- prefix (str): execution namespace (prefix)
26
- simulator (:obj:`Simulator`): simulator
27
- client (:obj:`Client`): MQTT client
28
- app_name (str): application name
29
- app_description (str): application description
30
- time_status_step (:obj:`timedelta`): scenario duration between time status messages
31
- time_status_init (:obj:`datetime`): scenario time of first time status message
32
- time_step (:obj:`timedelta`): scenario time step used in execution
33
- """
34
-
35
- def __init__(self, app_name: str, app_description: str = None):
36
- """
37
- Initializes a new managed application.
38
-
39
- Args:
40
- app_name (str): application name
41
- app_description (str): application description
42
- """
43
- super().__init__(app_name, app_description)
44
- self.time_step = None
45
- self._sim_start_time = None
46
- self._sim_stop_time = None
47
-
48
- def start_up(
49
- self,
50
- prefix: str,
51
- config: ConnectionConfig,
52
- set_offset: bool = None,
53
- time_status_step: timedelta = None,
54
- time_status_init: datetime = None,
55
- shut_down_when_terminated: bool = None,
56
- time_step: timedelta = None,
57
- manager_app_name: str = None,
58
- ) -> None:
59
- """
60
- Starts up the application by connecting to message broker, starting a background event loop,
61
- subscribing to manager events, and registering callback functions.
62
-
63
- Args:
64
- prefix (str): execution namespace (prefix)
65
- config (:obj:`ConnectionConfig`): connection configuration
66
- set_offset (bool): True, if the system clock offset shall be set using a NTP request prior to execution
67
- time_status_step (:obj:`timedelta`): scenario duration between time status messages
68
- time_status_init (:obj:`datetime`): scenario time for first time status message
69
- shut_down_when_terminated (bool): True, if the application should shut down when the simulation is terminated
70
- time_step (:obj:`timedelta`): scenario time step used in execution (Default: 1 second)
71
- manager_app_name (str): manager application name (Default: manager)
72
- """
73
- if (
74
- set_offset is not None
75
- and time_status_step is not None
76
- and time_status_init is not None
77
- and shut_down_when_terminated is not None
78
- and time_step is not None
79
- and manager_app_name is not None
80
- ):
81
- self.set_offset = set_offset
82
- self.time_status_step = time_status_step
83
- self.time_status_init = time_status_init
84
- self.shut_down_when_terminated = shut_down_when_terminated
85
- self.time_step = time_step
86
- self.manager_app_name = manager_app_name
87
- else:
88
- self.config = config
89
- parameters = getattr(
90
- self.config.rc.simulation_configuration.execution_parameters,
91
- "managed_application",
92
- None,
93
- )
94
- self.set_offset = parameters.set_offset
95
- self.time_status_step = parameters.time_status_step
96
- self.time_status_init = parameters.time_status_init
97
- self.shut_down_when_terminated = parameters.shut_down_when_terminated
98
- self.time_step = parameters.time_step
99
- self.manager_app_name = parameters.manager_app_name
100
-
101
- # start up base application
102
- super().start_up(
103
- prefix,
104
- config,
105
- self.set_offset,
106
- self.time_status_step,
107
- self.time_status_init,
108
- self.shut_down_when_terminated,
109
- )
110
- self.time_step = self.time_step
111
- self.manager_app_name = self.manager_app_name
112
-
113
- # Register callback functions
114
- self.add_message_callback(
115
- app_name=self.manager_app_name,
116
- app_topic="init",
117
- user_callback=self.on_manager_init,
118
- )
119
- self.add_message_callback(
120
- app_name=self.manager_app_name,
121
- app_topic="start",
122
- user_callback=self.on_manager_start,
123
- )
124
- self.add_message_callback(
125
- app_name=self.manager_app_name,
126
- app_topic="stop",
127
- user_callback=self.on_manager_stop,
128
- )
129
- self.add_message_callback(
130
- app_name=self.manager_app_name,
131
- app_topic="update",
132
- user_callback=self.on_manager_update,
133
- )
134
-
135
- def shut_down(self) -> None:
136
- """
137
- Shuts down the application by stopping the background event loop and disconnecting
138
- the application from the broker.
139
- """
140
- # shut down base application
141
- super().shut_down()
142
-
143
- def on_manager_init(self, ch, method, properties, body) -> None:
144
- """
145
- Callback function for the managed application to respond to an initilize command sent from the manager.
146
- Parses the scenario start/end times and signals ready.
147
-
148
- Args:
149
- ch (:obj:`pika.channel.Channel`): The channel object used to communicate with the RabbitMQ server.
150
- method (:obj:`pika.spec.Basic.Deliver`): Delivery-related information such as delivery tag, exchange, and routing key.
151
- properties (:obj:`pika.BasicProperties`): Message properties including content type, headers, and more.
152
- body (bytes): The actual message body sent, containing the message payload.
153
- """
154
- try:
155
- # Parse message payload
156
- message = body.decode("utf-8")
157
- params = InitCommand.model_validate_json(message).tasking_parameters
158
- # update default execution start/end time
159
- self._sim_start_time = params.sim_start_time
160
- self._sim_stop_time = params.sim_stop_time
161
- self.ready()
162
-
163
- except Exception as e:
164
- logger.error(
165
- f"Exception (topic: {method.routing_key}, payload: {message}): {e}"
166
- )
167
- print(traceback.format_exc())
168
-
169
- def on_manager_start(self, ch, method, properties, body) -> None:
170
- """
171
- Callback function for the managed application to respond to a start command sent from the manager.
172
- Parses the scenario start/end time, wallclock epoch, and time scale factor and executes
173
- the simulator in a background thread.
174
-
175
- Args:
176
- ch (:obj:`pika.channel.Channel`): The channel object used to communicate with the RabbitMQ server.
177
- method (:obj:`pika.spec.Basic.Deliver`): Delivery-related information such as delivery tag, exchange, and routing key.
178
- properties (:obj:`pika.BasicProperties`): Message properties including content type, headers, and more.
179
- body (bytes): The actual message body sent, containing the message payload.
180
- """
181
- # Parse message payload
182
- message = body.decode("utf-8")
183
- params = StartCommand.model_validate_json(message).tasking_parameters
184
- logger.info(f"Received start command {params}")
185
- try:
186
-
187
- # check for optional start time
188
- if params.sim_start_time is not None:
189
- self._sim_start_time = params.sim_start_time
190
- logger.info(f"Sim start time: {params.sim_start_time}")
191
- # check for optional end time
192
- if params.sim_stop_time is not None:
193
- self._sim_stop_time = params.sim_stop_time
194
- logger.info(f"Sim stop time: {params.sim_stop_time}")
195
-
196
- threading.Thread(
197
- target=self.simulator.execute,
198
- kwargs={
199
- "init_time": self._sim_start_time,
200
- "duration": self._sim_stop_time - self._sim_start_time,
201
- "time_step": self.time_step,
202
- "wallclock_epoch": params.start_time,
203
- "time_scale_factor": params.time_scaling_factor,
204
- },
205
- ).start()
206
-
207
- except Exception as e:
208
- logger.error(
209
- f"Exception (topic: {method.routing_key}, payload: {message}): {e}"
210
- )
211
- print(traceback.format_exc())
212
-
213
- def on_manager_stop(self, ch, method, properties, body) -> None:
214
- """
215
- Callback function for the managed application ('self') to respond to a stop command sent from the manager.
216
- Parses the end time and updates the simulator.
217
-
218
- Args:
219
- ch (:obj:`pika.channel.Channel`): The channel object used to communicate with the RabbitMQ server.
220
- method (:obj:`pika.spec.Basic.Deliver`): Delivery-related information such as delivery tag, exchange, and routing key.
221
- properties (:obj:`pika.BasicProperties`): Message properties including content type, headers, and more.
222
- body (bytes): The actual message body sent, containing the message payload.
223
- """
224
- try:
225
- # Parse message payload
226
- message = body.decode("utf-8")
227
- params = StopCommand.model_validate_json(message).tasking_parameters
228
- logger.info(f"Received stop command {message}")
229
- # update execution end time
230
- self.simulator.set_end_time(params.sim_stop_time)
231
- except Exception as e:
232
- logger.error(
233
- f"Exception (topic: {method.routing_key}, payload: {message}): {e}"
234
- )
235
- print(traceback.format_exc())
236
-
237
- def on_manager_update(self, ch, method, properties, body) -> None:
238
- """
239
- Callback function for the managed application ('self') to respond to an update command sent from the manager.
240
- Parses the time scaling factor and scenario update time and updates the simulator.
241
-
242
- Args:
243
- ch (:obj:`pika.channel.Channel`): The channel object used to communicate with the RabbitMQ server.
244
- method (:obj:`pika.spec.Basic.Deliver`): Delivery-related information such as delivery tag, exchange, and routing key.
245
- properties (:obj:`pika.BasicProperties`): Message properties including content type, headers, and more.
246
- body (bytes): The actual message body sent, containing the message payload.
247
- """
248
- try:
249
- # Parse message payload
250
- message = body.decode("utf-8")
251
- params = UpdateCommand.model_validate_json(message).tasking_parameters
252
- logger.info(f"Received update command {message}")
253
- # update execution time scale factor
254
- self.simulator.set_time_scale_factor(
255
- params.time_scaling_factor, params.sim_update_time
256
- )
257
- except Exception as e:
258
- logger.error(
259
- f"Exception (topic: {method.routing_key}, payload: {message}): {e}"
260
- )
261
- print(traceback.format_exc())
1
+ """
2
+ Provides a base application that manages communication between a simulator and broker.
3
+ """
4
+
5
+ import logging
6
+ import threading
7
+ import traceback
8
+ from datetime import datetime, timedelta
9
+
10
+ from .application import Application
11
+ from .application_utils import ConnectionConfig
12
+ from .schemas import InitCommand, StartCommand, StopCommand, UpdateCommand
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class ManagedApplication(Application):
18
+ """
19
+ Managed NOS-T Application.
20
+
21
+ This object class defines the basic functionality for a NOS-T application
22
+ that utilizes an external Manager to command simulator execution.
23
+
24
+ Attributes:
25
+ prefix (str): execution namespace (prefix)
26
+ simulator (:obj:`Simulator`): simulator
27
+ client (:obj:`Client`): MQTT client
28
+ app_name (str): application name
29
+ app_description (str): application description
30
+ time_status_step (:obj:`timedelta`): scenario duration between time status messages
31
+ time_status_init (:obj:`datetime`): scenario time of first time status message
32
+ time_step (:obj:`timedelta`): scenario time step used in execution
33
+ """
34
+
35
+ def __init__(self, app_name: str, app_description: str = None):
36
+ """
37
+ Initializes a new managed application.
38
+
39
+ Args:
40
+ app_name (str): application name
41
+ app_description (str): application description
42
+ """
43
+ super().__init__(app_name, app_description)
44
+ self.time_step = None
45
+ self._sim_start_time = None
46
+ self._sim_stop_time = None
47
+
48
+ def start_up(
49
+ self,
50
+ prefix: str,
51
+ config: ConnectionConfig,
52
+ set_offset: bool = None,
53
+ time_status_step: timedelta = None,
54
+ time_status_init: datetime = None,
55
+ shut_down_when_terminated: bool = None,
56
+ time_step: timedelta = None,
57
+ manager_app_name: str = None,
58
+ ) -> None:
59
+ """
60
+ Starts up the application by connecting to message broker, starting a background event loop,
61
+ subscribing to manager events, and registering callback functions.
62
+
63
+ Args:
64
+ prefix (str): execution namespace (prefix)
65
+ config (:obj:`ConnectionConfig`): connection configuration
66
+ set_offset (bool): True, if the system clock offset shall be set using a NTP request prior to execution
67
+ time_status_step (:obj:`timedelta`): scenario duration between time status messages
68
+ time_status_init (:obj:`datetime`): scenario time for first time status message
69
+ shut_down_when_terminated (bool): True, if the application should shut down when the simulation is terminated
70
+ time_step (:obj:`timedelta`): scenario time step used in execution (Default: 1 second)
71
+ manager_app_name (str): manager application name (Default: manager)
72
+ """
73
+ if (
74
+ set_offset is not None
75
+ and time_status_step is not None
76
+ and time_status_init is not None
77
+ and shut_down_when_terminated is not None
78
+ and time_step is not None
79
+ and manager_app_name is not None
80
+ ):
81
+ self.set_offset = set_offset
82
+ self.time_status_step = time_status_step
83
+ self.time_status_init = time_status_init
84
+ self.shut_down_when_terminated = shut_down_when_terminated
85
+ self.time_step = time_step
86
+ self.manager_app_name = manager_app_name
87
+ else:
88
+ self.config = config
89
+ parameters = getattr(
90
+ self.config.rc.simulation_configuration.execution_parameters,
91
+ "managed_application",
92
+ None,
93
+ )
94
+ self.set_offset = parameters.set_offset
95
+ self.time_status_step = parameters.time_status_step
96
+ self.time_status_init = parameters.time_status_init
97
+ self.shut_down_when_terminated = parameters.shut_down_when_terminated
98
+ self.time_step = parameters.time_step
99
+ self.manager_app_name = parameters.manager_app_name
100
+
101
+ # start up base application
102
+ super().start_up(
103
+ prefix,
104
+ config,
105
+ self.set_offset,
106
+ self.time_status_step,
107
+ self.time_status_init,
108
+ self.shut_down_when_terminated,
109
+ )
110
+ self.time_step = self.time_step
111
+ self.manager_app_name = self.manager_app_name
112
+
113
+ # Register callback functions
114
+ self.add_message_callback(
115
+ app_name=self.manager_app_name,
116
+ app_topic="init",
117
+ user_callback=self.on_manager_init,
118
+ )
119
+ self.add_message_callback(
120
+ app_name=self.manager_app_name,
121
+ app_topic="start",
122
+ user_callback=self.on_manager_start,
123
+ )
124
+ self.add_message_callback(
125
+ app_name=self.manager_app_name,
126
+ app_topic="stop",
127
+ user_callback=self.on_manager_stop,
128
+ )
129
+ self.add_message_callback(
130
+ app_name=self.manager_app_name,
131
+ app_topic="update",
132
+ user_callback=self.on_manager_update,
133
+ )
134
+
135
+ def shut_down(self) -> None:
136
+ """
137
+ Shuts down the application by stopping the background event loop and disconnecting
138
+ the application from the broker.
139
+ """
140
+ # shut down base application
141
+ super().shut_down()
142
+
143
+ def on_manager_init(self, ch, method, properties, body) -> None:
144
+ """
145
+ Callback function for the managed application to respond to an initilize command sent from the manager.
146
+ Parses the scenario start/end times and signals ready.
147
+
148
+ Args:
149
+ ch (:obj:`pika.channel.Channel`): The channel object used to communicate with the RabbitMQ server.
150
+ method (:obj:`pika.spec.Basic.Deliver`): Delivery-related information such as delivery tag, exchange, and routing key.
151
+ properties (:obj:`pika.BasicProperties`): Message properties including content type, headers, and more.
152
+ body (bytes): The actual message body sent, containing the message payload.
153
+ """
154
+ try:
155
+ # Parse message payload
156
+ message = body.decode("utf-8")
157
+ params = InitCommand.model_validate_json(message).tasking_parameters
158
+ # update default execution start/end time
159
+ self._sim_start_time = params.sim_start_time
160
+ self._sim_stop_time = params.sim_stop_time
161
+ self.ready()
162
+
163
+ except Exception as e:
164
+ logger.error(
165
+ f"Exception (topic: {method.routing_key}, payload: {message}): {e}"
166
+ )
167
+ print(traceback.format_exc())
168
+
169
+ def on_manager_start(self, ch, method, properties, body) -> None:
170
+ """
171
+ Callback function for the managed application to respond to a start command sent from the manager.
172
+ Parses the scenario start/end time, wallclock epoch, and time scale factor and executes
173
+ the simulator in a background thread.
174
+
175
+ Args:
176
+ ch (:obj:`pika.channel.Channel`): The channel object used to communicate with the RabbitMQ server.
177
+ method (:obj:`pika.spec.Basic.Deliver`): Delivery-related information such as delivery tag, exchange, and routing key.
178
+ properties (:obj:`pika.BasicProperties`): Message properties including content type, headers, and more.
179
+ body (bytes): The actual message body sent, containing the message payload.
180
+ """
181
+ # Parse message payload
182
+ message = body.decode("utf-8")
183
+ params = StartCommand.model_validate_json(message).tasking_parameters
184
+ logger.info(f"Received start command {params}")
185
+ try:
186
+
187
+ # check for optional start time
188
+ if params.sim_start_time is not None:
189
+ self._sim_start_time = params.sim_start_time
190
+ logger.info(f"Sim start time: {params.sim_start_time}")
191
+ # check for optional end time
192
+ if params.sim_stop_time is not None:
193
+ self._sim_stop_time = params.sim_stop_time
194
+ logger.info(f"Sim stop time: {params.sim_stop_time}")
195
+
196
+ threading.Thread(
197
+ target=self.simulator.execute,
198
+ kwargs={
199
+ "init_time": self._sim_start_time,
200
+ "duration": self._sim_stop_time - self._sim_start_time,
201
+ "time_step": self.time_step,
202
+ "wallclock_epoch": params.start_time,
203
+ "time_scale_factor": params.time_scaling_factor,
204
+ },
205
+ ).start()
206
+
207
+ except Exception as e:
208
+ logger.error(
209
+ f"Exception (topic: {method.routing_key}, payload: {message}): {e}"
210
+ )
211
+ print(traceback.format_exc())
212
+
213
+ def on_manager_stop(self, ch, method, properties, body) -> None:
214
+ """
215
+ Callback function for the managed application ('self') to respond to a stop command sent from the manager.
216
+ Parses the end time and updates the simulator.
217
+
218
+ Args:
219
+ ch (:obj:`pika.channel.Channel`): The channel object used to communicate with the RabbitMQ server.
220
+ method (:obj:`pika.spec.Basic.Deliver`): Delivery-related information such as delivery tag, exchange, and routing key.
221
+ properties (:obj:`pika.BasicProperties`): Message properties including content type, headers, and more.
222
+ body (bytes): The actual message body sent, containing the message payload.
223
+ """
224
+ try:
225
+ # Parse message payload
226
+ message = body.decode("utf-8")
227
+ params = StopCommand.model_validate_json(message).tasking_parameters
228
+ logger.info(f"Received stop command {message}")
229
+ # update execution end time
230
+ self.simulator.set_end_time(params.sim_stop_time)
231
+ except Exception as e:
232
+ logger.error(
233
+ f"Exception (topic: {method.routing_key}, payload: {message}): {e}"
234
+ )
235
+ print(traceback.format_exc())
236
+
237
+ def on_manager_update(self, ch, method, properties, body) -> None:
238
+ """
239
+ Callback function for the managed application ('self') to respond to an update command sent from the manager.
240
+ Parses the time scaling factor and scenario update time and updates the simulator.
241
+
242
+ Args:
243
+ ch (:obj:`pika.channel.Channel`): The channel object used to communicate with the RabbitMQ server.
244
+ method (:obj:`pika.spec.Basic.Deliver`): Delivery-related information such as delivery tag, exchange, and routing key.
245
+ properties (:obj:`pika.BasicProperties`): Message properties including content type, headers, and more.
246
+ body (bytes): The actual message body sent, containing the message payload.
247
+ """
248
+ try:
249
+ # Parse message payload
250
+ message = body.decode("utf-8")
251
+ params = UpdateCommand.model_validate_json(message).tasking_parameters
252
+ logger.info(f"Received update command {message}")
253
+ # update execution time scale factor
254
+ self.simulator.set_time_scale_factor(
255
+ params.time_scaling_factor, params.sim_update_time
256
+ )
257
+ except Exception as e:
258
+ logger.error(
259
+ f"Exception (topic: {method.routing_key}, payload: {message}): {e}"
260
+ )
261
+ print(traceback.format_exc())