Skip to content

Commit d4d72ab

Browse files
committed
docs: add command provider and consumer examples to quickstart guide
- Minimal command provider example using BITControlProvider - Command provider with execution status using GlobalVectorControlProvider - Minimal command consumer example using BITControlConsumer - Reference to globalvector_consumer example for a complete implementation
1 parent 56b044f commit d4d72ab

1 file changed

Lines changed: 177 additions & 0 deletions

File tree

python/docs/getting-started.md

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,183 @@ async def main():
131131
asyncio.run(main())
132132
```
133133

134+
## Minimal Example — Command Provider
135+
136+
Command providers receive commands addressed to them and drive each command
137+
through the ICD state machine: **ISSUED → COMMANDED → EXECUTING → COMPLETED**.
138+
Subclass a pre-wired provider and override the `on_executing()` hook to
139+
implement your domain logic:
140+
141+
```python
142+
import asyncio
143+
import logging
144+
from rtiumaapy import DDSContext, set_timestamp
145+
from rtiumaapy.services.so import BITControlProvider
146+
from rtiumaapy.command_provider_session import CommandProviderSession
147+
148+
logging.basicConfig(level=logging.INFO)
149+
150+
class MyBITProvider(BITControlProvider):
151+
"""Simple Built-In Test provider that always succeeds."""
152+
153+
async def on_commanded(self, session: CommandProviderSession):
154+
print(f"BIT command accepted — session {session.session_id}")
155+
156+
async def on_executing(self, session: CommandProviderSession):
157+
print("Running built-in test...")
158+
await asyncio.sleep(1) # simulate work
159+
print("Built-in test passed.")
160+
161+
async def on_complete(self, session: CommandProviderSession):
162+
print("BIT command completed successfully.")
163+
164+
async def main():
165+
ctx = DDSContext(domain_id=0)
166+
provider = MyBITProvider(ctx, source_id=ctx.source_id)
167+
provider.start()
168+
await ctx.run_until_shutdown()
169+
170+
asyncio.run(main())
171+
```
172+
173+
The base class handles ack publishing, status transitions, and session
174+
management automatically — you only write the behavior.
175+
176+
## Command Provider with Execution Status
177+
178+
Some command services include an execution-status topic for progress feedback.
179+
The `GlobalVectorControlProvider` (MO domain) is a good example — it reports
180+
whether heading and speed targets have been achieved:
181+
182+
```python
183+
import asyncio
184+
import logging
185+
from rtiumaapy import DDSContext, set_timestamp
186+
from rtiumaapy.services.mo import GlobalVectorControlProvider
187+
from rtiumaapy.command_provider_session import CommandProviderSession
188+
from rtiumaapy.datamodel.GlobalVectorExecutionStatusReportType import (
189+
UMAA_MO_GlobalVectorControl_GlobalVectorExecutionStatusReportType
190+
as GlobalVectorExecStatus,
191+
)
192+
193+
logging.basicConfig(level=logging.INFO)
194+
195+
class MyGlobalVectorProvider(GlobalVectorControlProvider):
196+
"""Heading/speed controller that publishes execution status."""
197+
198+
async def on_commanded(self, session: CommandProviderSession):
199+
cmd = session.command
200+
print(f"GlobalVector COMMANDED — direction={getattr(cmd, 'direction', '?')}")
201+
202+
async def on_executing(self, session: CommandProviderSession):
203+
cmd = session.command
204+
205+
# Publish execution status showing targets achieved
206+
exec_status = GlobalVectorExecStatus()
207+
exec_status.source = self._source_id
208+
exec_status.sessionID = cmd.sessionID
209+
exec_status.directionAchieved = True
210+
exec_status.speedAchieved = True
211+
exec_status.elevationAchieved = True
212+
set_timestamp(exec_status)
213+
214+
if self._exec_status_writer is not None:
215+
self._exec_status_writer.write(exec_status)
216+
217+
print("GlobalVector targets achieved.")
218+
219+
async def on_complete(self, session: CommandProviderSession):
220+
print("GlobalVector COMPLETED")
221+
222+
async def main():
223+
ctx = DDSContext(domain_id=0)
224+
provider = MyGlobalVectorProvider(ctx, source_id=ctx.source_id)
225+
provider.start()
226+
await ctx.run_until_shutdown()
227+
228+
asyncio.run(main())
229+
```
230+
231+
## Minimal Example — Command Consumer
232+
233+
The consumer side sends a command to a provider and reacts to lifecycle
234+
events (ack, status transitions, execution status, terminal) via hooks.
235+
Here's a `BITControlConsumer` that sends a BIT command and waits for
236+
completion:
237+
238+
```python
239+
import asyncio
240+
import logging
241+
from rtiumaapy import DDSContext
242+
from rtiumaapy.guid_util import GUIDUtil
243+
from rtiumaapy.services.so import BITControlConsumer
244+
from rtiumaapy.datamodel.BITCommandType import (
245+
UMAA_SO_BITControl_BITCommandType as BITCommandType,
246+
)
247+
248+
logging.basicConfig(level=logging.INFO)
249+
250+
class MyBITConsumer(BITControlConsumer):
251+
"""BIT consumer that logs every lifecycle event."""
252+
253+
def __init__(self, ctx, *, source_id, destination_id):
254+
super().__init__(ctx, source_id=source_id, destination_id=destination_id)
255+
self.done = asyncio.Event()
256+
257+
async def on_ack(self, session_id, ack):
258+
print(f"ACK received — session={GUIDUtil.to_hex(session_id)}")
259+
260+
async def on_status(self, session_id, status):
261+
print(f"STATUS — {status.commandStatus}")
262+
263+
async def on_terminal(self, session_id, status):
264+
print(f"TERMINAL — {status.commandStatus if status else 'cancelled'}")
265+
self.done.set()
266+
267+
async def main():
268+
ctx = DDSContext(domain_id=0)
269+
270+
# destination_id must match the provider's source_id
271+
destination_id = GUIDUtil.make_source_id("<provider-guid-hex>")
272+
273+
consumer = MyBITConsumer(
274+
ctx, source_id=ctx.source_id, destination_id=destination_id,
275+
)
276+
consumer.start()
277+
278+
# Wait for the provider to appear on the network
279+
await consumer.wait_for_discovery(timeout=30.0)
280+
281+
# Build and send the command
282+
cmd = BITCommandType()
283+
session_id = await consumer.send(cmd)
284+
print(f"Command sent — session={GUIDUtil.to_hex(session_id)}")
285+
286+
# Wait for the command lifecycle to finish
287+
await asyncio.wait_for(consumer.done.wait(), timeout=30.0)
288+
289+
asyncio.run(main())
290+
```
291+
292+
Key points:
293+
294+
- `source_id` identifies this consumer; `destination_id` is the target
295+
provider's identity (its content filter routes on this).
296+
- `consumer.send(cmd)` auto-stamps header fields and returns the session ID.
297+
- `wait_for_discovery()` blocks until a matching provider is found on the
298+
network.
299+
- The consumer reader loops dispatch `on_ack`, `on_status`, `on_exec_status`,
300+
and `on_terminal` as events arrive.
301+
302+
```{tip}
303+
Run the provider example from the previous section in one terminal and this
304+
consumer in another to see the full
305+
ISSUED → COMMANDED → EXECUTING → COMPLETED lifecycle.
306+
307+
For a more complete consumer with argument parsing, execution-status tracking,
308+
and graceful shutdown, see `examples/globalvector_consumer/run_globalvector_consumer.py`.
309+
```
310+
134311
## What's Next
135312

136313
- {doc}`building-a-component` — Build a multi-service component from scratch

0 commit comments

Comments
 (0)