exporter.py
Go to the documentation of this file.
1 #!/usr/bin/python
2 #
3 # Software License Agreement (BSD License)
4 #
5 # Copyright (c) 2009, Willow Garage, Inc.
6 # All rights reserved.
7 #
8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions
10 # are met:
11 #
12 # * Redistributions of source code must retain the above copyright
13 # notice, this list of conditions and the following disclaimer.
14 # * Redistributions in binary form must reproduce the above
15 # copyright notice, this list of conditions and the following
16 # disclaimer in the documentation and/or other materials provided
17 # with the distribution.
18 # * Neither the name of the Willow Garage nor the names of its
19 # contributors may be used to endorse or promote products derived
20 # from this software without specific prior written permission.
21 #
22 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 # POSSIBILITY OF SUCH DAMAGE.
34 
35 ##\author Kevin Watts
36 ##\brief LogExporter class does diagnostics logfile conversion to CSV
37 
38 import roslib
39 import rosbag
40 import diagnostic_msgs.msg
41 import time, sys, os
42 import operator, tempfile, subprocess
43 
44 ##\brief Converts and processes diagnostics logs to CSV format
45 
48  ##\param output_dir str : Complete path of output dir. If None, uses temp dir
49  ##\param logfile str : path of logfile
50  def __init__(self, output_dir, logfile):
51  self._temp = False
52  self._stats = {}
53  self.logfile = logfile
54 
55  self.output_dir = output_dir
56  if self.output_dir is None:
57  self.output_dir = tempfile.mkdtemp()
58  self._temp = True
59 
60  if not os.path.isdir(self.output_dir):
61  os.makedirs(self.output_dir)
62 
63  ##\brief Removes all output files. Removes directory if temp
64  def remove_files(self):
65  for name in self._stats:
66  file = self._stats[name]['file_name']
67  os.remove(file)
68  if self._temp:
69  os.rmdir(self.output_dir)
70 
71  ##\brief Return filename of output
72  ##\param name str : DiagnosticStatus name ex: 'Mechanism Control'
73  def get_filename(self, name):
74  if not self._stats.has_key(name):
75  return None # self.output_dir + '/%s.csv' % name.replace(' ', '_')
76  return self._stats[name]['file_name']
77 
78  ##\brief Use rosrecord to play back bagfile
79  def process_log(self):
80  bag = rosbag.Bag(self.logfile)
81  for (topic, msg, t) in bag.read_messages():
82  self._update(topic, msg)
83 
84  ##\brief Creates and updates data files with new messages
85  def _update(self, topic, msg):
86  if (not (topic == '/diagnostics')):
87  print "Discarding message on topic: %s" % topic
88  return
89 
90  t = time.localtime(float(str(msg.header.stamp)) / 1000000000.0)
91 
92  for status in msg.status:
93  name = status.name
94 
95  if (not self._stats.has_key(name)):
96  self._stats[name] = {}
97 
98  fields = {}
99  for index, s in enumerate(status.values):
100  fields[s.key] = index
101 
102  self._stats[name]['fields'] = fields
103 
104  self._stats[name]['level'] = status.level
105  self._stats[name]['message'] = status.message
106  self._stats[name]['hardware_id'] = status.hardware_id
107 
108  # Use named temp file, will cat this to header on close
109  datafile, tmp_name = tempfile.mkstemp()
110  self._stats[name]['data_file'] = os.fdopen(datafile, 'w')
111  self._stats[name]['data_name'] = tmp_name
112 
113 
114  # Check to see if fields have changed. Add new fields to map
115  if (not [s.key for s in status.values] == self._stats[name]['fields'].keys()):
116  for s in status.values:
117  if not self._stats[name]['fields'].has_key(s.key):
118  self._stats[name]['fields'][s.key] = len(self._stats[name]['fields'])
119 
120  # Add values in correct place for header index
121  # Key/Value pairs can move around, this makes sure values are
122  # added to correct keys
123  vals = []
124  for key, val in self._stats[name]['fields'].iteritems():
125  vals.append('')
126  for s in status.values:
127  vals[self._stats[name]['fields'][s.key]] = s.value.replace('\n',' ').replace(',',' ')
128 
129  msg = status.message.replace(',',' ').strip()
130  hw_id = status.hardware_id.replace(',', ' ')
131 
132  self._stats[name]['data_file'].write(','.join([time.strftime("%Y/%m/%d %H:%M:%S", t)] +
133  [str(status.level), msg, hw_id] + vals) + '\n')
134 
135  ##\brief Close logfile, append data to header
136  def finish_logfile(self):
137  for name in self._stats:
138  # Sort fields by correct index, add to header
139  field_dict = sorted(self._stats[name]['fields'].iteritems(), key=operator.itemgetter(1))
140  fields = map(operator.itemgetter(0), field_dict)
141 
142  header_line = ','.join(['Timestamp'] + ['Level', 'Message', 'Hardware ID'] +
143  [f.replace(',','').replace('\n', ' ') for f in fields]) + '\n'
144 
145  file_name = os.path.join(self.output_dir, name.replace(' ', '_').replace('(', '').replace(')', '').replace('/', '__').replace('.', '').replace('#', '') + '.csv')
146 
147  output_file = file(file_name, 'w')
148  output_file.write(header_line)
149  output_file.close()
150 
151 
152  self._stats[name]['data_file'].close() # Close data
153 
154  # Append the tmp data file to the header
155  subprocess.call("cat %s >> %s" % (self._stats[name]['data_name'], file_name), shell=True)
156  # Remove tmp file
157  os.remove(self._stats[name]['data_name'])
158  # Store filename
159  self._stats[name]['file_name'] = file_name
160 
def __init__(self, output_dir, logfile)
Definition: exporter.py:50
def remove_files(self)
Removes all output files.
Definition: exporter.py:64
def process_log(self)
Use rosrecord to play back bagfile.
Definition: exporter.py:79
Converts and processes diagnostics logs to CSV format.
Definition: exporter.py:47
def _update(self, topic, msg)
Creates and updates data files with new messages.
Definition: exporter.py:85
def finish_logfile(self)
Close logfile, append data to header.
Definition: exporter.py:136
def get_filename(self, name)
Return filename of output.
Definition: exporter.py:73


diagnostic_analysis
Author(s): Kevin Watts, Brice Rebsamen , Eric Berger, Kevin Watts
autogenerated on Wed Mar 27 2019 03:02:21