@@ -131,6 +131,183 @@ async def main():
131131asyncio.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