10 from __future__
import print_function
23 from std_srvs.srv
import Trigger, TriggerRequest, TriggerResponse
24 from mavros_msgs.msg
import State
29 Base class for event handlers 33 'name',
'events',
'actions',
'lock',
36 def __init__(self, name, events=[], actions=[]):
40 self.
lock = threading.RLock()
46 if event
not in self.
events:
49 idx = self.events.index(event)
52 if hasattr(self,
'action_' + action):
53 getattr(self,
'action_' + action)()
55 rospy.logerr(
"Misconfiguration of %s, there no action '%s'",
59 raise NotImplementedError
72 'process',
'command',
'args',
'logfile',
75 def __init__(self, name, command, args=[], logfile=None, events=[], actions=[]):
76 super(ShellHandler, self).
__init__(name, events, actions)
85 rospy.loginfo(
"%s: process still active, terminating before run", str(self))
88 rospy.loginfo(
"%s: starting...", self)
93 child_stderr = subprocess.STDOUT
95 child_stdout = subprocess.PIPE
96 child_stderr = subprocess.PIPE
98 if hasattr(child_stdout,
'write'):
99 child_stdout.write(
"\n--- run cut: %s ---\n" % time.ctime())
101 self.
process = subprocess.Popen(args, stdout=child_stdout, stderr=child_stderr,
102 close_fds=
True, preexec_fn=os.setsid)
104 poll_result = self.process.poll()
105 if poll_result
is None or poll_result == 0:
106 rospy.loginfo(
"%s: started, pid: %s", self, self.process.pid)
108 rospy.logerr(
"Failed to start '%s'", self)
116 rospy.loginfo(
"%s: stoppig...", self)
119 pid = self.process.pid
120 pgid = os.getpgid(pid)
122 def poll_timeout(timeout):
123 timeout_t = time.time() + timeout
124 retcode = self.process.poll()
125 while time.time() < timeout_t
and retcode
is None:
127 retcode = self.process.poll()
131 rospy.loginfo(
"%s: sending SIGINT to pid [%s] pgid [%s]", self, pid, pgid)
132 os.killpg(pgid, signal.SIGINT)
134 retcode = poll_timeout(15.0)
136 rospy.logwarn(
"%s: escalating to SIGTERM", self)
137 os.killpg(pgid, signal.SIGTERM)
139 retcode = poll_timeout(2.0)
141 rospy.logerr(
"%s: escalating to SIGKILL, may still be running", self)
143 os.killpg(pgid, signal.SIGKILL)
144 except OSError
as ex:
145 rospy.logerr(
"%s: %s", self, ex)
147 if retcode
is not None:
148 rospy.loginfo(
"%s: process (pid %s) terminated, exit code: %s",
156 poll_result = self.process.poll()
157 if poll_result
is not None:
158 rospy.loginfo(
"%s: process (pid %s) terminated, exit code: %s",
159 self, self.process.pid, poll_result)
184 params = rospy.get_param(
'~')
185 if not isinstance(params, dict):
186 raise KeyError(
"bad configuration")
187 except KeyError
as e:
188 rospy.logerr(
'Config error: %s', e)
192 for k, v
in params.iteritems():
200 rospy.logwarn(
"Skipping unknown section: %s", k)
203 rospy.loginfo(
"Known events: %s",
', '.join(self.
known_events))
207 rospy.logwarn(
"%s: unknown event: %s", h.name, evt)
216 rospy.logdebug(
"Loading trigger: %s", name)
221 return TriggerResponse(success=
True)
224 self.known_events.append(name)
225 self.
triggers[name] = rospy.Service(params[
'service'], Trigger, gen_cb(name))
226 rospy.loginfo(
"Trigger: %s (%s)", name, params[
'service'])
229 rospy.logdebug(
"Loading shell: %s", name)
234 return os.path.expanduser(os.path.expandvars(p))
236 args = params[
'shell']
237 if not isinstance(args, list):
238 args = shlex.split(args)
240 command = expandpath(args[0])
243 logfile = params.get(
'logfile')
245 logfile = expandpath(logfile)
247 rospy.loginfo(
"Shell: %s (%s)", name,
' '.join([command] + [repr(v)
for v
in args]))
249 rospy.loginfo(
"Log: %s -> %s", name, logfile)
251 handler =
ShellHandler(name, command, args, logfile, events, actions)
252 self.handlers.append(handler)
258 raise ValueError(
"event and action fileds has different size!")
262 if isinstance(str_or_list, list):
263 return [it.strip()
for it
in str_or_list]
266 for it
in str_or_list.split():
267 ret.extend([v.strip()
for v
in it.split(
',')
if v])
271 rospy.logdebug(
'Event: %s', event)
275 except Exception
as ex:
277 rospy.logerr(
"Event %s -> %s exception: %s", event, h, ex)
278 rospy.logerr(traceback.format_exc())
282 rospy.logwarn(
"No event handlers defined, terminating.")
285 rate = rospy.Rate(1.0)
286 while not rospy.is_shutdown():
295 self(
'armed' if msg.armed
else 'disarmed')
299 parser = argparse.ArgumentParser(
300 description=
"This node launch/terminate processes on events.")
301 parser.add_argument(
'-n',
'--mavros-ns', help=
"ROS node namespace", default=mavros.DEFAULT_NAMESPACE)
303 args = parser.parse_args(rospy.myargv(argv=sys.argv)[1:])
305 rospy.init_node(
"event_launcher")
308 rospy.loginfo(
"Starting event launcher...")
def _get_evt_act(self, params)
def __init__(self, name, command, args=[], logfile=None, events=[], actions=[])
def _load_trigger(self, name, params)
def _param_to_list(self, str_or_list)
def _load_shell(self, name, params)
def __init__(self, name, events=[], actions=[])
def __call__(self, event)
def mavros_state_cb(self, msg)
def set_namespace(ns=DEFAULT_NAMESPACE)
def __call__(self, event)