Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions client/python/projectairsim/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ bonsai = [
datacollection = [
"albumentations==1.3.0",
]
tests = [
"pytest",
"psutil>=5.9.0",
]

[tool.setuptools]
# Source layout: packages live under src/
Expand Down
16 changes: 10 additions & 6 deletions client/python/projectairsim/src/projectairsim/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,6 @@ def connect(self):
)
projectairsim_log().info("Connection opened.")
self.state = True
self.recv_topic_thread = threading.Thread(target=self.__recv_topic)
self.recv_topic_thread.start()
projectairsim_log().info("Started the pub-sub topic receiving thread.")

def get_topic_info(self):
"""This is used by World to get the list of topic info on reload scene."""
Expand Down Expand Up @@ -125,6 +122,12 @@ def subscribe(self, topic, callback, reliability=1.0):
topic (str): the name of the topic
callback (callable): function to call when data is received
"""
# Lazily start the recv thread on first subscribe call
if self.recv_topic_thread is None:
self.socket_topics.recv_timeout = 100 # ms — bounded block for clean shutdown
self.recv_topic_thread = threading.Thread(target=self.__recv_topic)
self.recv_topic_thread.start()

if topic in self.subs:
self.subs[topic]["callbacks"].append(callback)
self.subs[topic]["reliability"] = reliability
Expand Down Expand Up @@ -479,9 +482,10 @@ def __make_message_frame(self, topic, message):

def __recv_topic_frame(self):
try:
# Check if new data is ready to be received with non-blocking recv() call
frame_packed = self.socket_topics.recv(block=False)
except pynng.exceptions.TryAgain:
# Blocking recv with timeout (set on socket in subscribe()).
# Returns None on timeout so the loop can check self.state.
frame_packed = self.socket_topics.recv()
except (pynng.exceptions.TryAgain, pynng.exceptions.Timeout):
return

# If data was ready to be received, process and return it.
Expand Down
29 changes: 28 additions & 1 deletion client/python/projectairsim/src/projectairsim/world.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,34 @@ def continue_for_single_step(self, wait_until_complete: bool = True) -> int:
single_step_result: str = self.client.request(single_step_req)
return single_step_result

def list_actors(self) -> List[str]:
def step(self, dt_ns: int) -> dict:
"""Advance sim by dt_ns nanoseconds and return bundled state + events.

This is the pull API entry point. The sim advances time, then returns
per-drone kinematics and any events (collisions) that
occurred during the step.

Actions should be sent to individual drones via their existing move
methods before calling step().

Args:
dt_ns: simulation time to advance, in nanoseconds

Returns:
dict with keys:
sim_time_ns (int): current sim time after the step
drones (dict): per-drone data keyed by drone ID, each containing:
state (dict): position, orientation, linear_velocity, angular_velocity
events (list): collision and gate_pass events with sim_time_ns ordering
"""
step_req: dict = {
"method": f"{self.parent_topic}/Step",
"params": {"dt_ns": dt_ns},
"version": 1.0,
}
return self.client.request(step_req)

def list_actors(self) -> list[str]:
"""List actor names in the scene

Returns:
Expand Down
Loading
Loading