tui.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 # Software License Agreement (BSD License)
00003 #
00004 #  Copyright (c) 2012, UC Regents
00005 #  All rights reserved.
00006 #
00007 #  Redistribution and use in source and binary forms, with or without
00008 #  modification, are permitted provided that the following conditions
00009 #  are met:
00010 #
00011 #   * Redistributions of source code must retain the above copyright
00012 #     notice, this list of conditions and the following disclaimer.
00013 #   * Redistributions in binary form must reproduce the above
00014 #     copyright notice, this list of conditions and the following
00015 #     disclaimer in the documentation and/or other materials provided
00016 #     with the distribution.
00017 #   * Neither the name of the University of California nor the names of its
00018 #     contributors may be used to endorse or promote products derived
00019 #     from this software without specific prior written permission.
00020 #
00021 #  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022 #  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023 #  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024 #  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025 #  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026 #  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027 #  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028 #  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029 #  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030 #  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031 #  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032 #  POSSIBILITY OF SUCH DAMAGE.
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 #11.85 #self.iloop_data.battVoltage
00130         if self.cur_page == self.page_names.index('Home'):
00131             # Altitude:
00132             y, x = self.stdscr.getyx()
00133             self.update_alt(0, 0)
00134             # Mode:
00135             cur_mode = self.latest.controller_status.active_mode
00136             self.update_mode(1, cur_mode)
00137             # Position:
00138             self.update_pos(2)
00139                 
00140         elif self.cur_page == self.page_names.index('Control'):
00141             # State:
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             # Mode:
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             # Mode:
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         # Footer:
00241         # Page number:
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         # Console:
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         # Separator:
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         # Voltage:
00257         self.update_voltage(maxy-1,maxx-8,volts)
00258 
00259         # Spinner:
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     # Individual page elements:
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 #        self.stdscr.addstr(row, 0, "X:", self.colors.cyan)
00272 #        self.stdscr.addstr(POS_FMT % x)
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                 # Page-specific keys:
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             # Refresh display:    
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 


starmac_tui
Author(s): bouffard
autogenerated on Sun Jan 5 2014 11:38:30