nost-tools 2.1.1__tar.gz → 2.2.0__tar.gz
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-2.1.1 → nost_tools-2.2.0}/PKG-INFO +1 -1
- {nost_tools-2.1.1 → nost_tools-2.2.0}/nost_tools/__init__.py +1 -1
- {nost_tools-2.1.1 → nost_tools-2.2.0}/nost_tools/application.py +1 -1
- {nost_tools-2.1.1 → nost_tools-2.2.0}/nost_tools/application_utils.py +6 -7
- {nost_tools-2.1.1 → nost_tools-2.2.0}/nost_tools/configuration.py +32 -0
- {nost_tools-2.1.1 → nost_tools-2.2.0}/nost_tools/manager.py +74 -1
- {nost_tools-2.1.1 → nost_tools-2.2.0}/nost_tools/schemas.py +40 -1
- {nost_tools-2.1.1 → nost_tools-2.2.0}/nost_tools.egg-info/PKG-INFO +1 -1
- {nost_tools-2.1.1 → nost_tools-2.2.0}/LICENSE +0 -0
- {nost_tools-2.1.1 → nost_tools-2.2.0}/README.md +0 -0
- {nost_tools-2.1.1 → nost_tools-2.2.0}/nost_tools/entity.py +0 -0
- {nost_tools-2.1.1 → nost_tools-2.2.0}/nost_tools/errors.py +0 -0
- {nost_tools-2.1.1 → nost_tools-2.2.0}/nost_tools/logger_application.py +0 -0
- {nost_tools-2.1.1 → nost_tools-2.2.0}/nost_tools/managed_application.py +0 -0
- {nost_tools-2.1.1 → nost_tools-2.2.0}/nost_tools/observer.py +0 -0
- {nost_tools-2.1.1 → nost_tools-2.2.0}/nost_tools/publisher.py +0 -0
- {nost_tools-2.1.1 → nost_tools-2.2.0}/nost_tools/simulator.py +0 -0
- {nost_tools-2.1.1 → nost_tools-2.2.0}/nost_tools.egg-info/SOURCES.txt +0 -0
- {nost_tools-2.1.1 → nost_tools-2.2.0}/nost_tools.egg-info/dependency_links.txt +0 -0
- {nost_tools-2.1.1 → nost_tools-2.2.0}/nost_tools.egg-info/requires.txt +0 -0
- {nost_tools-2.1.1 → nost_tools-2.2.0}/nost_tools.egg-info/top_level.txt +0 -0
- {nost_tools-2.1.1 → nost_tools-2.2.0}/pyproject.toml +0 -0
- {nost_tools-2.1.1 → nost_tools-2.2.0}/setup.cfg +0 -0
- {nost_tools-2.1.1 → nost_tools-2.2.0}/tests/test_entity.py +0 -0
- {nost_tools-2.1.1 → nost_tools-2.2.0}/tests/test_observer.py +0 -0
- {nost_tools-2.1.1 → nost_tools-2.2.0}/tests/test_simulator.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nost_tools
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Tools for Novel Observing Strategies Testbed (NOS-T) Applications
|
|
5
5
|
Author-email: "Paul T. Grogan" <paul.grogan@asu.edu>, "Emmanuel M. Gonzalez" <emmanuelgonzalez@asu.edu>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -253,10 +253,9 @@ class ModeStatusObserver(Observer):
|
|
|
253
253
|
if not isinstance(self.app.prefix, str):
|
|
254
254
|
raise ValueError(f"Exchange ({self.app.prefix}) must be a string")
|
|
255
255
|
|
|
256
|
-
#
|
|
257
|
-
|
|
258
|
-
self.app.
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
)
|
|
256
|
+
# if self.app.channel.is_open and self.app.connection.is_open:
|
|
257
|
+
self.app.send_message(
|
|
258
|
+
app_name=self.app.app_name,
|
|
259
|
+
app_topics="status.mode",
|
|
260
|
+
payload=status.model_dump_json(by_alias=True, exclude_none=True),
|
|
261
|
+
)
|
|
@@ -25,6 +25,7 @@ from .schemas import (
|
|
|
25
25
|
|
|
26
26
|
logger = logging.getLogger(__name__)
|
|
27
27
|
|
|
28
|
+
|
|
28
29
|
class ConnectionConfig:
|
|
29
30
|
"""Connection configuration.
|
|
30
31
|
|
|
@@ -59,6 +60,7 @@ class ConnectionConfig:
|
|
|
59
60
|
virtual_host: str = None,
|
|
60
61
|
is_tls: bool = True,
|
|
61
62
|
yaml_file: str = None,
|
|
63
|
+
app_name: str = None,
|
|
62
64
|
):
|
|
63
65
|
"""
|
|
64
66
|
Initializes a new connection configuration.
|
|
@@ -75,6 +77,7 @@ class ConnectionConfig:
|
|
|
75
77
|
virtual_host (str): RabbitMQ virtual host
|
|
76
78
|
is_tls (bool): True, if the connection uses TLS
|
|
77
79
|
yaml_file (str): Path to the YAML configuration file
|
|
80
|
+
app_name (str): Name of the application to get specific configuration for
|
|
78
81
|
"""
|
|
79
82
|
self.username = username
|
|
80
83
|
self.password = password
|
|
@@ -93,6 +96,8 @@ class ConnectionConfig:
|
|
|
93
96
|
self.yaml_file = yaml_file
|
|
94
97
|
self.unique_exchanges = {}
|
|
95
98
|
self.channel_configs = []
|
|
99
|
+
self.app_name = app_name
|
|
100
|
+
self.app_specific = None
|
|
96
101
|
|
|
97
102
|
self.create_connection_config()
|
|
98
103
|
|
|
@@ -208,6 +213,29 @@ class ConnectionConfig:
|
|
|
208
213
|
except ValidationError as err:
|
|
209
214
|
raise EnvironmentVariableError(f"Invalid environment variables: {err}")
|
|
210
215
|
|
|
216
|
+
def get_app_specific_config(self, app_name):
|
|
217
|
+
"""
|
|
218
|
+
Get application-specific configuration from execution.managed_applications if available.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
app_name (str): Name of the application
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
dict: Application-specific configuration parameters if available, otherwise None.
|
|
225
|
+
"""
|
|
226
|
+
if not os.path.exists(self.yaml_file):
|
|
227
|
+
raise ConfigurationError("Couldn't load config file (not found)")
|
|
228
|
+
|
|
229
|
+
with open(self.yaml_file, "r", encoding="utf-8") as f:
|
|
230
|
+
yaml_data = yaml.safe_load(f)
|
|
231
|
+
|
|
232
|
+
try:
|
|
233
|
+
return yaml_data["execution"]["managed_applications"][app_name][
|
|
234
|
+
"configuration_parameters"
|
|
235
|
+
]
|
|
236
|
+
except:
|
|
237
|
+
return None
|
|
238
|
+
|
|
211
239
|
def load_yaml_config_file(self):
|
|
212
240
|
"""
|
|
213
241
|
Loads a YAML configuration file and returns the parsed data.
|
|
@@ -243,6 +271,9 @@ class ConnectionConfig:
|
|
|
243
271
|
), "Application names do not match the channels defined in the configuration file."
|
|
244
272
|
except ConfigAssertionError as e:
|
|
245
273
|
raise ValueError(f"Assertion error: {e}")
|
|
274
|
+
# Load app-specific configuration if app_name is provided
|
|
275
|
+
if self.app_name:
|
|
276
|
+
self.app_specific = self.get_app_specific_config(self.app_name)
|
|
246
277
|
else:
|
|
247
278
|
try:
|
|
248
279
|
self.yaml_config = Config(
|
|
@@ -301,4 +332,5 @@ class ConnectionConfig:
|
|
|
301
332
|
credentials=self.credentials_config,
|
|
302
333
|
server_configuration=server_config,
|
|
303
334
|
simulation_configuration=self.simulation_config,
|
|
335
|
+
application_configuration=self.app_specific,
|
|
304
336
|
)
|
|
@@ -13,6 +13,7 @@ from typing import List
|
|
|
13
13
|
from pydantic import ValidationError
|
|
14
14
|
|
|
15
15
|
from .application import Application
|
|
16
|
+
from .application_utils import ConnectionConfig
|
|
16
17
|
from .schemas import (
|
|
17
18
|
InitCommand,
|
|
18
19
|
ReadyStatus,
|
|
@@ -128,6 +129,69 @@ class Manager(Application):
|
|
|
128
129
|
f"Heartbeat check: {remaining:.2f} seconds remaining in sleep"
|
|
129
130
|
)
|
|
130
131
|
|
|
132
|
+
def start_up(
|
|
133
|
+
self,
|
|
134
|
+
prefix: str,
|
|
135
|
+
config: ConnectionConfig,
|
|
136
|
+
set_offset: bool = None,
|
|
137
|
+
time_status_step: timedelta = None,
|
|
138
|
+
time_status_init: datetime = None,
|
|
139
|
+
shut_down_when_terminated: bool = None,
|
|
140
|
+
time_step: timedelta = None,
|
|
141
|
+
manager_app_name: str = None,
|
|
142
|
+
) -> None:
|
|
143
|
+
"""
|
|
144
|
+
Starts up the application by connecting to message broker, starting a background event loop,
|
|
145
|
+
subscribing to manager events, and registering callback functions.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
prefix (str): execution namespace (prefix)
|
|
149
|
+
config (:obj:`ConnectionConfig`): connection configuration
|
|
150
|
+
set_offset (bool): True, if the system clock offset shall be set using a NTP request prior to execution
|
|
151
|
+
time_status_step (:obj:`timedelta`): scenario duration between time status messages
|
|
152
|
+
time_status_init (:obj:`datetime`): scenario time for first time status message
|
|
153
|
+
shut_down_when_terminated (bool): True, if the application should shut down when the simulation is terminated
|
|
154
|
+
time_step (:obj:`timedelta`): scenario time step used in execution (Default: 1 second)
|
|
155
|
+
manager_app_name (str): manager application name (Default: manager)
|
|
156
|
+
"""
|
|
157
|
+
if (
|
|
158
|
+
set_offset is not None
|
|
159
|
+
and time_status_step is not None
|
|
160
|
+
and time_status_init is not None
|
|
161
|
+
and shut_down_when_terminated is not None
|
|
162
|
+
and time_step is not None
|
|
163
|
+
and manager_app_name is not None
|
|
164
|
+
):
|
|
165
|
+
self.set_offset = set_offset
|
|
166
|
+
self.time_status_step = time_status_step
|
|
167
|
+
self.time_status_init = time_status_init
|
|
168
|
+
self.shut_down_when_terminated = shut_down_when_terminated
|
|
169
|
+
self.time_step = time_step
|
|
170
|
+
self.manager_app_name = manager_app_name
|
|
171
|
+
else:
|
|
172
|
+
self.config = config
|
|
173
|
+
parameters = (
|
|
174
|
+
self.config.rc.simulation_configuration.execution_parameters.manager
|
|
175
|
+
)
|
|
176
|
+
self.set_offset = parameters.set_offset
|
|
177
|
+
self.time_status_step = parameters.time_status_step
|
|
178
|
+
self.time_status_init = parameters.time_status_init
|
|
179
|
+
self.shut_down_when_terminated = parameters.shut_down_when_terminated
|
|
180
|
+
self.time_step = parameters.time_step
|
|
181
|
+
|
|
182
|
+
# start up base application
|
|
183
|
+
super().start_up(
|
|
184
|
+
prefix,
|
|
185
|
+
config,
|
|
186
|
+
self.set_offset,
|
|
187
|
+
self.time_status_step,
|
|
188
|
+
self.time_status_init,
|
|
189
|
+
self.shut_down_when_terminated,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# Establish the exchange
|
|
193
|
+
self.establish_exchange()
|
|
194
|
+
|
|
131
195
|
def execute_test_plan(self, *args, **kwargs) -> None:
|
|
132
196
|
"""
|
|
133
197
|
Starts the test plan execution in a background thread.
|
|
@@ -218,7 +282,16 @@ class Manager(Application):
|
|
|
218
282
|
"No configuration runtime. Please provide simulation start and stop times."
|
|
219
283
|
)
|
|
220
284
|
|
|
221
|
-
|
|
285
|
+
# Convert TimeScaleUpdateSchema objects to TimeScaleUpdate objects
|
|
286
|
+
converted_updates = []
|
|
287
|
+
for update_schema in parameters.time_scale_updates:
|
|
288
|
+
converted_updates.append(
|
|
289
|
+
TimeScaleUpdate(
|
|
290
|
+
time_scale_factor=update_schema.time_scale_factor,
|
|
291
|
+
sim_update_time=update_schema.sim_update_time,
|
|
292
|
+
)
|
|
293
|
+
)
|
|
294
|
+
self.time_scale_updates = converted_updates
|
|
222
295
|
|
|
223
296
|
# Set up tracking of required applications
|
|
224
297
|
self.required_apps_status = dict(
|
|
@@ -311,6 +311,19 @@ class GeneralConfig(BaseModel):
|
|
|
311
311
|
prefix: str = Field("nost", description="Execution prefix.")
|
|
312
312
|
|
|
313
313
|
|
|
314
|
+
class TimeScaleUpdateSchema(BaseModel):
|
|
315
|
+
"""
|
|
316
|
+
Provides a scheduled update to the simulation time scale factor.
|
|
317
|
+
"""
|
|
318
|
+
|
|
319
|
+
time_scale_factor: float = Field(
|
|
320
|
+
..., description="Scenario seconds per wallclock second"
|
|
321
|
+
)
|
|
322
|
+
sim_update_time: datetime = Field(
|
|
323
|
+
..., description="Scenario time that the update will occur"
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
|
|
314
327
|
class ManagerConfig(BaseModel):
|
|
315
328
|
sim_start_time: Optional[datetime] = Field(
|
|
316
329
|
None, description="Simulation start time."
|
|
@@ -322,7 +335,7 @@ class ManagerConfig(BaseModel):
|
|
|
322
335
|
description="Time step for the simulation.",
|
|
323
336
|
)
|
|
324
337
|
time_scale_factor: float = Field(1.0, description="Time scale factor.")
|
|
325
|
-
time_scale_updates: List[
|
|
338
|
+
time_scale_updates: List[TimeScaleUpdateSchema] = Field(
|
|
326
339
|
default_factory=list, description="List of time scale updates."
|
|
327
340
|
)
|
|
328
341
|
time_status_step: Optional[timedelta] = Field(None, description="Time status step.")
|
|
@@ -437,6 +450,26 @@ class LoggerApplicationConfig(BaseModel):
|
|
|
437
450
|
)
|
|
438
451
|
|
|
439
452
|
|
|
453
|
+
class ApplicationConfig(BaseModel):
|
|
454
|
+
set_offset: Optional[bool] = Field(True, description="Set offset.")
|
|
455
|
+
time_scale_factor: Optional[float] = Field(1.0, description="Time scale factor.")
|
|
456
|
+
time_step: Optional[timedelta] = Field(
|
|
457
|
+
timedelta(seconds=1), description="Time step for swe_change."
|
|
458
|
+
)
|
|
459
|
+
time_status_step: Optional[timedelta] = Field(
|
|
460
|
+
timedelta(seconds=10), description="Time status step."
|
|
461
|
+
)
|
|
462
|
+
time_status_init: Optional[datetime] = Field(
|
|
463
|
+
datetime.now(), description="Time status init."
|
|
464
|
+
)
|
|
465
|
+
shut_down_when_terminated: Optional[bool] = Field(
|
|
466
|
+
False, description="Shut down when terminated."
|
|
467
|
+
)
|
|
468
|
+
manager_app_name: Optional[str] = Field(
|
|
469
|
+
"manager", description="Manager application name."
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
|
|
440
473
|
class ExecConfig(BaseModel):
|
|
441
474
|
general: GeneralConfig
|
|
442
475
|
manager: Optional[ManagerConfig] = Field(None, description="Manager configuration.")
|
|
@@ -447,6 +480,9 @@ class ExecConfig(BaseModel):
|
|
|
447
480
|
logger_application: Optional[LoggerApplicationConfig] = Field(
|
|
448
481
|
None, description="Logger application configuration."
|
|
449
482
|
)
|
|
483
|
+
application: Optional[ApplicationConfig] = Field(
|
|
484
|
+
None, description="Application configuration."
|
|
485
|
+
)
|
|
450
486
|
|
|
451
487
|
|
|
452
488
|
class Config(BaseModel):
|
|
@@ -500,3 +536,6 @@ class RuntimeConfig(BaseModel):
|
|
|
500
536
|
simulation_configuration: SimulationConfig = Field(
|
|
501
537
|
..., description="Simulation configuration."
|
|
502
538
|
)
|
|
539
|
+
application_configuration: Optional[Dict] = Field(
|
|
540
|
+
None, description="Application-specific, user-provided configuration."
|
|
541
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nost_tools
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Tools for Novel Observing Strategies Testbed (NOS-T) Applications
|
|
5
5
|
Author-email: "Paul T. Grogan" <paul.grogan@asu.edu>, "Emmanuel M. Gonzalez" <emmanuelgonzalez@asu.edu>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|