exporter.py
Go to the documentation of this file.
00001 #!/usr/bin/python
00002 #
00003 # Software License Agreement (BSD License)
00004 #
00005 # Copyright (c) 2009, Willow Garage, Inc.
00006 # All rights reserved.
00007 #
00008 # Redistribution and use in source and binary forms, with or without
00009 # modification, are permitted provided that the following conditions
00010 # are met:
00011 #
00012 #  * Redistributions of source code must retain the above copyright
00013 #    notice, this list of conditions and the following disclaimer.
00014 #  * Redistributions in binary form must reproduce the above
00015 #    copyright notice, this list of conditions and the following
00016 #    disclaimer in the documentation and/or other materials provided
00017 #    with the distribution.
00018 #  * Neither the name of the Willow Garage nor the names of its
00019 #    contributors may be used to endorse or promote products derived
00020 #    from this software without specific prior written permission.
00021 #
00022 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00025 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00026 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00027 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00028 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00030 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00032 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033 # POSSIBILITY OF SUCH DAMAGE.
00034 
00035 ##\author Kevin Watts
00036 ##\brief LogExporter class does diagnostics logfile conversion to CSV
00037 
00038 import roslib
00039 import rosbag
00040 import diagnostic_msgs.msg
00041 import time, sys, os
00042 import operator, tempfile, subprocess
00043 
00044 ##\brief Converts and processes diagnostics logs to CSV format
00045 ##
00046 ## Used by scripts/export_csv.py to convert diagnostics log files to CSV format
00047 class LogExporter:
00048     ##\param output_dir str : Complete path of output dir. If None, uses temp dir
00049     ##\param logfile str : path of logfile
00050     def __init__(self, output_dir, logfile):
00051         self._temp = False
00052         self._stats = {}
00053         self.logfile = logfile
00054 
00055         self.output_dir = output_dir
00056         if self.output_dir is None:
00057             self.output_dir = tempfile.mkdtemp()
00058             self._temp = True
00059         
00060         if not os.path.isdir(self.output_dir):
00061             os.makedirs(self.output_dir)
00062 
00063     ##\brief Removes all output files. Removes directory if temp
00064     def remove_files(self):
00065         for name in self._stats:
00066             file = self._stats[name]['file_name']
00067             os.remove(file)
00068         if self._temp:
00069             os.rmdir(self.output_dir)
00070         
00071     ##\brief Return filename of output
00072     ##\param name str : DiagnosticStatus name ex: 'Mechanism Control'
00073     def get_filename(self, name):
00074         if not self._stats.has_key(name):
00075             return None # self.output_dir + '/%s.csv' % name.replace(' ', '_')
00076         return self._stats[name]['file_name']
00077 
00078     ##\brief Use rosrecord to play back bagfile
00079     def process_log(self):
00080         bag = rosbag.Bag(self.logfile)
00081         for (topic, msg, t) in bag.read_messages():
00082             self._update(topic, msg)
00083 
00084     ##\brief Creates and updates data files with new messages
00085     def _update(self, topic, msg):
00086         if (not (topic == '/diagnostics')):
00087             print "Discarding message on topic: %s" % topic
00088             return
00089         
00090         t = time.localtime(float(str(msg.header.stamp)) / 1000000000.0)
00091         
00092         for status in msg.status:
00093             name = status.name
00094 
00095             if (not self._stats.has_key(name)):
00096                 self._stats[name] = {}
00097                 
00098                 fields = {}
00099                 for index, s in enumerate(status.values):
00100                     fields[s.key] = index
00101                     
00102                 self._stats[name]['fields'] = fields
00103                 
00104                 self._stats[name]['level'] = status.level
00105                 self._stats[name]['message'] = status.message
00106                 self._stats[name]['hardware_id'] = status.hardware_id
00107 
00108                 # Use named temp file, will cat this to header on close
00109                 datafile, tmp_name = tempfile.mkstemp()
00110                 self._stats[name]['data_file'] = os.fdopen(datafile, 'w')
00111                 self._stats[name]['data_name'] = tmp_name
00112                 
00113 
00114             # Check to see if fields have changed. Add new fields to map
00115             if (not [s.key for s in status.values] == self._stats[name]['fields'].keys()):
00116                 for s in status.values:
00117                     if not self._stats[name]['fields'].has_key(s.key):
00118                         self._stats[name]['fields'][s.key] = len(self._stats[name]['fields'])
00119 
00120             # Add values in correct place for header index
00121             # Key/Value pairs can move around, this makes sure values are 
00122             # added to correct keys
00123             vals = []
00124             for key, val in self._stats[name]['fields'].iteritems():
00125                 vals.append('')
00126             for s in status.values:
00127                 vals[self._stats[name]['fields'][s.key]] = s.value.replace('\n','  ').replace(',',' ')
00128         
00129             msg = status.message.replace(',',' ').strip()
00130             hw_id = status.hardware_id.replace(',', ' ')
00131         
00132             self._stats[name]['data_file'].write(','.join([time.strftime("%Y/%m/%d %H:%M:%S", t)] + 
00133                                             [str(status.level), msg, hw_id] + vals) + '\n')
00134 
00135     ##\brief Close logfile, append data to header
00136     def finish_logfile(self):
00137         for name in self._stats:
00138             # Sort fields by correct index, add to header
00139             field_dict = sorted(self._stats[name]['fields'].iteritems(), key=operator.itemgetter(1))
00140             fields = map(operator.itemgetter(0), field_dict)
00141             
00142             header_line = ','.join(['Timestamp'] + ['Level', 'Message', 'Hardware ID'] + 
00143                                     [f.replace(',','').replace('\n', ' ') for f in fields]) + '\n'
00144             
00145             file_name = os.path.join(self.output_dir, name.replace(' ', '_').replace('(', '').replace(')', '').replace('/', '__').replace('.', '').replace('#', '') + '.csv')
00146             
00147             output_file = file(file_name, 'w')
00148             output_file.write(header_line)
00149             output_file.close()
00150 
00151 
00152             self._stats[name]['data_file'].close() # Close data
00153             
00154             # Append the tmp data file to the header
00155             subprocess.call("cat %s >> %s" % (self._stats[name]['data_name'], file_name), shell=True)
00156             # Remove tmp file
00157             os.remove(self._stats[name]['data_name'])
00158             # Store filename
00159             self._stats[name]['file_name'] = file_name
00160     


diagnostic_analysis
Author(s): Kevin Watts, Brice Rebsamen , Eric Berger, Kevin Watts
autogenerated on Sun Oct 5 2014 23:27:27