8181from .spot_images import SpotImages
8282from .spot_world_objects import SpotWorldObjects
8383
84- from .wrapper_helpers import RobotCommandData , RobotState
84+ from .wrapper_helpers import RobotCommandData , RobotState , ClaimAndPowerDecorator
8585
8686"""Service name for getting pointcloud of VLP16 connected to Spot Core"""
8787point_cloud_sources = ["velodyne-point-cloud" ]
@@ -376,38 +376,6 @@ def _start_query(self):
376376 pass
377377
378378
379- def try_claim (func = None , * , power_on = False ):
380- """
381- Decorator which tries to acquire the lease before executing the wrapped function
382-
383- the _func=None and * args are required to allow this decorator to be used with or without arguments
384-
385- Args:
386- func: Function that is being wrapped
387- power_on: If true, power on after claiming the lease
388-
389- Returns:
390- Decorator which will wrap the decorated function
391- """
392- # If this decorator is being used without the power_on arg, return it as if it was called with that arg specified
393- if func is None :
394- return functools .partial (try_claim , power_on = power_on )
395-
396- @functools .wraps (func )
397- def wrapper_try_claim (self , * args , ** kwargs ):
398- if self ._get_lease_on_action :
399- if power_on :
400- # Power on is also wrapped by this decorator so if we request power on the lease will also be claimed
401- response = self .power_on ()
402- else :
403- response = self .claim ()
404- if not response [0 ]:
405- return response
406- return func (self , * args , ** kwargs )
407-
408- return wrapper_try_claim
409-
410-
411379class SpotWrapper :
412380 """Generic wrapper class to encompass release 1.1.4 API features as well as maintaining leases automatically"""
413381
@@ -456,7 +424,10 @@ def __init__(
456424 self ._rates = rates or {}
457425 self ._callbacks = callbacks or {}
458426 self ._use_take_lease = use_take_lease
459- self ._get_lease_on_action = get_lease_on_action
427+ self ._claim_decorator = ClaimAndPowerDecorator (
428+ self .power_on , self .claim , get_lease_on_action
429+ )
430+ self .decorate_functions ()
460431 self ._continually_try_stand = continually_try_stand
461432 self ._rgb_cameras = rgb_cameras
462433 self ._frame_prefix = ""
@@ -650,6 +621,7 @@ def __init__(
650621 self ._manipulation_api_client ,
651622 self ._robot_state_client ,
652623 MAX_COMMAND_DURATION ,
624+ self ._claim_decorator ,
653625 )
654626 else :
655627 self ._spot_arm = None
@@ -663,6 +635,7 @@ def __init__(
663635 self ._command_data ,
664636 self ._docking_client ,
665637 self ._robot_command_client ,
638+ self ._claim_decorator ,
666639 )
667640
668641 self ._spot_graph_nav = SpotGraphNav (
@@ -707,6 +680,40 @@ def __init__(
707680 self ._robot_id = None
708681 self ._lease = None
709682
683+ def decorate_functions (self ):
684+ """
685+ Many of the functions in the wrapper need to have the lease claimed and the robot powered on before they will
686+ function. The TryClaimDecorator object includes a decorator which is the mechanism we use to make sure that
687+ is the case, assuming the get_lease_on_action variable is true. Otherwise, it is up to the user to ensure
688+ that the lease is claimed and the power is on before running commands, otherwise the commands will fail.
689+ """
690+ decorated_funcs = [
691+ self .stop ,
692+ self .self_right ,
693+ self .sit ,
694+ self .simple_stand ,
695+ self .stand ,
696+ self .battery_change_pose ,
697+ self .velocity_cmd ,
698+ self .trajectory_cmd ,
699+ self .navigate_to ,
700+ self ._navigate_to ,
701+ self ._navigate_route ,
702+ self .execute_dance ,
703+ self ._robot_command ,
704+ self ._manipulation_request ,
705+ ]
706+ decorated_funcs_no_power = [
707+ self .stop ,
708+ self .power_on ,
709+ self .safe_power_off ,
710+ self .toggle_power ,
711+ ]
712+
713+ self ._claim_decorator .decorate_functions (
714+ self , decorated_funcs , decorated_funcs_no_power
715+ )
716+
710717 @staticmethod
711718 def authenticate (
712719 robot : Robot , username : str , password : str , logger : logging .Logger
@@ -1147,7 +1154,6 @@ def _manipulation_request(
11471154 self ._logger .error (f"Unable to execute manipulation command: { e } " )
11481155 return False , str (e ), None
11491156
1150- @try_claim
11511157 def stop (self ) -> typing .Tuple [bool , str ]:
11521158 """
11531159 Stop any action the robot is currently doing.
@@ -1159,7 +1165,6 @@ def stop(self) -> typing.Tuple[bool, str]:
11591165 response = self ._robot_command (RobotCommandBuilder .stop_command ())
11601166 return response [0 ], response [1 ]
11611167
1162- @try_claim (power_on = True )
11631168 def self_right (self ) -> typing .Tuple [bool , str ]:
11641169 """
11651170 Have the robot self-right.
@@ -1170,7 +1175,6 @@ def self_right(self) -> typing.Tuple[bool, str]:
11701175 response = self ._robot_command (RobotCommandBuilder .selfright_command ())
11711176 return response [0 ], response [1 ]
11721177
1173- @try_claim (power_on = True )
11741178 def sit (self ) -> typing .Tuple [bool , str ]:
11751179 """
11761180 Stop the robot's motion and sit down if able.
@@ -1183,7 +1187,6 @@ def sit(self) -> typing.Tuple[bool, str]:
11831187 self .last_sit_command = response [2 ]
11841188 return response [0 ], response [1 ]
11851189
1186- @try_claim (power_on = True )
11871190 def simple_stand (self , monitor_command : bool = True ) -> typing .Tuple [bool , str ]:
11881191 """
11891192 If the e-stop is enabled, and the motor power is enabled, stand the robot up.
@@ -1198,7 +1201,6 @@ def simple_stand(self, monitor_command: bool = True) -> typing.Tuple[bool, str]:
11981201 self .last_stand_command = response [2 ]
11991202 return response [0 ], response [1 ]
12001203
1201- @try_claim (power_on = True )
12021204 def stand (
12031205 self ,
12041206 monitor_command : bool = True ,
@@ -1242,7 +1244,6 @@ def stand(
12421244 self .last_stand_command = response [2 ]
12431245 return response [0 ], response [1 ]
12441246
1245- @try_claim (power_on = True )
12461247 def battery_change_pose (self , dir_hint : int = 1 ) -> typing .Tuple [bool , str ]:
12471248 """
12481249 Put the robot into the battery change pose
@@ -1260,7 +1261,6 @@ def battery_change_pose(self, dir_hint: int = 1) -> typing.Tuple[bool, str]:
12601261 return response [0 ], response [1 ]
12611262 return False , "Call sit before trying to roll over"
12621263
1263- @try_claim
12641264 def safe_power_off (self ) -> typing .Tuple [bool , str ]:
12651265 """
12661266 Stop the robot's motion and sit if possible. Once sitting, disable motor power.
@@ -1288,7 +1288,6 @@ def clear_behavior_fault(
12881288 except Exception as e :
12891289 return False , f"Exception while clearing behavior fault: { e } " , None
12901290
1291- @try_claim
12921291 def power_on (self ) -> typing .Tuple [bool , str ]:
12931292 """
12941293 Enable the motor power if e-stop is enabled.
@@ -1325,7 +1324,6 @@ def get_mobility_params(self) -> spot_command_pb2.MobilityParams:
13251324 """Get mobility params"""
13261325 return self ._mobility_params
13271326
1328- @try_claim
13291327 def velocity_cmd (
13301328 self , v_x : float , v_y : float , v_rot : float , cmd_duration : float = 0.125
13311329 ) -> typing .Tuple [bool , str ]:
@@ -1353,7 +1351,6 @@ def velocity_cmd(
13531351 self .last_velocity_command_time = end_time
13541352 return response [0 ], response [1 ]
13551353
1356- @try_claim
13571354 def trajectory_cmd (
13581355 self ,
13591356 goal_x : float ,
@@ -1462,7 +1459,6 @@ def get_manipulation_command_feedback(self, cmd_id):
14621459 manipulation_api_feedback_request = feedback_request
14631460 )
14641461
1465- @try_claim
14661462 def toggle_power (self , should_power_on ):
14671463 """Power the robot on/off dependent on the current power state."""
14681464 is_powered_on = self .check_is_powered_on ()
@@ -1500,7 +1496,6 @@ def check_is_powered_on(self) -> bool:
15001496 self ._powered_on = power_state .motor_power_state == power_state .STATE_ON
15011497 return self ._powered_on
15021498
1503- @try_claim
15041499 def execute_dance (self , data ):
15051500 if self ._is_licensed_for_choreography :
15061501 return self ._spot_dance .execute_dance (data )
0 commit comments