service_handler.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 
3 import subprocess
4 import os
5 import fcntl
6 import sys
7 import rospy
8 
9 from std_srvs.srv import Trigger, TriggerResponse
10 from vizanti.srv import GetNodeParameters, GetNodeParametersResponse
11 from vizanti.srv import LoadMap, LoadMapResponse, SaveMap, SaveMapResponse
12 from vizanti.srv import RecordRosbag, RecordRosbagResponse
13 from vizanti.srv import ManageNode, ListPackages, ListExecutables, ListExecutablesResponse, ListPackagesResponse
14 
15 
17 
18  def __init__(self):
19  rospy.init_node('vizanti_service_handler')
20 
21  self.proc = None
22  self.packages = self.get_packages()
23 
24  self.get_nodes_service = rospy.Service('vizanti/get_dynamic_reconfigure_nodes', Trigger,self. get_dynamic_reconfigure_nodes)
25  self.get_node_parameters_service = rospy.Service('vizanti/get_node_parameters', GetNodeParameters, self.get_node_parameters)
26 
27  self.load_map_service = rospy.Service('vizanti/load_map', LoadMap, self.load_map)
28  self.save_map_service = rospy.Service('vizanti/save_map', SaveMap, self.save_map)
29 
30  self.record_setup_service = rospy.Service('vizanti/bag/setup', RecordRosbag, self.recording_setup)
31  self.record_status_service = rospy.Service('vizanti/bag/status', Trigger, self.recording_status)
32 
33  self.node_kill_service = rospy.Service('vizanti/node/kill', ManageNode, self.node_kill)
34  self.node_start_service = rospy.Service('vizanti/node/start', ManageNode, self.node_start)
35  self.node_info_service = rospy.Service('vizanti/node/info', ManageNode, self.node_info)
36  self.node_info_service = rospy.Service('vizanti/roswtf', Trigger, self.roswtf)
37 
38  self.list_packages_service = rospy.Service('vizanti/list_packages', ListPackages ,self.list_packages_callback)
39  self.list_executables_service = rospy.Service('vizanti/list_executables', ListExecutables ,self.list_executables_callback)
40 
41  rospy.loginfo("Service handler ready.")
42 
43  def get_packages(self):
44  # Use rospack to get list of packages
45  cmd = ["rospack", "list"]
46  process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
47  output, error = process.communicate()
48  # Process output
49  lines = output.decode('utf-8').split('\n')
50  packages = {line.split()[0]: line.split()[1] for line in lines if line}
51  return packages
52 
53  def list_packages_callback(self, req):
54  return ListPackagesResponse(self.packages.keys())
55 
56  def list_executables_callback(self, req):
57  if req.package not in self.packages:
58  rospy.logerr("Package not found: " + req.package)
59  return ListExecutablesResponse([])
60 
61  path = self.packages[req.package]
62  # Use find to get list of executables
63  cmd_exec = ["find", path, "-type", "f", "!", "-name", "*.*", "-executable"]
64  cmd_python_and_launch = ["find", path, "-type", "f",
65  "(",
66  "-iname", "*.py", "-executable",
67  "-o",
68  "-iname", "*.launch",
69  ")"]
70 
71  process_exec = subprocess.Popen(cmd_exec, stdout=subprocess.PIPE)
72  process_python_and_launch = subprocess.Popen(cmd_python_and_launch, stdout=subprocess.PIPE)
73 
74  output_exec, error_exec = process_exec.communicate()
75  output_python_and_launch, error_python_and_launch = process_python_and_launch.communicate()
76 
77  # Process output
78  lines_exec = output_exec.decode('utf-8').split('\n')
79  lines_python_and_launch = output_python_and_launch.decode('utf-8').split('\n')
80 
81  executables = [line.split("/")[-1] for line in lines_exec if line]
82  python_and_launch_files = [line.split("/")[-1] for line in lines_python_and_launch if line]
83 
84  return ListExecutablesResponse(executables + python_and_launch_files)
85 
86  def node_kill(self, req):
87  try:
88  subprocess.call(['rosnode', 'kill', req.node])
89  return {'success': True, 'message': f'Killed node {req.node}'}
90  except Exception as e:
91  return {'success': False, 'message': str(e)}
92 
93  def node_start(self, req):
94  try:
95  args = req.node.split(" ")
96 
97  # Open /dev/null
98  devnull = open(os.devnull, 'w')
99 
100  # Set up the process to ignore the SIGTERM signal
101  def preexec():
102  os.setpgrp()
103  sys.stdin = open(os.devnull, 'r')
104  sys.stdout = open(os.devnull, 'w')
105  sys.stderr = open(os.devnull, 'w')
106 
107  subprocess.Popen(args, stdout=devnull, stderr=devnull, preexec_fn=preexec)
108 
109  return {'success': True, 'message': f'Started node {req.node}'}
110  except Exception as e:
111  return {'success': False, 'message': str(e)}
112 
113  def node_info(self, req):
114  try:
115  rosinfo = subprocess.check_output(["rosnode", "info", req.node]).decode('utf-8')
116  rosinfo = rosinfo.replace("--------------------------------------------------------------------------------","")
117  return {'success': True, 'message': rosinfo}
118  except Exception as e:
119  return {'success': False, 'message': str(e)}
120 
121  def roswtf(self, req):
122  try:
123  rosinfo = subprocess.check_output(["roswtf"]).decode('utf-8')
124  return {'success': True, 'message': rosinfo}
125  except Exception as e:
126  return {'success': False, 'message': str(e)}
127 
128 
129  def load_map(self, req):
130  file_path = os.path.expanduser(req.file_path)
131  topic = req.topic
132  try:
133  process = subprocess.Popen(["rosrun", "map_server", "map_server", file_path, "map:=" + topic, "__name:=vizanti_map_server"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
134  flags = fcntl.fcntl(process.stdout, fcntl.F_GETFL)
135  fcntl.fcntl(process.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK)
136 
137  # Wait for it to either fail or not
138  rospy.sleep(1)
139 
140  # Check if the process is still running
141  if process.poll() is not None:
142  # Process terminated, read the error output
143  error_output = process.stdout.read().decode('utf-8')
144  return LoadMapResponse(success=False, message="Map server failed to load the map: " + error_output)
145 
146  return LoadMapResponse(success=True, message="Map loaded successfully")
147  except Exception as e:
148  return LoadMapResponse(success=False, message=str(e))
149 
150  def save_map(self, req):
151  file_path = os.path.expanduser(req.file_path)
152  topic = req.topic
153  try:
154  process = subprocess.Popen(["rosrun", "map_server", "map_saver", "-f", file_path, "map:=" + topic, "__name:=vizanti_map_saver"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
155  flags = fcntl.fcntl(process.stdout, fcntl.F_GETFL)
156  fcntl.fcntl(process.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK)
157 
158  while True:
159  # Check if the process is still running
160  if process.poll() is not None:
161  break
162 
163  while True:
164  try:
165  line = process.stdout.readline()
166  if not line:
167  break
168 
169  if b"[ERROR]" in line:
170  process.terminate()
171  return SaveMapResponse(success=False, message="Map saver failed to save the map: " + line.decode('utf-8'))
172  except IOError:
173  break
174 
175  # Sleep for a short period of time to avoid excessive CPU usage
176  rospy.sleep(0.2)
177 
178  return SaveMapResponse(success=True, message="Map saved successfully")
179  except Exception as e:
180  return SaveMapResponse(success=False, message=str(e))
181 
183  list_nodes_output = subprocess.check_output(["rosrun", "dynamic_reconfigure", "dynparam", "list"]).decode('utf-8')
184  nodes_list = list_nodes_output.strip().split("\n")
185 
186  response = TriggerResponse()
187  response.success = True
188  response.message = "\n".join(nodes_list)
189 
190  return response
191 
192  def get_node_parameters(self, req):
193  node_params = subprocess.check_output(["rosrun", "dynamic_reconfigure", "dynparam", "get", req.node]).decode('utf-8')
194 
195  response = GetNodeParametersResponse()
196  response.parameters = node_params
197 
198  return response
199 
200  def recording_status(self, msg):
201  response = TriggerResponse()
202  response.success = self.proc is not None
203 
204  if response.success:
205  response.message = "Bag recording in progress..."
206  else:
207  response.message = "Bag recorder idle."
208 
209  return response
210 
211 
212  def recording_setup(self, req):
213 
214  response = RecordRosbagResponse()
215 
216  if req.start:
217  if self.proc is not None:
218  response.success = False
219  response.message = "Already recording, please stop the current recording first."
220  else:
221  command = ['rosbag', 'record', '-O']
222 
223  # Expand and add the path to the command
224  expanded_path = os.path.expanduser(req.path)
225  command.append(expanded_path)
226 
227  # Add the topics to the command
228  for topic in req.topics:
229  command.append(topic)
230 
231  # Use subprocess to start rosbag record in a new process
232  self.proc = subprocess.Popen(command)
233 
234  response.success = True
235  response.message = "Started recording rosbag with PID " + str(self.proc.pid) +" to path "+ str(expanded_path)
236  else:
237  if self.proc is None:
238  response.success = False
239  response.message = "No recording to stop."
240  else:
241  self.proc.terminate()
242  self.proc = None
243 
244  response.success = True
245  response.message = "Stopped recording rosbag."
246 
247  return response
248 
249 
250 handler = ServiceHandler()
251 rospy.spin()
service_handler.ServiceHandler.node_kill
def node_kill(self, req)
Definition: service_handler.py:86
service_handler.ServiceHandler.list_packages_service
list_packages_service
Definition: service_handler.py:38
service_handler.ServiceHandler.node_start
def node_start(self, req)
Definition: service_handler.py:93
service_handler.ServiceHandler.proc
proc
Definition: service_handler.py:21
service_handler.ServiceHandler.node_info_service
node_info_service
Definition: service_handler.py:35
service_handler.ServiceHandler.node_start_service
node_start_service
Definition: service_handler.py:34
service_handler.ServiceHandler.get_packages
def get_packages(self)
Definition: service_handler.py:43
service_handler.ServiceHandler.recording_setup
def recording_setup(self, req)
Definition: service_handler.py:212
service_handler.ServiceHandler.load_map
def load_map(self, req)
Definition: service_handler.py:129
service_handler.ServiceHandler.roswtf
def roswtf(self, req)
Definition: service_handler.py:121
service_handler.ServiceHandler.record_setup_service
record_setup_service
Definition: service_handler.py:30
service_handler.ServiceHandler.record_status_service
record_status_service
Definition: service_handler.py:31
service_handler.ServiceHandler.list_executables_callback
def list_executables_callback(self, req)
Definition: service_handler.py:56
service_handler.ServiceHandler.get_node_parameters
def get_node_parameters(self, req)
Definition: service_handler.py:192
service_handler.ServiceHandler.node_info
def node_info(self, req)
Definition: service_handler.py:113
service_handler.ServiceHandler.list_executables_service
list_executables_service
Definition: service_handler.py:39
service_handler.ServiceHandler.get_node_parameters_service
get_node_parameters_service
Definition: service_handler.py:25
service_handler.ServiceHandler.load_map_service
load_map_service
Definition: service_handler.py:27
service_handler.ServiceHandler.save_map_service
save_map_service
Definition: service_handler.py:28
service_handler.ServiceHandler.recording_status
def recording_status(self, msg)
Definition: service_handler.py:200
service_handler.ServiceHandler.__init__
def __init__(self)
Definition: service_handler.py:18
service_handler.ServiceHandler.packages
packages
Definition: service_handler.py:22
service_handler.ServiceHandler.get_nodes_service
get_nodes_service
Definition: service_handler.py:24
service_handler.ServiceHandler.node_kill_service
node_kill_service
Definition: service_handler.py:33
service_handler.ServiceHandler.get_dynamic_reconfigure_nodes
def get_dynamic_reconfigure_nodes(self, req)
Definition: service_handler.py:182
service_handler.ServiceHandler.save_map
def save_map(self, req)
Definition: service_handler.py:150
service_handler.ServiceHandler.list_packages_callback
def list_packages_callback(self, req)
Definition: service_handler.py:53
service_handler.ServiceHandler
Definition: service_handler.py:16


vizanti
Author(s): MoffKalast
autogenerated on Wed May 21 2025 02:34:06