1515)
1616from viam .proto .service .motion import (
1717 Constraints ,
18+ GetPlanRequest ,
19+ GetPlanResponse ,
1820 GetPoseRequest ,
1921 GetPoseResponse ,
22+ ListPlanStatusesRequest ,
23+ ListPlanStatusesResponse ,
2024 MotionConfiguration ,
2125 MotionServiceStub ,
26+ MoveOnGlobeNewRequest ,
27+ MoveOnGlobeNewResponse ,
2228 MoveOnGlobeRequest ,
2329 MoveOnGlobeResponse ,
2430 MoveOnMapRequest ,
2531 MoveOnMapResponse ,
2632 MoveRequest ,
2733 MoveResponse ,
34+ StopPlanRequest ,
35+ StopPlanResponse ,
2836)
2937from viam .resource .rpc_client_base import ReconfigurableResourceRPCClientBase
3038from viam .resource .types import RESOURCE_NAMESPACE_RDK , RESOURCE_TYPE_SERVICE , Subtype
@@ -108,10 +116,10 @@ async def move_on_globe(
108116 component_name (ResourceName): The component to move
109117 destination (GeoPoint): The destination point
110118 movement_sensor_name (ResourceName): The ``MovementSensor`` which will be used to check robot location
111- obstacles (Optional[Sequence[GeoObstacle]], optional ): Obstacles to be considered for motion planning. Defaults to None.
112- heading (Optional[float], optional ): Compass heading to achieve at the destination, in degrees [0-360). Defaults to None.
113- linear_meters_per_sec (Optional[float], optional ): Linear velocity to target when moving. Defaults to None.
114- angular_deg_per_sec (Optional[float], optional ): Angular velocity to target when turning. Defaults to None.
119+ obstacles (Optional[Sequence[GeoObstacle]]): Obstacles to be considered for motion planning. Defaults to None.
120+ heading (Optional[float]): Compass heading to achieve at the destination, in degrees [0-360). Defaults to None.
121+ linear_meters_per_sec (Optional[float]): Linear velocity to target when moving. Defaults to None.
122+ angular_deg_per_sec (Optional[float]): Angular velocity to target when turning. Defaults to None.
115123
116124 Returns:
117125 bool: Whether the request was successful
@@ -131,6 +139,57 @@ async def move_on_globe(
131139 response : MoveOnGlobeResponse = await self .client .MoveOnGlobe (request , timeout = timeout )
132140 return response .success
133141
142+ async def move_on_globe_new (
143+ self ,
144+ component_name : ResourceName ,
145+ destination : GeoPoint ,
146+ movement_sensor_name : ResourceName ,
147+ obstacles : Optional [Sequence [GeoObstacle ]] = None ,
148+ heading : Optional [float ] = None ,
149+ configuration : Optional [MotionConfiguration ] = None ,
150+ * ,
151+ extra : Optional [Mapping [str , ValueTypes ]] = None ,
152+ timeout : Optional [float ] = None ,
153+ ) -> str :
154+ """
155+ **Experimental**: use move_on_globe instead.
156+ Move a component to a specific latitude and longitude, using a ``MovementSensor`` to check the location.
157+
158+ ``move_on_globe_new()`` is non blocking, meaning the motion service will move the component to the destination
159+ GPS point after ``move_on_globe_new()`` returns.
160+
161+ Each successful ``move_on_globe_new()`` call retuns a unique ExectionID which you can use to identify all plans
162+ generated durring the ``move_on_globe_new()`` call.
163+
164+ You can monitor the progress of the ``move_on_globe_new()`` call by querying ``get_plan()`` and ``list_plan_statuses()``.
165+
166+ Args:
167+ component_name (ResourceName): The component to move
168+ destination (GeoPoint): The destination point
169+ movement_sensor_name (ResourceName): The ``MovementSensor`` which will be used to check robot location
170+ obstacles (Optional[Sequence[GeoObstacle]]): Obstacles to be considered for motion planning. Defaults to None.
171+ heading (Optional[float]): Compass heading to achieve at the destination, in degrees [0-360]. Defaults to None.
172+ linear_meters_per_sec (Optional[float]): Linear velocity to target when moving. Defaults to None.
173+ angular_deg_per_sec (Optional[float]): Angular velocity to target when turning. Defaults to None.
174+
175+ Returns:
176+ str: ExecutionID of the move_on_globe_new call, which can be used to track execution progress.
177+ """
178+ if extra is None :
179+ extra = {}
180+ request = MoveOnGlobeNewRequest (
181+ name = self .name ,
182+ component_name = component_name ,
183+ destination = destination ,
184+ movement_sensor_name = movement_sensor_name ,
185+ obstacles = obstacles ,
186+ heading = heading ,
187+ motion_configuration = configuration ,
188+ extra = dict_to_struct (extra ),
189+ )
190+ response : MoveOnGlobeNewResponse = await self .client .MoveOnGlobeNew (request , timeout = timeout )
191+ return response .execution_id
192+
134193 async def move_on_map (
135194 self ,
136195 component_name : ResourceName ,
@@ -140,7 +199,8 @@ async def move_on_map(
140199 extra : Optional [Mapping [str , ValueTypes ]] = None ,
141200 timeout : Optional [float ] = None ,
142201 ) -> bool :
143- """Move a component to a specific pose, using a ``SlamService`` for the SLAM map
202+ """
203+ Move a component to a specific pose, using a ``SlamService`` for the SLAM map
144204
145205 Args:
146206 component_name (ResourceName): The component to move
@@ -162,6 +222,117 @@ async def move_on_map(
162222 response : MoveOnMapResponse = await self .client .MoveOnMap (request , timeout = timeout )
163223 return response .success
164224
225+ async def stop_plan (
226+ self ,
227+ component_name : ResourceName ,
228+ * ,
229+ extra : Optional [Mapping [str , ValueTypes ]] = None ,
230+ timeout : Optional [float ] = None ,
231+ ):
232+ """**Experimental**
233+ Stop a component being moved by an in progress ``move_on_globe_new()`` call.
234+
235+ Args:
236+ component_name (ResourceName): The component to stop
237+
238+ Returns:
239+ None
240+ """
241+ if extra is None :
242+ extra = {}
243+
244+ request = StopPlanRequest (
245+ name = self .name ,
246+ component_name = component_name ,
247+ extra = dict_to_struct (extra ),
248+ )
249+ _ : StopPlanResponse = await self .client .StopPlan (request , timeout = timeout )
250+ return
251+
252+ async def get_plan (
253+ self ,
254+ component_name : ResourceName ,
255+ last_plan_only : bool = False ,
256+ execution_id : Optional [str ] = None ,
257+ * ,
258+ extra : Optional [Mapping [str , ValueTypes ]] = None ,
259+ timeout : Optional [float ] = None ,
260+ ) -> GetPlanResponse :
261+ """**Experimental**
262+ By default: returns the plan history of the most recent ``move_on_globe_new()`` call to move a component.
263+
264+ The plan history for executions before the most recent can be requested by providing an ExecutionID in the request.
265+
266+ Returns a result if both of the following conditions are met:
267+
268+ - the execution (call to ``move_on_globe_new()``) is still executing **or** changed state within the last 24 hours
269+ - the robot has not reinitialized
270+
271+ Plans never change.
272+
273+ Replans always create new plans.
274+
275+ Replans share the ExecutionID of the previously executing plan.
276+
277+ All repeated fields are in time ascending order.
278+
279+ Args:
280+ component_name (ResourceName): The component to stop
281+ last_plan_only (Optional[bool]): If supplied, the response will only return the last plan for the component / execution
282+ execution_id (Optional[str]): If supplied, the response will only return plans with the provided execution_id
283+
284+ Returns:
285+ ``GetPlanResponse`` (GetPlanResponse): The current PlanWithStatus & replan history which matches the request
286+ """
287+ if extra is None :
288+ extra = {}
289+
290+ if last_plan_only is None :
291+ last_plan_only = False
292+ request = GetPlanRequest (
293+ name = self .name ,
294+ component_name = component_name ,
295+ last_plan_only = last_plan_only ,
296+ execution_id = execution_id ,
297+ extra = dict_to_struct (extra ),
298+ )
299+ response : GetPlanResponse = await self .client .GetPlan (request , timeout = timeout )
300+ return response
301+
302+ async def list_plan_statuses (
303+ self ,
304+ only_active_plans : bool = False ,
305+ * ,
306+ extra : Optional [Mapping [str , ValueTypes ]] = None ,
307+ timeout : Optional [float ] = None ,
308+ ) -> ListPlanStatusesResponse :
309+ """**Experimental**
310+ Returns the statuses of plans created by `move_on_globe_new()` calls that meet at least one of the following
311+ conditions since the motion service initialized:
312+
313+ - the plan's status is in progress
314+ - the plan's status changed state within the last 24 hours
315+
316+ All repeated fields are in chronological order.
317+
318+ Args:
319+ only_active_plans (Optional[bool]): If supplied, the response will filter out any plans that are not executing
320+
321+ Returns:
322+ ``ListPlanStatusesResponse`` (ListPlanStatusesResponse): List of last known statuses with the
323+ associated IDs of all plans within the TTL ordered by timestamp in ascending order
324+ """
325+ if extra is None :
326+ extra = {}
327+
328+ request = ListPlanStatusesRequest (
329+ name = self .name ,
330+ only_active_plans = only_active_plans ,
331+ extra = dict_to_struct (extra ),
332+ )
333+ response : ListPlanStatusesResponse = await self .client .ListPlanStatuses (request , timeout = timeout )
334+ return response
335+
165336 async def get_pose (
166337 self ,
167338 component_name : ResourceName ,
0 commit comments