1
0
mirror of https://github.com/ARM-software/devlib.git synced 2025-01-31 10:10:46 +00:00

instrument: add get_raw() API

Derived metrics may be calculated form data in raw output that is not
present in the resulting MeasurementCSV. This adds a method to provide
uniform access to raw artifacts generated by an instrument.
This commit is contained in:
Sergei Trofimov 2017-08-30 14:55:42 +01:00
parent 2afa8f86a4
commit 823ce718bf
6 changed files with 36 additions and 8 deletions

View File

@ -297,3 +297,6 @@ class Instrument(object):
def get_data(self, outfile): def get_data(self, outfile):
pass pass
def get_raw(self):
return []

View File

@ -121,3 +121,6 @@ class AcmeCapeInstrument(Instrument):
output_row.append(float(row[i])/1000) output_row.append(float(row[i])/1000)
writer.writerow(output_row) writer.writerow(output_row)
return MeasurementsCsv(outfile, self.active_channels, self.sample_rate_hz) return MeasurementsCsv(outfile, self.active_channels, self.sample_rate_hz)
def get_raw(self):
return [self.raw_data_file]

View File

@ -33,6 +33,7 @@ class DaqInstrument(Instrument):
# pylint: disable=no-member # pylint: disable=no-member
super(DaqInstrument, self).__init__(target) super(DaqInstrument, self).__init__(target)
self._need_reset = True self._need_reset = True
self._raw_files = []
if execute_command is None: if execute_command is None:
raise HostError('Could not import "daqpower": {}'.format(import_error_mesg)) raise HostError('Could not import "daqpower": {}'.format(import_error_mesg))
if labels is None: if labels is None:
@ -68,6 +69,7 @@ class DaqInstrument(Instrument):
if not result.status == Status.OK: # pylint: disable=no-member if not result.status == Status.OK: # pylint: disable=no-member
raise HostError(result.message) raise HostError(result.message)
self._need_reset = False self._need_reset = False
self._raw_files = []
def start(self): def start(self):
if self._need_reset: if self._need_reset:
@ -86,6 +88,7 @@ class DaqInstrument(Instrument):
site = os.path.splitext(entry)[0] site = os.path.splitext(entry)[0]
path = os.path.join(tempdir, entry) path = os.path.join(tempdir, entry)
raw_file_map[site] = path raw_file_map[site] = path
self._raw_files.append(path)
active_sites = unique([c.site for c in self.active_channels]) active_sites = unique([c.site for c in self.active_channels])
file_handles = [] file_handles = []
@ -131,6 +134,9 @@ class DaqInstrument(Instrument):
for fh in file_handles: for fh in file_handles:
fh.close() fh.close()
def get_raw(self):
return self._raw_files
def teardown(self): def teardown(self):
self.execute('close') self.execute('close')

View File

@ -52,6 +52,7 @@ class EnergyProbeInstrument(Instrument):
self.raw_output_directory = None self.raw_output_directory = None
self.process = None self.process = None
self.sample_rate_hz = 10000 # Determined empirically self.sample_rate_hz = 10000 # Determined empirically
self.raw_data_file = None
for label in self.labels: for label in self.labels:
for kind in self.attributes: for kind in self.attributes:
@ -64,6 +65,7 @@ class EnergyProbeInstrument(Instrument):
for i, rval in enumerate(self.resistor_values)] for i, rval in enumerate(self.resistor_values)]
rstring = ''.join(parts) rstring = ''.join(parts)
self.command = '{} -d {} -l {} {}'.format(self.caiman, self.device_entry, rstring, self.raw_output_directory) self.command = '{} -d {} -l {} {}'.format(self.caiman, self.device_entry, rstring, self.raw_output_directory)
self.raw_data_file = None
def start(self): def start(self):
self.logger.debug(self.command) self.logger.debug(self.command)
@ -92,10 +94,10 @@ class EnergyProbeInstrument(Instrument):
num_of_ports = len(self.resistor_values) num_of_ports = len(self.resistor_values)
struct_format = '{}I'.format(num_of_ports * self.attributes_per_sample) struct_format = '{}I'.format(num_of_ports * self.attributes_per_sample)
not_a_full_row_seen = False not_a_full_row_seen = False
raw_data_file = os.path.join(self.raw_output_directory, '0000000000') self.raw_data_file = os.path.join(self.raw_output_directory, '0000000000')
self.logger.debug('Parsing raw data file: {}'.format(raw_data_file)) self.logger.debug('Parsing raw data file: {}'.format(self.raw_data_file))
with open(raw_data_file, 'rb') as bfile: with open(self.raw_data_file, 'rb') as bfile:
with open(outfile, 'wb') as wfh: with open(outfile, 'wb') as wfh:
writer = csv.writer(wfh) writer = csv.writer(wfh)
writer.writerow(active_channels) writer.writerow(active_channels)
@ -114,3 +116,6 @@ class EnergyProbeInstrument(Instrument):
else: else:
not_a_full_row_seen = True not_a_full_row_seen = True
return MeasurementsCsv(outfile, self.active_channels, self.sample_rate_hz) return MeasurementsCsv(outfile, self.active_channels, self.sample_rate_hz)
def get_raw(self):
return [self.raw_data_file]

