nost-tools 2.0.3__tar.gz → 2.0.5__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.

Files changed (26) hide show
  1. {nost_tools-2.0.3 → nost_tools-2.0.5}/PKG-INFO +1 -1
  2. {nost_tools-2.0.3 → nost_tools-2.0.5}/nost_tools/__init__.py +1 -1
  3. {nost_tools-2.0.3 → nost_tools-2.0.5}/nost_tools/manager.py +97 -48
  4. {nost_tools-2.0.3 → nost_tools-2.0.5}/nost_tools.egg-info/PKG-INFO +1 -1
  5. {nost_tools-2.0.3 → nost_tools-2.0.5}/LICENSE +0 -0
  6. {nost_tools-2.0.3 → nost_tools-2.0.5}/README.md +0 -0
  7. {nost_tools-2.0.3 → nost_tools-2.0.5}/nost_tools/application.py +0 -0
  8. {nost_tools-2.0.3 → nost_tools-2.0.5}/nost_tools/application_utils.py +0 -0
  9. {nost_tools-2.0.3 → nost_tools-2.0.5}/nost_tools/configuration.py +0 -0
  10. {nost_tools-2.0.3 → nost_tools-2.0.5}/nost_tools/entity.py +0 -0
  11. {nost_tools-2.0.3 → nost_tools-2.0.5}/nost_tools/errors.py +0 -0
  12. {nost_tools-2.0.3 → nost_tools-2.0.5}/nost_tools/logger_application.py +0 -0
  13. {nost_tools-2.0.3 → nost_tools-2.0.5}/nost_tools/managed_application.py +0 -0
  14. {nost_tools-2.0.3 → nost_tools-2.0.5}/nost_tools/observer.py +0 -0
  15. {nost_tools-2.0.3 → nost_tools-2.0.5}/nost_tools/publisher.py +0 -0
  16. {nost_tools-2.0.3 → nost_tools-2.0.5}/nost_tools/schemas.py +0 -0
  17. {nost_tools-2.0.3 → nost_tools-2.0.5}/nost_tools/simulator.py +0 -0
  18. {nost_tools-2.0.3 → nost_tools-2.0.5}/nost_tools.egg-info/SOURCES.txt +0 -0
  19. {nost_tools-2.0.3 → nost_tools-2.0.5}/nost_tools.egg-info/dependency_links.txt +0 -0
  20. {nost_tools-2.0.3 → nost_tools-2.0.5}/nost_tools.egg-info/requires.txt +0 -0
  21. {nost_tools-2.0.3 → nost_tools-2.0.5}/nost_tools.egg-info/top_level.txt +0 -0
  22. {nost_tools-2.0.3 → nost_tools-2.0.5}/pyproject.toml +0 -0
  23. {nost_tools-2.0.3 → nost_tools-2.0.5}/setup.cfg +0 -0
  24. {nost_tools-2.0.3 → nost_tools-2.0.5}/tests/test_entity.py +0 -0
  25. {nost_tools-2.0.3 → nost_tools-2.0.5}/tests/test_observer.py +0 -0
  26. {nost_tools-2.0.3 → nost_tools-2.0.5}/tests/test_simulator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nost_tools
3
- Version: 2.0.3
3
+ Version: 2.0.5
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
@@ -1,4 +1,4 @@
1
- __version__ = "2.0.3"
1
+ __version__ = "2.0.5"
2
2
 
3
3
  from .application import Application
4
4
  from .application_utils import ConnectionConfig, ModeStatusObserver, TimeStatusPublisher
@@ -98,7 +98,51 @@ class Manager(Application):
98
98
  auto_delete=True,
99
99
  )
100
100
 
