00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 import roslib; roslib.load_manifest('starmac_tui')
00034 import rospy
00035 import curses
00036 from starmac_tui.curses_app import CursesApp
00037
00038 from rosgraph_msgs.msg import Log
00039 from flyer_controller.msg import controller_status
00040 from flyer_controller.msg import controller_cmd
00041 from flyer_controller.msg import control_mode_hover_info
00042 from flyer_controller.msg import control_mode_cmd
00043 from nav_msgs.msg import Odometry
00044
00045 from asctec_hl_comm.msg import mav_status
00046
00047 class Bag:
00048 pass
00049
00050 spinner = "\|/-"
00051 POS_FMT = "% 7.3f"
00052
00053 class StarmacTuiNode(CursesApp):
00054
00055 def __init__(self):
00056 rospy.init_node('tui')
00057 self.init_curses()
00058 self.spinner_state = 0
00059 self.blink_state = True
00060 self.scroll_state = 0
00061 self.scroll_incr = 1
00062 self.cur_page = 0
00063 self.prev_page = 0
00064 self.page_names = ('Home', 'Control', 'Mode', 'Health', 'Console', 'Help')
00065 self.num_pages = len(self.page_names)
00066 self.msg_buf = []
00067 self.moving_hover_point = False
00068 self.moving_yaw_in_hover = False
00069 self.latest = Bag()
00070 self.latest.controller_status = controller_status()
00071 self.latest.estimator_output = Odometry()
00072 self.latest.hover_info = control_mode_hover_info()
00073 self.latest.mav_status = mav_status()
00074 self.init_publishers()
00075 self.hover_info_sub = None
00076 self.hover_cmd_pub = None
00077 self.init_subscribers()
00078
00079
00080 def init_publishers(self):
00081 self.controller_cmd_pub = rospy.Publisher('controller/cmd', controller_cmd)
00082
00083 def init_subscribers(self):
00084 self.rosout_sub = rospy.Subscriber('/rosout_agg', Log, self.rosout_cb)
00085 self.controller_status_sub = rospy.Subscriber('controller/status', controller_status, self.controller_status_cb)
00086 self.est_sub = rospy.Subscriber('estimator/output', Odometry, self.est_cb)
00087 self.mav_status_sub = rospy.Subscriber('fcu/status', mav_status, self.mav_status_cb)
00088
00089 def est_cb(self, msg):
00090 self.latest.estimator_output = msg
00091
00092 def rosout_cb(self, msg):
00093 self.msg_buf.append((msg.level, msg.msg))
00094
00095 def controller_status_cb(self, msg):
00096 self.latest.controller_status = msg
00097
00098 def hover_info_cb(self, msg):
00099 self.latest.hover_info = msg
00100
00101 def mav_status_cb(self, msg):
00102 self.latest.mav_status = msg
00103
00104 def volts_color(self, volts):
00105 if volts < 4.0:
00106 vc = self.colors.blue + curses.A_BOLD
00107 elif volts < 10.0:
00108 if self.blink_state:
00109 vc = self.colors.red + curses.A_BOLD + curses.A_BLINK
00110 else:
00111 vc = self.colors.black
00112 curses.beep()
00113 elif volts < 11.0:
00114 vc = self.colors.yellow + curses.A_BOLD
00115 else:
00116 vc = self.colors.green
00117 return vc
00118
00119 def update(self):
00120 self.blink_state = not self.blink_state
00121 maxy, maxx = self.stdscr.getmaxyx()
00122 addstr = self.stdscr.addstr
00123
00124 if self.cur_page != self.prev_page:
00125 self.stdscr.clear()
00126 self.stdscr.setscrreg(0,maxy-2)
00127
00128
00129 volts = self.latest.mav_status.battery_voltage
00130 if self.cur_page == self.page_names.index('Home'):
00131
00132 y, x = self.stdscr.getyx()
00133 self.update_alt(0, 0)
00134
00135 cur_mode = self.latest.controller_status.active_mode
00136 self.update_mode(1, cur_mode)
00137
00138 self.update_pos(2)
00139
00140 elif self.cur_page == self.page_names.index('Control'):
00141
00142 if self.latest.controller_status.state == controller_status.STATE_OPERATIONAL:
00143 state = 'Operational'
00144 color = self.colors.green
00145 elif self.latest.controller_status.state == controller_status.STATE_STANDBY:
00146 state = 'Standby'
00147 color = self.colors.yellow
00148 elif self.latest.controller_status.state == controller_status.STATE_INITIALIZING:
00149 state = 'Initializing'
00150 color = self.colors.yellow
00151 elif self.latest.controller_status.state == controller_status.STATE_ERROR:
00152 state = 'Off'
00153 color = self.colors.blue
00154 else:
00155 state = '?'
00156 color = self.colors.red
00157 addstr(0,0,state,color)
00158 self.stdscr.clrtoeol()
00159
00160
00161 addstr(1,0,'Active:',self.colors.cyan)
00162 cur_mode = self.latest.controller_status.active_mode
00163 self.update_mode(2, cur_mode)
00164 addstr(3,0,'Standby:',self.colors.cyan)
00165 for i in range(len(self.latest.controller_status.standby_modes)):
00166 addstr(4+i,0,'%d %s' % (i+1, self.latest.controller_status.standby_modes[i]))
00167 self.stdscr.clrtoeol()
00168 self.stdscr.clrtobot()
00169
00170
00171 elif self.cur_page == self.page_names.index('Mode'):
00172
00173 addstr(0,0,'Active:',self.colors.cyan)
00174 cur_mode = self.latest.controller_status.active_mode
00175 self.update_mode(1, cur_mode)
00176
00177 if cur_mode == "idle":
00178 pass
00179 elif cur_mode == "attitude":
00180 pass
00181 elif cur_mode == "hover":
00182 if self.hover_info_sub is None:
00183 self.hover_info_sub = rospy.Subscriber('control_mode_hover/info', control_mode_hover_info, self.hover_info_cb)
00184 self.hover_cmd_pub = rospy.Publisher('control_mode_hover/cmd', control_mode_cmd)
00185 self.stdscr.clear()
00186 else:
00187 row = 2
00188 self.add_field('Pt: ', self.latest.hover_info.hover_point, row, 0)
00189 self.add_field(' Cmd Act Err [m]', '', row+1, 0)
00190 cmd_act_err_x = (POS_FMT % self.latest.hover_info.north_cmd) + " " \
00191 + (POS_FMT % self.latest.estimator_output.pose.pose.position.x) + " " \
00192 + (POS_FMT % self.latest.hover_info.north_err)
00193 self.add_field('X:', cmd_act_err_x, row+2, 0)
00194 cmd_act_err_y = (POS_FMT % self.latest.hover_info.east_cmd) + " " \
00195 + (POS_FMT % self.latest.estimator_output.pose.pose.position.y) + " " \
00196 + (POS_FMT % self.latest.hover_info.east_err)
00197 self.add_field('Y:', cmd_act_err_y, row+3, 0)
00198 cmd_act_err_yaw = (POS_FMT % self.latest.hover_info.yaw_cmd) + " " \
00199 + (POS_FMT % 0.0) + " " \
00200 + (POS_FMT % self.latest.hover_info.yaw_err)
00201 self.add_field('Yaw:', cmd_act_err_yaw, row+4, 0)
00202 row += 5
00203 if self.moving_hover_point:
00204 if self.blink_state:
00205 color = self.colors.yellow
00206 else:
00207 color = self.colors.black
00208 addstr(row, 0, 'MOVING HOVER POINT', color)
00209 self.stdscr.clrtoeol()
00210 else:
00211 addstr(row, 0, 'm - move hover pt')
00212 self.stdscr.clrtoeol()
00213 if self.moving_yaw_in_hover:
00214 addstr(row+1, 0, 'ADJUSTING YAW', self.colors.yellow)
00215 self.stdscr.clrtoeol()
00216 else:
00217 addstr(row+1, 0, 'y - adjust yaw')
00218 self.stdscr.clrtoeol()
00219
00220 elif self.cur_page == self.page_names.index('Health'):
00221 addstr(0,0,'Bat:')
00222 self.update_voltage(0,5,volts)
00223
00224 elif self.cur_page == self.page_names.index('Console'):
00225 for level, msg in self.msg_buf:
00226 self.print_console(maxy-2, level, msg)
00227 self.stdscr.scrollok(True)
00228 self.stdscr.scroll()
00229 self.stdscr.scrollok(False)
00230
00231 self.msg_buf = []
00232
00233 elif self.cur_page == self.page_names.index('Help'):
00234 addstr(0,0,'Instructions:')
00235 addstr(2,0,'l/r arrows: chg. page')
00236 addstr(4,0,'q: quit')
00237 else:
00238 pass
00239
00240
00241
00242 for i in range(self.num_pages):
00243 if i == self.cur_page:
00244 color = self.colors.black_on_white
00245 else:
00246 color = self.colors.white
00247 addstr(maxy-1,i,str(i),color)
00248
00249 if self.cur_page != self.page_names.index('Console'):
00250 if len(self.msg_buf) > 0:
00251 self.print_console(maxy-2, self.msg_buf[-1][0], self.msg_buf[-1][1])
00252
00253 addstr(maxy-1,self.num_pages,'-'*(maxx-self.num_pages-1))
00254 addstr(maxy-1,self.num_pages+2,self.page_names[self.cur_page], self.colors.cyan)
00255
00256
00257 self.update_voltage(maxy-1,maxx-8,volts)
00258
00259
00260 self.stdscr.addch(maxy-1,maxx-2,ord(spinner[self.spinner_state]))
00261 self.spinner_state = (self.spinner_state + 1) % len(spinner)
00262
00263
00264 self.prev_page = self.cur_page
00265
00266
00267 def update_pos(self, row):
00268 x = self.latest.estimator_output.pose.pose.position.x
00269 y = self.latest.estimator_output.pose.pose.position.y
00270 self.add_field("X:", POS_FMT % x, row, 0)
00271
00272
00273 self.stdscr.addstr(" Y:", self.colors.cyan)
00274 self.stdscr.addstr(POS_FMT % y)
00275 self.stdscr.clrtoeol()
00276
00277 def update_alt(self, row, col):
00278 alt = -self.latest.estimator_output.pose.pose.position.z
00279 self.stdscr.addstr(row, col, "Alt:", self.colors.cyan)
00280 self.stdscr.addstr(POS_FMT % alt)
00281 self.stdscr.clrtoeol()
00282
00283 def update_voltage(self, row, col, volts):
00284 self.stdscr.addstr(row,col,"%-3.2fV" % volts, self.volts_color(volts))
00285
00286 def update_mode(self, row, mode):
00287 maxy, maxx = self.stdscr.getmaxyx()
00288 self.stdscr.addstr(row,0,mode[self.scroll_state:self.scroll_state+maxx])
00289 if len(mode) > maxx:
00290 if (self.scroll_incr == 1 and self.scroll_state == len(mode) - maxx) \
00291 or (self.scroll_incr == -1 and self.scroll_state == 0):
00292 self.scroll_incr = -self.scroll_incr
00293 self.scroll_state = self.scroll_state + self.scroll_incr
00294 else:
00295 self.stdscr.clrtoeol()
00296
00297 def print_console(self, row, level, msg):
00298 maxy, maxx = self.stdscr.getmaxyx()
00299 if level == Log.INFO:
00300 color = self.colors.white
00301 elif level == Log.WARN:
00302 color = self.colors.yellow
00303 elif level == Log.ERROR:
00304 color = self.colors.red
00305 elif level == Log.DEBUG:
00306 color = self.colors.cyan
00307 self.stdscr.addstr(row,0,' '*(maxx-1))
00308 self.stdscr.addstr(row,0,msg.replace('\n',r'\n')[:maxx-1], color)
00309
00310
00311 def mode_to_active(self, mode_num):
00312 if mode_num < len(self.latest.controller_status.standby_modes):
00313 new_mode = self.latest.controller_status.standby_modes[mode_num]
00314 cmd = controller_cmd()
00315 cmd.cmd = "control_mode to_active %s" % new_mode
00316 self.controller_cmd_pub.publish(cmd)
00317
00318 def toggle_modify_hover(self):
00319 if self.hover_cmd_pub is not None:
00320 msg = control_mode_cmd()
00321 if self.moving_hover_point:
00322 msg.cmd = "adjust hover_point off"
00323 else:
00324 msg.cmd = "adjust hover_point on"
00325 self.hover_cmd_pub.publish(msg)
00326 self.moving_hover_point = not self.moving_hover_point
00327 else:
00328 pass
00329
00330 def toggle_modify_yaw_in_hover(self):
00331 if self.hover_cmd_pub is not None:
00332 msg = control_mode_cmd()
00333 if self.moving_yaw_in_hover:
00334 msg.cmd = "adjust yaw off"
00335 else:
00336 msg.cmd = "adjust yaw on"
00337 self.hover_cmd_pub.publish(msg)
00338 self.moving_yaw_in_hover = not self.moving_yaw_in_hover
00339 else:
00340 pass
00341
00342 def go(self, *args, **kwargs):
00343 r = rospy.Rate(20)
00344 count = 0
00345 while not rospy.is_shutdown():
00346 c = self.stdscr.getch()
00347 if c == ord('q'):
00348 self.curses_msg("Shutting down...")
00349 self.restore_screen()
00350 rospy.signal_shutdown("Quitting..")
00351 elif c == ord('r'):
00352 self.reset()
00353 elif c == curses.KEY_RIGHT:
00354 self.cur_page = (self.cur_page + 1) % self.num_pages
00355 elif c == curses.KEY_LEFT:
00356 self.cur_page = (self.cur_page - 1) % self.num_pages
00357 else:
00358
00359 if self.cur_page == self.page_names.index("Control"):
00360 if c >= ord('1') and c <= ord('9'):
00361 self.mode_to_active(c - ord('1'))
00362 if self.cur_page == self.page_names.index('Mode'):
00363 cur_mode = self.latest.controller_status.active_mode
00364 if cur_mode == "hover":
00365 if c == ord('m'):
00366 self.toggle_modify_hover()
00367 elif c == ord('y'):
00368 self.toggle_modify_yaw_in_hover()
00369
00370
00371 count = (count + 1) % 4
00372 if count == 0 or (self.cur_page != self.prev_page):
00373 self.update()
00374 r.sleep()
00375
00376 if __name__ == "__main__":
00377 stn = StarmacTuiNode()
00378 try:
00379 stn.go()
00380 finally:
00381 stn.restore_screen()
00382
00383