1+ from flexbe_core import EventState , Logger
2+ from flexbe_core .proxy import ProxyActionClient
3+ from gripper_msgs .action import Gripper
4+ from rclpy .duration import Duration
5+ class gripperMoveForward (EventState ):
6+ """
7+ This state navigates the robot to the given pose using NavigateToPose messages
8+
9+ Parameters
10+ -- timeout Maximum time allowed (seconds)
11+ -- action_topic Name of action to invoke
12+
13+ Outputs
14+ <= pose_reached Robot reached pose successful.
15+ <= failed Failed for some reason.
16+ <= canceled User canceled before completion.
17+ <= timeout The action has timed out.
18+
19+ User data
20+ ># frame Frame of the goal pose
21+ ># x_target X value of goal pose
22+ ># y_target Y value of goal pose
23+ ># z_target z value of goal pose
24+ """
25+ def __init__ (self , timeout ,action_topic ):
26+
27+ super ().__init__ (outcomes = ['pose_reached' , 'failed' , 'canceled' , 'timeout' ],
28+ input_keys = ['frame' , 'x_target' ,'y_target' , 'z_target' ],
29+ output_keys = [])
30+ self ._timeout = Duration (seconds = timeout )
31+ self ._timeout_sec = timeout
32+ self ._topic = action_topic
33+
34+ # Create the action client when building the behavior.
35+ # Using the proxy client provides asynchronous access to the result and status
36+ # and makes sure only one client is used, no matter how often this state is used in a behavior.
37+ ProxyActionClient .initialize (gripperMoveForward ._node )
38+
39+ self ._client = ProxyActionClient ({self ._topic : Gripper },
40+ wait_duration = 0.0 ) # pass required clients as dict (topic: type)
41+
42+ # It may happen that the action client fails to send the action goal.
43+ self ._error = False
44+ self ._return = None # Retain return value in case the outcome is blocked by operator
45+ self ._start_time = None
46+
47+ def execute (self , userdata ):
48+ """
49+ Call this method periodically while the state is active.
50+
51+ If no outcome is returned, the state will stay active.
52+ """
53+ # Check if the client failed to send the goal.
54+ if self ._error :
55+ return 'failed'
56+
57+ if self ._return is not None :
58+ # Return prior outcome in case transition is blocked by autonomy level
59+ return self ._return
60+
61+ if self ._node .get_clock ().now ().nanoseconds - self ._start_time .nanoseconds > self ._target_time .nanoseconds :
62+ # Normal completion, do not bother repeating the publish
63+ # We won't bother publishing a 0 command unless blocked (above)
64+ # so that we can chain multiple motions together
65+ self ._return = 'timeout'
66+ return 'timeout'
67+
68+ # Normal operation
69+ if self ._cmd_topic :
70+ Logger .localinfo (f"{ self ._name } : { self ._twist } " ) # For initial debugging
71+ self ._pub .publish (self ._cmd_topic , self ._twist )
72+
73+ return None
74+
75+ def on_enter (self , userdata ):
76+ """
77+ Call this method when the state becomes active.
78+
79+ i.e. a transition from another state to this one is taken.
80+ """
81+ self ._error = False
82+ self ._return = None # reset the completion flag
83+ if 'x_target' not in userdata :
84+ self ._error = True
85+ Logger .logwarn ("MoveTotState requires userdata.target_x key!" )
86+ return
87+
88+ if 'y_target' not in userdata :
89+ self ._error = True
90+ Logger .logwarn ("MoveTotState requires userdata.target_y key!" )
91+ return
92+
93+ if 'z_target' not in userdata :
94+ self ._error = True
95+ Logger .logwarn ("MoveTotState requires userdata.target_yaw key!" )
96+ return
97+ # create goal msg
98+ goal = Gripper .Goal ()
99+ # Recording the start time to set rotation duration output
100+ self ._start_time = self ._node .get_clock ().now ()
101+ # goal.pose.header.stamp = self._start_time
102+
103+ Logger .logwarn ("frame_id type %s. Expects a string." , type (userdata .frame ).__name__ )
104+ Logger .logwarn ("frame_id = %s." , userdata .frame )
105+ Logger .logwarn ("target_x is %s. Expects a float." , type (userdata .x_target ).__name__ )
106+ Logger .logwarn ("target_x is %f." , userdata .x_target )
107+ Logger .logwarn ("target_y is %s. Expects a float." , type (userdata .y_target ).__name__ )
108+ Logger .logwarn ("target_y is %f." , userdata .y_target )
109+ Logger .logwarn ("target_yaw is %s. Expects a float." , type (userdata .z_target ).__name__ )
110+ Logger .logwarn ("target_yaw is %f." , userdata .z_target )
111+
112+ if isinstance (userdata .frame , str ):
113+ goal .frame = userdata .frame
114+ else :
115+ Logger .logwarn (f"Invalid frame_id type: { type (userdata .frame ).__name__ } . Expected a string." )
116+ self ._error = True
117+ return
118+
119+
120+ if isinstance (userdata .x_target , float ):
121+ goal .x_target = userdata .x_target
122+ else :
123+ Logger .logwarn (f"Invalid target_x type: { type (userdata .x_target ).__name__ } . Expected float." )
124+ self ._error = True
125+ return
126+
127+ if isinstance (userdata .y_target , float ):
128+ goal .y_target = userdata .y_target
129+ else :
130+ Logger .logwarn (f"Invalid target_y type: { type (userdata .y_target ).__name__ } . Expected float." )
131+ self ._error = True
132+ return
133+
134+ if isinstance (userdata .z_target , float ):
135+ goal .z_target = userdata .z_target
136+ else :
137+ Logger .logwarn (f"Invalid target_z type: { type (userdata .z_target ).__name__ } . Expected float." )
138+ self ._error = True
139+ return
140+
141+ # Send the goal.
142+ try :
143+ self ._client .send_goal (self ._topic , goal , wait_duration = self ._timeout_sec )
144+ except Exception as exc : # pylint: disable=W0703
145+ # Since a state failure not necessarily causes a behavior failure,
146+ # it is recommended to only print warnings, not errors.
147+ # Using a linebreak before appending the error log enables the operator to collapse details in the GUI.
148+ Logger .logwarn (f"Failed to send the NavigateToPose command:\n { type (exc )} - { exc } " )
149+ self ._error = True
150+
151+ def on_exit (self , userdata ):
152+ # Make sure that the action is not running when leaving this state.
153+ # A situation where the action would still be active is for example when the operator manually triggers an outcome.
154+
155+ if not self ._client .has_result (self ._topic ):
156+ self ._client .cancel (self ._topic )
157+ Logger .loginfo ('Cancelled active action goal.' )
0 commit comments