101
- def execute_test_plan(
101
+ def _sleep_with_heartbeat(self, total_seconds):
102
+ """
103
+ Sleep for a specified number of seconds while allowing connection heartbeats.
104
+ Works with SelectConnection by using short sleep intervals.
105
+
106
+ Args:
107
+ total_seconds (float): Total number of seconds to sleep
108
+ """
109
+ if total_seconds <= 0:
110
+ return
111
+
112
+ # Sleep in smaller chunks to allow heartbeats to pass through
113
+ check_interval = 30 # Check every 30 seconds at most
114
+ end_time = time.time() + total_seconds
115
+
116
+ logger.debug(f"Starting heartbeat-safe sleep for {total_seconds:.2f} seconds")
117
+
118
+ while time.time() < end_time:
119
+ # Calculate remaining time
120
+ remaining = end_time - time.time()
121
+
122
+ # Sleep for the shorter of check_interval or remaining time
123
+ sleep_time = min(check_interval, remaining)
124
+
125
+ if sleep_time > 0:
126
+ time.sleep(sleep_time)
127
+ logger.debug(
128
+ f"Heartbeat check: {remaining:.2f} seconds remaining in sleep"
129
+ )
130
+
131
+ def execute_test_plan(self, *args, **kwargs) -> None:
132
+ """
133
+ Starts the test plan execution in a background thread.
134
+
135
+ Args:
136
+ *args: Positional arguments to be passed to the test plan execution.
137
+ **kwargs: Keyword arguments to be passed to the test plan execution.
138
+ """
139
+ thread = threading.Thread(
140
+ target=self._execute_test_plan_impl, args=args, kwargs=kwargs, daemon=True
141
+ )
142
+ logger.debug("Running test plan in background thread.")
143
+ thread.start()
144
+
145
+ def _execute_test_plan_impl(
102
146
  self,
103
147
  sim_start_time: datetime = None,
104
148
  sim_stop_time: datetime = None,
@@ -133,6 +177,7 @@ class Manager(Application):
133
177
  init_retry_delay_s (float): number of seconds to wait between initialization commands while waiting for required applications
134
178
  init_max_retry (int): number of initialization commands while waiting for required applications before continuing to execution
135
179
  """
180
+ # Initialize parameters from arguments or config
136
181
  if sim_start_time is not None and sim_stop_time is not None:
137
182
  self.sim_start_time = sim_start_time
138
183
  self.sim_stop_time = sim_stop_time
@@ -163,9 +208,6 @@ class Manager(Application):
163
208
  self.time_status_step = parameters.time_status_step
164
209
  self.time_status_init = parameters.time_status_init
165
210
  self.command_lead = parameters.command_lead
166
- # self.required_apps = (
167
- # self.config.rc.simulation_configuration.execution_parameters.required_apps
168
- # )
169
211
  self.required_apps = [
170
212
  app for app in parameters.required_apps if app != self.app_name
171
213
  ]
@@ -175,13 +217,10 @@ class Manager(Application):
175
217
  raise ValueError(
176
218
  "No configuration runtime. Please provide simulation start and stop times."
177
219
  )
178
- ####
220
+
179
221
  self.establish_exchange()
180
- # if self.predefined_exchanges_queues:
181
- # self.declare_exchange()
182
- # self.declare_bind_queue()
183
- ####
184
222
 
223
+ # Set up tracking of required applications
185
224
  self.required_apps_status = dict(
186
225
  zip(self.required_apps, [False] * len(self.required_apps))
187
226
  )
@@ -189,35 +228,37 @@ class Manager(Application):
189
228
  self.add_message_callback("*", "status.time", self.on_app_time_status)
190
229
 
191
230
  self._create_time_status_publisher(self.time_status_step, self.time_status_init)
231
+
232
+ # Initialize with retry logic
192
233
  for i in range(self.init_max_retry):
193
- # issue the init command
194
234
  self.init(self.sim_start_time, self.sim_stop_time, self.required_apps)
195
235
  next_try = self.simulator.get_wallclock_time() + timedelta(
196
236
  seconds=self.init_retry_delay_s
197
237
  )
198
- # wait until all required apps are ready
199
238
  while (
200
239
  not all([self.required_apps_status[app] for app in self.required_apps])
201
240
  and self.simulator.get_wallclock_time() < next_try
202
241
  ):
203
242
  time.sleep(0.001)
204
- # self.remove_message_callback("*", "status.ready")
205
- # self.remove_message_callback()
206
- # configure start time
243
+
244
+ # Configure start time if not provided
207
245
  if self.start_time is None:
208
246
  self.start_time = self.simulator.get_wallclock_time() + self.command_lead
209
- # sleep until the start command needs to be issued
210
- time.sleep(
211
- max(
212
- 0,
213
- (
214
- (self.start_time - self.simulator.get_wallclock_time())
215
- - self.command_lead
216
- )
217
- / timedelta(seconds=1),
247
+
248
+ # Sleep until start time using heartbeat-safe approach
249
+ sleep_seconds = max(
250
+ 0,
251
+ (
252
+ (self.start_time - self.simulator.get_wallclock_time())
253
+ - self.command_lead
218
254
  )
255
+ / timedelta(seconds=1),
219
256
  )
220
- # issue the start command
257
+
258
+ # Use our heartbeat-safe sleep
259
+ self._sleep_with_heartbeat(sleep_seconds)
260
+
261
+ # Issue the start command
221
262
  self.start(
222
263
  self.sim_start_time,
223
264
  self.sim_stop_time,
@@ -227,43 +268,51 @@ class Manager(Application):
227
268
  self.time_status_step,
228
269
  self.time_status_init,
229
270
  )
230
- # wait for simulation to start executing
271
+
272
+ # Wait for simulation to start executing
231
273
  while self.simulator.get_mode() != Mode.EXECUTING:
232
274
  time.sleep(0.001)
275
+
276
+ # Process time scale updates
233
277
  for update in self.time_scale_updates:
234
278
  update_time = self.simulator.get_wallclock_time_at_simulation_time(
235
279
  update.sim_update_time
236
280
  )
237
- # sleep until the update command needs to be issued
238
- time.sleep(
239
- max(
240
- 0,
241
- (
242
- (update_time - self.simulator.get_wallclock_time())
243
- - self.command_lead
244
- )
245
- / timedelta(seconds=1),
281
+ # Sleep until update time using heartbeat-safe approach
282
+ sleep_seconds = max(
283
+ 0,
284
+ (
285
+ (update_time - self.simulator.get_wallclock_time())
286
+ - self.command_lead
246
287
  )
288
+ / timedelta(seconds=1),
247
289
  )
248
- # issue the update command
249
- self.update(
250
- update.time_scale_factor, update.sim_update_time, self.required_apps
251
- )
252
- # wait until the update command takes effect
290
+
291
+ # Use our heartbeat-safe sleep
292
+ self._sleep_with_heartbeat(sleep_seconds)
293
+
294
+ # Issue the update command
295
+ self.update(update.time_scale_factor, update.sim_update_time)
296
+
297
+ # Wait until update takes effect
253
298
  while self.simulator.get_time_scale_factor() != update.time_scale_factor:
254
- time.sleep(self.command_lead / timedelta(seconds=1) / 100)
299
+ time.sleep(0.001)
300
+
255
301
  end_time = self.simulator.get_wallclock_time_at_simulation_time(
256
302
  self.simulator.get_end_time()
257
303
  )
258
- # sleep until the stop command should be issued
259
- time.sleep(
260
- max(
261
- 0,
262
- ((end_time - self.simulator.get_wallclock_time()) - self.command_lead)
263
- / timedelta(seconds=1),
264
- )
304
+
305
+ # Sleep until stop time using heartbeat-safe approach
306
+ sleep_seconds = max(
307
+ 0,
308
+ ((end_time - self.simulator.get_wallclock_time()) - self.command_lead)
309
+ / timedelta(seconds=1),
265
310
  )
266
- # issue the stop command
311
+
312
+ # Use our heartbeat-safe sleep
313
+ self._sleep_with_heartbeat(sleep_seconds)
314
+
315
+ # Issue the stop command
267
316
  self.stop(self.sim_stop_time)
268
317
 
269
318
  def on_app_ready_status(self, ch, method, properties, body) -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nost_tools
3
- Version: 2.0.3
3
+ Version: 2.0.5
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