nost-tools 2.0.0__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.
- nost_tools/__init__.py +29 -0
- nost_tools/application.py +793 -0
- nost_tools/application_utils.py +262 -0
- nost_tools/configuration.py +304 -0
- nost_tools/entity.py +73 -0
- nost_tools/errors.py +14 -0
- nost_tools/logger_application.py +192 -0
- nost_tools/managed_application.py +261 -0
- nost_tools/manager.py +472 -0
- nost_tools/observer.py +181 -0
- nost_tools/publisher.py +141 -0
- nost_tools/schemas.py +426 -0
- nost_tools/simulator.py +531 -0
- nost_tools-2.0.0.dist-info/METADATA +119 -0
- nost_tools-2.0.0.dist-info/RECORD +18 -0
- nost_tools-2.0.0.dist-info/WHEEL +5 -0
- nost_tools-2.0.0.dist-info/licenses/LICENSE +29 -0
- nost_tools-2.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Provides a base logger application that subscribes and writes all messages to file.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
from datetime import datetime, timedelta
|
|
8
|
+
|
|
9
|
+
from .application import Application
|
|
10
|
+
from .configuration import ConnectionConfig
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class LoggerApplication(Application):
|
|
16
|
+
"""
|
|
17
|
+
Logger NOS-T Application.
|
|
18
|
+
|
|
19
|
+
This object class defines the basic functionality for a NOS-T application
|
|
20
|
+
that subscribes to a specified topic and logs all messages to file.
|
|
21
|
+
|
|
22
|
+
Attributes:
|
|
23
|
+
prefix (str): The test run namespace (prefix)
|
|
24
|
+
simulator (:obj:`Simulator`): Application simulator defined in the *Simulator* class
|
|
25
|
+
app_name (str): Logger application name (default: logger)
|
|
26
|
+
app_description (str): Logger application description (optional)
|
|
27
|
+
log_app (str): Application name to be logged (default: "+")
|
|
28
|
+
log_topic (str): Topic to be logged (default: "#")
|
|
29
|
+
log_dir (str): Directory to write log files (default: ".")
|
|
30
|
+
log_file (:obj:`File`): Current log file
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, app_name: str = "logger", app_description: str = None):
|
|
34
|
+
"""
|
|
35
|
+
Initializes a new logging application.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
app_name (str): application name (default: "logger")
|
|
39
|
+
app_description (str): application description (optional)
|
|
40
|
+
"""
|
|
41
|
+
super().__init__(app_name, app_description)
|
|
42
|
+
self.log_topic = None
|
|
43
|
+
self.log_app = None
|
|
44
|
+
self.log_dir = None
|
|
45
|
+
self.log_file = None
|
|
46
|
+
|
|
47
|
+
def start_up(
|
|
48
|
+
self,
|
|
49
|
+
prefix: str,
|
|
50
|
+
config: ConnectionConfig,
|
|
51
|
+
set_offset: bool = None,
|
|
52
|
+
time_status_step: timedelta = None,
|
|
53
|
+
time_status_init: datetime = None,
|
|
54
|
+
shut_down_when_terminated: bool = None,
|
|
55
|
+
time_step: timedelta = None,
|
|
56
|
+
manager_app_name: str = None,
|
|
57
|
+
log_app: str = "+",
|
|
58
|
+
log_topic: str = "#",
|
|
59
|
+
log_dir: str = ".",
|
|
60
|
+
) -> None:
|
|
61
|
+
"""
|
|
62
|
+
Starts up the logger application by connecting to message broker,
|
|
63
|
+
starting a background event loop, subscribing to manager events, and
|
|
64
|
+
registering callback functions.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
prefix (str): The test run namespace (prefix)
|
|
68
|
+
config (:obj:`ConnectionConfig`): The connection configuration
|
|
69
|
+
set_offset (bool): True, if the system clock offset shall be set
|
|
70
|
+
time_status_step (:obj:`timedelta`): Time interval for status messages
|
|
71
|
+
time_status_init (:obj:`datetime`): Initial time for status messages
|
|
72
|
+
shut_down_when_terminated (bool): True, if the application shall shut down when terminated
|
|
73
|
+
time_step (:obj:`timedelta`): Time step for the application
|
|
74
|
+
manager_app_name (str): Manager application name
|
|
75
|
+
log_app (str): Application name to be logged (default: "+")
|
|
76
|
+
log_topic (str): Topic to be logged (default: "#")
|
|
77
|
+
log_dir (str): Directory to write log files (default: ".")
|
|
78
|
+
"""
|
|
79
|
+
if (
|
|
80
|
+
set_offset is not None
|
|
81
|
+
and time_status_step is not None
|
|
82
|
+
and time_status_init is not None
|
|
83
|
+
and shut_down_when_terminated is not None
|
|
84
|
+
and time_step is not None
|
|
85
|
+
and manager_app_name is not None
|
|
86
|
+
):
|
|
87
|
+
self.set_offset = set_offset
|
|
88
|
+
self.time_status_step = time_status_step
|
|
89
|
+
self.time_status_init = time_status_init
|
|
90
|
+
self.shut_down_when_terminated = shut_down_when_terminated
|
|
91
|
+
self.time_step = time_step
|
|
92
|
+
self.manager_app_name = manager_app_name
|
|
93
|
+
else:
|
|
94
|
+
self.config = config
|
|
95
|
+
parameters = getattr(
|
|
96
|
+
self.config.rc.simulation_configuration.execution_parameters,
|
|
97
|
+
"logger_application",
|
|
98
|
+
None,
|
|
99
|
+
)
|
|
100
|
+
self.set_offset = parameters.set_offset
|
|
101
|
+
self.time_status_step = parameters.time_status_step
|
|
102
|
+
self.time_status_init = parameters.time_status_init
|
|
103
|
+
self.shut_down_when_terminated = parameters.shut_down_when_terminated
|
|
104
|
+
|
|
105
|
+
self.log_app = log_app
|
|
106
|
+
self.log_topic = log_topic
|
|
107
|
+
self.log_dir = log_dir
|
|
108
|
+
|
|
109
|
+
# Create log directory if it doesn't exist
|
|
110
|
+
os.makedirs(self.log_dir, exist_ok=True)
|
|
111
|
+
|
|
112
|
+
# Open log file now
|
|
113
|
+
self._open_log_file()
|
|
114
|
+
|
|
115
|
+
# Start up base application
|
|
116
|
+
super().start_up(
|
|
117
|
+
prefix,
|
|
118
|
+
config,
|
|
119
|
+
self.set_offset,
|
|
120
|
+
self.time_status_step,
|
|
121
|
+
self.time_status_init,
|
|
122
|
+
self.shut_down_when_terminated,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Add callback for specified topic(s)
|
|
126
|
+
self.add_message_callback(self.log_app, self.log_topic, self.on_log_message)
|
|
127
|
+
|
|
128
|
+
logger.info(f"Logger {self.app_name} started up and listening for messages.")
|
|
129
|
+
|
|
130
|
+
def shut_down(self) -> None:
|
|
131
|
+
"""
|
|
132
|
+
Shuts down the application by stopping the background event loop
|
|
133
|
+
and disconnecting from the message broker.
|
|
134
|
+
"""
|
|
135
|
+
# Close the log file if it's open
|
|
136
|
+
self._close_log_file()
|
|
137
|
+
|
|
138
|
+
# Shut down base application
|
|
139
|
+
super().shut_down()
|
|
140
|
+
|
|
141
|
+
def _open_log_file(self) -> None:
|
|
142
|
+
"""
|
|
143
|
+
Opens a new log file for writing messages.
|
|
144
|
+
"""
|
|
145
|
+
if self.log_file is not None:
|
|
146
|
+
self._close_log_file()
|
|
147
|
+
|
|
148
|
+
ts = (
|
|
149
|
+
str(self.simulator.get_wallclock_time()).replace(" ", "T").replace(":", "-")
|
|
150
|
+
)
|
|
151
|
+
log_filename = os.path.join(self.log_dir, f"{ts}.log")
|
|
152
|
+
self.log_file = open(log_filename, "a")
|
|
153
|
+
self.log_file.write(f"Timestamp,Topic,Payload\n")
|
|
154
|
+
logger.info(f"Logger {self.app_name} opened file {self.log_file.name}.")
|
|
155
|
+
|
|
156
|
+
def _close_log_file(self) -> None:
|
|
157
|
+
"""
|
|
158
|
+
Closes the current log file if it's open.
|
|
159
|
+
"""
|
|
160
|
+
if self.log_file is not None:
|
|
161
|
+
self.log_file.close()
|
|
162
|
+
logger.info(f"Logger {self.app_name} closed file {self.log_file.name}.")
|
|
163
|
+
self.log_file = None
|
|
164
|
+
|
|
165
|
+
def on_log_message(self, ch, method, properties, body):
|
|
166
|
+
"""
|
|
167
|
+
Callback function to log a message received by the logger application.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
ch: The channel object
|
|
171
|
+
method: The method frame
|
|
172
|
+
properties: The message properties
|
|
173
|
+
body: The message body
|
|
174
|
+
"""
|
|
175
|
+
if self.log_file is not None:
|
|
176
|
+
try:
|
|
177
|
+
routing_key = method.routing_key
|
|
178
|
+
payload = body.decode("utf-8") if isinstance(body, bytes) else str(body)
|
|
179
|
+
|
|
180
|
+
logger.debug(f"Logger {self.app_name} logging message: {payload}")
|
|
181
|
+
|
|
182
|
+
timestamp = self.simulator.get_wallclock_time()
|
|
183
|
+
self.log_file.write(f"{timestamp},{routing_key},{payload}\n")
|
|
184
|
+
self.log_file.flush() # Ensure data is written immediately
|
|
185
|
+
except Exception as e:
|
|
186
|
+
logger.error(f"Error logging message: {e}")
|
|
187
|
+
else:
|
|
188
|
+
# If log file isn't open, try to reopen it
|
|
189
|
+
self._open_log_file()
|
|
190
|
+
logger.error(f"Logger {self.app_name} had to reopen log file.")
|
|
191
|
+
# Try to log the message again
|
|
192
|
+
self.on_log_message(ch, method, properties, body)
|
|
@@ -0,0 +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())
|