Rose-Hulman Robotics Team

Changeset 651

Show
Ignore:
Timestamp:
02/02/10 02:46:01 (2 years ago)
Author:
mosttw
Message:

Added way to change sensor log ("transcript") file from GUI, plus some other GUI tweaks

Location:
trunk/software
Files:
7 modified

Legend:

Unmodified
Added
Removed
  • trunk/software/rb/controller.py

    r650 r651  
    132132                        from rb.transcript import TranscriptLogger 
    133133                        self.transcript = TranscriptLogger( 
    134                                         datetime.now().strftime('logs/rblog-%Y-%m-%dT%H:%M:%S.log'), 
     134                                        datetime.now().strftime(rbconfig.transcript_filename), 
    135135                                        self.drive, self.gps, self.microstrain) 
    136136 
  • trunk/software/rb/gui/gui.py

    r626 r651  
    162162                message_id = self.statusbar.push(context_id, msg) 
    163163                logger.info(msg) 
    164                 gobject.timeout_add(3000, lambda: self.statusbar.remove(context_id, message_id) and False) 
     164                def remove(): 
     165                        self.statusbar.remove_message(context_id, message_id) 
     166                        return False 
     167                gobject.timeout_add(5000, remove) 
    165168                 
    166169                return success 
  • trunk/software/rb/gui/main.gtk

    r649 r651  
    2626                      <object class="GtkImageMenuItem" id="quit_imagemenuitem"> 
    2727                        <property name="visible">True</property> 
     28                        <property name="use_action_appearance">True</property> 
    2829                        <property name="related_action">quit_action</property> 
    29                         <property name="use_action_appearance">True</property> 
    3030                        <property name="use_underline">True</property> 
    3131                        <property name="use_stock">True</property> 
     
    5252                      <object class="GtkRadioMenuItem" id="nav_gui_radiomenuitem"> 
    5353                        <property name="visible">True</property> 
     54                        <property name="use_action_appearance">True</property> 
    5455                        <property name="related_action">nav_gui_action</property> 
    55                         <property name="use_action_appearance">True</property> 
    5656                        <property name="active">True</property> 
    5757                        <property name="draw_as_radio">True</property> 
     
    6161                      <object class="GtkRadioMenuItem" id="nav_auto_radiomenuitem"> 
    6262                        <property name="visible">True</property> 
     63                        <property name="use_action_appearance">True</property> 
    6364                        <property name="related_action">nav_wiimote_action</property> 
    64                         <property name="use_action_appearance">True</property> 
    6565                        <property name="draw_as_radio">True</property> 
    6666                        <property name="group">nav_gui_radiomenuitem</property> 
     
    7070                      <object class="GtkRadioMenuItem" id="nav_wiimote_radiomenuitem"> 
    7171                        <property name="visible">True</property> 
     72                        <property name="use_action_appearance">True</property> 
    7273                        <property name="related_action">nav_auto_action</property> 
    73                         <property name="use_action_appearance">True</property> 
    7474                        <property name="draw_as_radio">True</property> 
    7575                        <property name="group">nav_gui_radiomenuitem</property> 
     
    113113              <object class="GtkRadioToolButton" id="nav_gui_toolbarbutton"> 
    114114                <property name="visible">True</property> 
     115                <property name="use_action_appearance">True</property> 
    115116                <property name="related_action">nav_gui_action</property> 
    116                 <property name="use_action_appearance">True</property> 
    117117                <property name="label" translatable="yes">toolbutton1</property> 
    118118                <property name="use_underline">True</property> 
     
    127127              <object class="GtkRadioToolButton" id="nav_wiimote_toolbarbutton"> 
    128128                <property name="visible">True</property> 
     129                <property name="use_action_appearance">True</property> 
    129130                <property name="related_action">nav_wiimote_action</property> 
    130                 <property name="use_action_appearance">True</property> 
    131131                <property name="label" translatable="yes">toolbutton2</property> 
    132132                <property name="use_underline">True</property> 
     
    141141              <object class="GtkRadioToolButton" id="nav_auto_toolbarbutton"> 
    142142                <property name="visible">True</property> 
     143                <property name="use_action_appearance">True</property> 
    143144                <property name="related_action">nav_auto_action</property> 
    144                 <property name="use_action_appearance">True</property> 
    145145                <property name="label" translatable="yes">toolbutton3</property> 
    146146                <property name="use_underline">True</property> 
     
    621621                  <packing> 
    622622                    <property name="position">0</property> 
     623                  </packing> 
     624                </child> 
     625                <child> 
     626                  <object class="GtkHBox" id="hbox4"> 
     627                    <property name="visible">True</property> 
     628                    <child> 
     629                      <object class="GtkLabel" id="sensors_transcript_label"> 
     630                        <property name="visible">True</property> 
     631                        <property name="xpad">1</property> 
     632                        <property name="label" translatable="yes">_Log filename prefix: </property> 
     633                        <property name="use_underline">True</property> 
     634                        <property name="mnemonic_widget">sensors_transcript_entry</property> 
     635                      </object> 
     636                      <packing> 
     637                        <property name="expand">False</property> 
     638                        <property name="position">0</property> 
     639                      </packing> 
     640                    </child> 
     641                    <child> 
     642                      <object class="GtkEntry" id="sensors_transcript_entry"> 
     643                        <property name="visible">True</property> 
     644                        <property name="can_focus">True</property> 
     645                        <property name="tooltip_text" translatable="yes">Use syntax valid for Pythons time.strftime() function.</property> 
     646                        <property name="invisible_char">&#x25CF;</property> 
     647                        <property name="activates_default">True</property> 
     648                        <property name="secondary_icon_sensitive">True</property> 
     649                      </object> 
     650                      <packing> 
     651                        <property name="position">1</property> 
     652                      </packing> 
     653                    </child> 
     654                    <child> 
     655                      <object class="GtkButton" id="sensors_transcript_button"> 
     656                        <property name="label">gtk-apply</property> 
     657                        <property name="visible">True</property> 
     658                        <property name="can_focus">True</property> 
     659                        <property name="can_default">True</property> 
     660                        <property name="receives_default">True</property> 
     661                        <property name="has_tooltip">True</property> 
     662                        <property name="tooltip_text" translatable="yes">Open a new logfile at the location specified to the left.</property> 
     663                        <property name="use_underline">True</property> 
     664                        <property name="use_stock">True</property> 
     665                      </object> 
     666                      <packing> 
     667                        <property name="expand">False</property> 
     668                        <property name="position">2</property> 
     669                      </packing> 
     670                    </child> 
     671                  </object> 
     672                  <packing> 
     673                    <property name="expand">False</property> 
     674                    <property name="position">1</property> 
    623675                  </packing> 
    624676                </child> 
     
    883935  <object class="GtkSizeGroup" id="sensors_1stcol_sizegroup"> 
    884936    <widgets> 
     937      <widget name="gps_speed_label"/> 
     938      <widget name="gps_lat_lon_label"/> 
     939      <widget name="ms_accel_label"/> 
     940      <widget name="ms_ang_vel_label"/> 
    885941      <widget name="ms_orient_label"/> 
    886       <widget name="ms_ang_vel_label"/> 
    887       <widget name="ms_accel_label"/> 
    888       <widget name="gps_lat_lon_label"/> 
    889       <widget name="gps_speed_label"/> 
    890942    </widgets> 
    891943  </object> 
    892944  <object class="GtkSizeGroup" id="ms_data_sizegroup"> 
    893945    <widgets> 
     946      <widget name="ms_accel_2"/> 
     947      <widget name="ms_accel_1"/> 
     948      <widget name="ms_accel_0"/> 
     949      <widget name="ms_ang_vel_2"/> 
     950      <widget name="ms_ang_vel_1"/> 
     951      <widget name="ms_ang_vel_0"/> 
     952      <widget name="ms_orient_2"/> 
     953      <widget name="ms_orient_0"/> 
    894954      <widget name="ms_orient_1"/> 
    895       <widget name="ms_orient_0"/> 
    896       <widget name="ms_orient_2"/> 
    897       <widget name="ms_ang_vel_0"/> 
    898       <widget name="ms_ang_vel_1"/> 
    899       <widget name="ms_ang_vel_2"/> 
    900       <widget name="ms_accel_0"/> 
    901       <widget name="ms_accel_1"/> 
    902       <widget name="ms_accel_2"/> 
    903955    </widgets> 
    904956  </object> 
  • trunk/software/rb/gui/sensors.py

    r635 r651  
    1515# You should have received a copy of the GNU General Public License 
    1616# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
     17 
     18import os 
     19from datetime import datetime 
    1720 
    1821import gobject 
     
    4649        ''' 
    4750         
    48         def __init__(self, controller, builder): 
     51        def __init__(self, gui, builder): 
     52                self.statusbar = gui.statusbar 
     53                self.transcript = gui.controller.transcript 
    4954                self.microstrain_labels = [builder.get_object(n) for n in MICROSTRAIN_DATA_LABELS] 
    5055                for name in GPS_LABELS: 
     
    6065                builder.get_object('sensor_gps_expander').set_expanded(rbconfig.gps != rbconfig.NONE) 
    6166                builder.get_object('sensor_microstrain_expander').set_expanded(rbconfig.microstrain != rbconfig.NONE) 
     67 
     68                # Transcript stuff 
     69                self.trans_entry = builder.get_object('sensors_transcript_entry') 
     70                self.trans_button = builder.get_object('sensors_transcript_button') 
     71                self.trans_entry.set_sensitive(rbconfig.transcript == rbconfig.REAL) 
     72                self.trans_entry.set_text(rbconfig.transcript_filename) 
     73                self.trans_button.set_sensitive(rbconfig.transcript == rbconfig.REAL) 
     74                self.check_transcript_filename(self.trans_entry) 
     75                self.trans_entry.connect('changed', self.check_transcript_filename) 
     76                self.trans_button.connect('clicked', self.change_transcript_filename) 
     77         
     78        def check_transcript_filename(self, entry): 
     79                try: 
     80                        path = datetime.now().strftime(self.trans_entry.get_text()) 
     81                        if not os.path.exists(os.path.dirname(path)): 
     82                                raise ValueError('Directory doesn\'t exist') 
     83                except (ValueError, IOError): 
     84                        self.trans_button.set_sensitive(False) 
     85                else: 
     86                        self.trans_button.set_sensitive(True) 
     87 
     88        def change_transcript_filename(self, button): 
     89                fn = datetime.now().strftime(self.trans_entry.get_text()) 
     90                success = self.transcript.set_log_filename(fn) 
     91 
     92                # Put a message in the statusbar 
     93                context_id = self.statusbar.get_context_id('transcript_filename') 
     94                if success: 
     95                        msg = 'Writing sensor data to %s' % fn 
     96                else: 
     97                        msg = 'Error changing sensor log to %s' % fn 
     98                message_id = self.statusbar.push(context_id, msg) 
     99 
     100                def remove(): 
     101                        self.statusbar.remove_message(context_id, message_id) 
     102                        return False 
     103                gobject.timeout_add(5000, remove) 
    62104         
    63105        def update_microstrain_status(self, status): 
  • trunk/software/rb/gui/speed.py

    r622 r651  
    120120                        context.stroke() 
    121121                 
    122                 # Draw Target Speed 
    123                 context.set_source_rgb(*DRIVE_COLOR) 
     122                if self.drive: 
     123                        # Draw Target Speed 
     124                        context.set_source_rgb(*DRIVE_COLOR) 
    124125 
    125                 ltarget, rtarget = self.drive.gettarget() 
    126                 x = float(ltarget - rtarget) 
    127                 y = float(ltarget + rtarget) 
    128                 x = ( x/4+0.5) * w 
    129                 y = (-y/4+0.5) * h 
    130                 context.arc(x, y, 5, 0, 2*pi) 
    131                 context.stroke() 
     126                        ltarget, rtarget = self.drive.gettarget() 
     127                        x = float(ltarget - rtarget) 
     128                        y = float(ltarget + rtarget) 
     129                        x = ( x/4+0.5) * w 
     130                        y = (-y/4+0.5) * h 
     131                        context.arc(x, y, 5, 0, 2*pi) 
     132                        context.stroke() 
    132133 
    133                 # Draw Current Speed 
    134                 lcur, rcur = self.drive.getspeed() 
    135                 x = float(lcur - rcur) 
    136                 y = float(lcur + rcur) 
    137                 x =  ( x/4+0.5) * w 
    138                 y =  (-y/4+0.5) * h 
    139                 context.arc(x, y, 10, 0, 2*pi) 
    140                 context.stroke() 
     134                        # Draw Current Speed 
     135                        lcur, rcur = self.drive.getspeed() 
     136                        x = float(lcur - rcur) 
     137                        y = float(lcur + rcur) 
     138                        x =  ( x/4+0.5) * w 
     139                        y =  (-y/4+0.5) * h 
     140                        context.arc(x, y, 10, 0, 2*pi) 
     141                        context.stroke() 
    141142         
    142143        def motion_cb(self, data, event): 
  • trunk/software/rb/transcript.py

    r528 r651  
    1 # Copyright (C) 2009 Thomas W. Most 
     1# Copyright (C) 2009-2010 Thomas W. Most 
    22# 
    33# This program is free software: you can redistribute it and/or modify 
     
    1414# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
    1515 
    16 from __future__ import with_statement 
    17  
    1816''' 
    1917Record sensor data to a log file. 
     
    2321import thread 
    2422import threading 
     23import logging 
     24 
     25logger = logging.getLogger('rb.transcript') 
    2526 
    2627 
     
    3233        (None in Python) will be written as blanks. 
    3334        ''' 
    34         def __init__(self, logfp, drive=None, gps=None, microstrain=None): 
     35        def __init__(self, filename, drive=None, gps=None, microstrain=None): 
    3536                ''' 
    36                 Create a `TranscriptLogger` object.  `logfp` is the string file 
     37                Create a `TranscriptLogger` object.  `filename` is the string file 
    3738                path to write the log to; `drive` is an ``rb.drive.Drive`` object, 
    3839                `gps` is a ``rb.gps.GPS`` object, and `microstrain` is an 
     
    4142                self.running = True 
    4243                self.lock = threading.RLock() 
    43                 self.logfile = open(logfp, 'w') 
     44 
    4445                self.drive = drive 
    4546                self.gps = gps 
    4647                self.microstrain = microstrain 
     48 
     49                self._log_filename = None 
     50                self._log_file = None 
     51                self.set_log_filename(filename) 
    4752                 
    4853                thread.start_new_thread(self._main, ()) 
    4954         
     55        def set_log_filename(self, filename): 
     56                ''' 
     57                Start writing to a new file, location specified by `filename`.  Return 
     58                True on success and False on failure. 
     59                ''' 
     60                with self.lock: 
     61                        logger.info('Setting sensor log file to %r', filename) 
     62                        if self._log_file is not None: 
     63                                logger.debug('Closing existing log file %r', self._log_file) 
     64                                self._log_file.close() 
     65                        self._log_filename = filename 
     66                        try: 
     67                                self._log_file = open(filename, 'w') 
     68                                self.write_comment('Start at %s' % time.time()) 
     69                                self.write_comment('Fields:') 
     70                                self.write_comment('\t'.join([ 
     71                                        'timestamp', 
     72                                        # Drive 
     73                                        'left_target', 'right_target', 
     74                                        'left_speed', 'right_speed', 
     75                                        # GPS 
     76                                        'gps_timestamp', 
     77                                        'gps_lat', 'gps_lon', 'gps_horiz_error', 
     78                                        'gps_speed', 'gps_speed_error', 
     79                                        # MicroStrain 
     80                                        'ms_timestamp', 
     81                                        'ms_orient_0', 'ms_orient_1', 'ms_orient_2', 
     82                                        'ms_ang_vel_0', 'ms_ang_vel_1', 'ms_ang_vel_2', 
     83                                        'ms_accel_0', 'ms_accel_1', 'ms_accel_2' 
     84                                ])) 
     85                        except IOError: 
     86                                logger.exception('Unable to start log file %r', filename) 
     87                                return False 
     88                return True 
     89         
     90        def get_log_filename(self): 
     91                with self.lock: 
     92                        return self._log_filename 
     93         
    5094        def _main(self): 
    51                 with self.lock: 
    52                         self.write_comment('Start at %s' % time.time()) 
    53                         self.write_comment('Fields:') 
    54                         self.write_comment('\t'.join([ 
    55                                 'timestamp', 
    56                                 # Drive 
    57                                 'left_target', 'right_target', 
    58                                 'left_speed', 'right_speed', 
    59                                 # GPS 
    60                                 'gps_timestamp', 
    61                                 'gps_lat', 'gps_lon', 'gps_horiz_error', 
    62                                 'gps_speed', 'gps_speed_error', 
    63                                 # MicroStrain 
    64                                 'ms_timestamp', 
    65                                 'ms_orient_0', 'ms_orient_1', 'ms_orient_2', 
    66                                 'ms_ang_vel_0', 'ms_ang_vel_1', 'ms_ang_vel_2', 
    67                                 'ms_accel_0', 'ms_accel_1', 'ms_accel_2' 
    68                         ])) 
    6995                while self.running: 
    7096                        with self.lock: 
     
    7399                                row += self.get_gps_data() 
    74100                                row += self.get_microstrain_data() 
    75                                 self.logfile.write('\t'.join(str(d) for d in row)) 
    76                                 self.logfile.write('\n') 
     101                                self._log_file.write('\t'.join(str(d) for d in row) + '\n') 
    77102                        time.sleep(.1) 
    78103         
    79104        def write_comment(self, comment): 
    80                 if '\n' in comment: 
     105                if '\n' in comment or '\r' in comment: 
    81106                        raise ValueError('Comments are single-line') 
    82107                with self.lock: 
    83                         self.logfile.write('# ') 
    84                         self.logfile.write(comment) 
    85                         self.logfile.write('\n') 
     108                        self._log_file.write('# %s\n' % comment) 
    86109         
    87110        def get_drive_data(self): 
  • trunk/software/rbconfig.py

    r639 r651  
    3434kalman            = REAL 
    3535transcript        = REAL 
     36transcript_filename = 'logs/rblog-%Y-%m-%dT%H:%M:%S.log' 
    3637drive_ctrl        = NONE 
    3738