2525# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2626# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2727# POSSIBILITY OF SUCH DAMAGE.
28+
2829from launch import LaunchDescription
2930from launch .actions import (
3031 DeclareLaunchArgument ,
31- EmitEvent ,
32+ ExecuteProcess ,
33+ OpaqueFunction ,
3234 RegisterEventHandler )
33- from launch .conditions import IfCondition
34- from launch .event_handlers import OnProcessStart
35- from launch .events import matches_action
36- from launch .substitutions import LaunchConfiguration
37- from launch_ros .actions import LifecycleNode
38- from launch_ros .event_handlers import OnStateTransition
39- from launch_ros .events .lifecycle import ChangeState
40- from lifecycle_msgs .msg import Transition
35+ from launch .event_handlers import OnProcessExit , OnProcessStart
36+ from launch .substitutions import FindExecutable , LaunchConfiguration
37+ from launch_ros .actions import LifecycleNode , Node
4138
4239
40+ def launch_setup (context , * args , ** kwargs ):
41+ # Apply context and type cast all LaunchConfiguration
42+ namespace = str (
43+ LaunchConfiguration ('namespace' ).perform (context ))
4344
44- def generate_launch_description ():
45- namespace = LaunchConfiguration ('namespace' )
46- interface = LaunchConfiguration ('interface' )
47- enable_can_fd = LaunchConfiguration ('enable_can_fd' )
48- interval_sec = LaunchConfiguration ('interval_sec' )
49- use_bus_time = LaunchConfiguration ('use_bus_time' )
50- filters = LaunchConfiguration ('filters' )
51- auto_configure = LaunchConfiguration ('auto_configure' )
52- auto_activate = LaunchConfiguration ('auto_activate' )
53- from_can_bus_topic = LaunchConfiguration ('from_can_bus_topic' )
45+ interface = str (
46+ LaunchConfiguration ('interface' ).perform (context ))
47+
48+ enable_can_fd = bool (
49+ LaunchConfiguration ('enable_can_fd' ).perform (context ) == 'true' )
50+
51+ interval_sec = float (
52+ LaunchConfiguration ('interval_sec' ).perform (context ))
53+
54+ use_bus_time = bool (
55+ LaunchConfiguration ('use_bus_time' ).perform (context ) == 'true' )
56+
57+ filters = str (
58+ LaunchConfiguration ('filters' ).perform (context ))
59+
60+ from_can_bus_topic = str (
61+ LaunchConfiguration ('from_can_bus_topic' ).perform (context ))
62+
63+ auto_configure = bool (
64+ LaunchConfiguration ('auto_configure' ).perform (context ) == 'true' )
65+
66+ auto_activate = bool (
67+ LaunchConfiguration ('auto_activate' ).perform (context ) == 'true' )
68+
69+ timeout = float (
70+ LaunchConfiguration ('timeout' ).perform (context ))
71+
72+ transition_attempts = int (
73+ LaunchConfiguration ('transition_attempts' ).perform (context ))
74+
75+ name = f'{ interface } _socket_can_receiver'
76+
77+ # SocketCAN receiver node
78+ node = LifecycleNode (
79+ package = 'ros2_socketcan' ,
80+ executable = 'socket_can_receiver_node_exe' ,
81+ name = name ,
82+ namespace = namespace ,
83+ parameters = [{
84+ 'interface' : interface ,
85+ 'enable_can_fd' : enable_can_fd == 'true' ,
86+ 'interval_sec' : interval_sec ,
87+ 'filters' : filters ,
88+ 'use_bus_time' : use_bus_time == 'true' ,
89+ }],
90+ remappings = [('from_can_bus' , from_can_bus_topic )],
91+ output = 'screen' )
92+
93+ # Wait for interface to be up
94+ wait_for_can_interface_proc = ExecuteProcess (
95+ cmd = [['until ' , FindExecutable (name = 'ip' ), ' link show ' , interface ,
96+ ' | ' , FindExecutable (name = 'grep' ), ' \" state UP\" ' , '; do sleep 1; done' ]],
97+ shell = True
98+ )
99+
100+ # Event to launch node once interface is up
101+ launch_node = RegisterEventHandler (
102+ event_handler = OnProcessExit (
103+ target_action = wait_for_can_interface_proc ,
104+ on_exit = node
105+ )
106+ )
107+
108+ # Activate launch
109+ activate_lifecycle_node = Node (
110+ name = f'activate_{ name } ' ,
111+ package = 'clearpath_ros2_socketcan_interface' ,
112+ executable = 'activate_lifecycle' ,
113+ namespace = namespace ,
114+ arguments = [
115+ '--namespace' , str (namespace ),
116+ '--node' , str (name ),
117+ '--auto_configure' , str (auto_configure ),
118+ '--auto_activate' , str (auto_activate ),
119+ '--timeout' , str (timeout ),
120+ '--transition_attempts' , str (transition_attempts )
121+ ]
122+ )
123+
124+ # Event to configure and activate node once node is up
125+ configure_event = RegisterEventHandler (
126+ event_handler = OnProcessStart (
127+ target_action = node ,
128+ on_start = [activate_lifecycle_node ],
129+ ),
130+ )
131+
132+ return [
133+ wait_for_can_interface_proc ,
134+ launch_node ,
135+ configure_event ,
136+ ]
54137
138+
139+ def generate_launch_description ():
55140 arg_namespace = DeclareLaunchArgument (
56141 'namespace' ,
57142 default_value = '' )
@@ -88,54 +173,16 @@ def generate_launch_description():
88173 'from_can_bus_topic' ,
89174 default_value = 'rx' )
90175
91- node = LifecycleNode (
92- package = 'ros2_socketcan' ,
93- executable = 'socket_can_receiver_node_exe' ,
94- name = 'socket_can_receiver' ,
95- namespace = namespace ,
96- parameters = [{
97- 'interface' : interface ,
98- 'enable_can_fd' : enable_can_fd ,
99- 'interval_sec' : interval_sec ,
100- 'filters' : filters ,
101- 'use_bus_time' : use_bus_time ,
102- }],
103- remappings = [('from_can_bus' , from_can_bus_topic )],
104- output = 'screen' )
105-
106- configure_event = RegisterEventHandler (
107- event_handler = OnProcessStart (
108- target_action = node ,
109- on_start = [
110- EmitEvent (
111- event = ChangeState (
112- lifecycle_node_matcher = matches_action (node ),
113- transition_id = Transition .TRANSITION_CONFIGURE ,
114- ),
115- ),
116- ],
117- ),
118- condition = IfCondition (auto_configure ),
119- )
176+ arg_timeout = DeclareLaunchArgument (
177+ 'timeout' ,
178+ default_value = '5.0' )
120179
121- activate_event = RegisterEventHandler (
122- event_handler = OnStateTransition (
123- target_lifecycle_node = node ,
124- start_state = 'configuring' ,
125- goal_state = 'inactive' ,
126- entities = [
127- EmitEvent (
128- event = ChangeState (
129- lifecycle_node_matcher = matches_action (node ),
130- transition_id = Transition .TRANSITION_ACTIVATE ,
131- ),
132- ),
133- ],
134- ),
135- condition = IfCondition (auto_activate ),
136- )
180+ arg_transition_attempts = DeclareLaunchArgument (
181+ 'transition_attempts' ,
182+ default_value = '3' )
137183
138184 ld = LaunchDescription ()
185+
139186 ld .add_action (arg_namespace )
140187 ld .add_action (arg_interface )
141188 ld .add_action (arg_enable_can_fd )
@@ -145,7 +192,7 @@ def generate_launch_description():
145192 ld .add_action (arg_auto_configure )
146193 ld .add_action (arg_auto_activate )
147194 ld .add_action (arg_from_can_bus_topic )
148- ld .add_action (node )
149- ld .add_action (configure_event )
150- ld .add_action (activate_event )
195+ ld .add_action (arg_timeout )
196+ ld .add_action (arg_transition_attempts )
197+ ld .add_action (OpaqueFunction ( function = launch_setup ) )
151198 return ld
0 commit comments