View File

@ -20,6 +20,7 @@ class FramesInstrument(Instrument):
self.collector = None self.collector = None
self.header = None self.header = None
self._need_reset = True self._need_reset = True
self._raw_file = None
self._init_channels() self._init_channels()
def reset(self, sites=None, kinds=None, channels=None): def reset(self, sites=None, kinds=None, channels=None):
@ -27,6 +28,7 @@ class FramesInstrument(Instrument):
self.collector = self.collector_cls(self.target, self.period, self.collector = self.collector_cls(self.target, self.period,
self.collector_target, self.header) self.collector_target, self.header)
self._need_reset = False self._need_reset = False
self._raw_file = None
def start(self): def start(self):
if self._need_reset: if self._need_reset:
@ -38,14 +40,16 @@ class FramesInstrument(Instrument):
self._need_reset = True self._need_reset = True
def get_data(self, outfile): def get_data(self, outfile):
raw_outfile = None
if self.keep_raw: if self.keep_raw:
raw_outfile = outfile + '.raw' self._raw_file = outfile + '.raw'
self.collector.process_frames(raw_outfile) self.collector.process_frames(self._raw_file)
active_sites = [chan.label for chan in self.active_channels] active_sites = [chan.label for chan in self.active_channels]
self.collector.write_frames(outfile, columns=active_sites) self.collector.write_frames(outfile, columns=active_sites)
return MeasurementsCsv(outfile, self.active_channels, self.sample_rate_hz) return MeasurementsCsv(outfile, self.active_channels, self.sample_rate_hz)
def get_raw(self):
return [self._raw_file] if self._raw_file else []
def _init_channels(self): def _init_channels(self):
raise NotImplementedError() raise NotImplementedError()

View File

@ -65,8 +65,8 @@ Instrument
:INSTANTANEOUS: The instrument supports taking a single sample via :INSTANTANEOUS: The instrument supports taking a single sample via
``take_measurement()``. ``take_measurement()``.
:CONTINUOUS: The instrument supports collecting measurements over a :CONTINUOUS: The instrument supports collecting measurements over a
period of time via ``start()``, ``stop()``, and period of time via ``start()``, ``stop()``, ``get_data()``,
``get_data()`` methods. and (optionally) ``get_raw`` methods.
.. note:: It's possible for one instrument to support more than a single .. note:: It's possible for one instrument to support more than a single
mode. mode.
@ -161,6 +161,13 @@ Instrument
.. note:: This method is only implemented by :class:`Instrument`\ s that .. note:: This method is only implemented by :class:`Instrument`\ s that
support ``CONTINUOUS`` measurement. support ``CONTINUOUS`` measurement.
.. method:: Instrument.get_raw()
Returns a list of paths to files containing raw output from the underlying
source(s) that is used to produce the data CSV. If now raw output is
generated or saved, an empty list will be returned. The format of the
contents of the raw files is entirely source-dependent.
.. attribute:: Instrument.sample_rate_hz .. attribute:: Instrument.sample_rate_hz
Sample rate of the instrument in Hz. Assumed to be the same for all channels. Sample rate of the instrument in Hz. Assumed to be the same for all channels.