blob: d4971515fa1353ed1646e7fe3ed7d9f5be9c6546 [file] [log] [blame]
Sergei Trofimov4e6afe92015-10-09 09:30:04 +01001import os
2import csv
3import tempfile
4from itertools import chain
5
6from devlib.instrument import Instrument, MeasurementsCsv, CONTINUOUS
7from devlib.exception import HostError
8from devlib.utils.misc import unique
9
10try:
11 from daqpower.client import execute_command, Status
12 from daqpower.config import DeviceConfiguration, ServerConfiguration
13except ImportError, e:
14 execute_command, Status = None, None
15 DeviceConfiguration, ServerConfiguration, ConfigurationError = None, None, None
16 import_error_mesg = e.message
17
18
19class DaqInstrument(Instrument):
20
21 mode = CONTINUOUS
22
23 def __init__(self, target, resistor_values, # pylint: disable=R0914
24 labels=None,
25 host='localhost',
26 port=45677,
27 device_id='Dev1',
28 v_range=2.5,
29 dv_range=0.2,
Brendan Jackman0d61ee52017-04-26 16:17:43 +010030 sample_rate_hz=10000,
Sergei Trofimov4e6afe92015-10-09 09:30:04 +010031 channel_map=(0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23),
32 ):
33 # pylint: disable=no-member
34 super(DaqInstrument, self).__init__(target)
35 self._need_reset = True
Sergei Trofimov823ce712017-08-30 14:55:42 +010036 self._raw_files = []
Sergei Trofimov4e6afe92015-10-09 09:30:04 +010037 if execute_command is None:
38 raise HostError('Could not import "daqpower": {}'.format(import_error_mesg))
39 if labels is None:
40 labels = ['PORT_{}'.format(i) for i in xrange(len(resistor_values))]
41 if len(labels) != len(resistor_values):
42 raise ValueError('"labels" and "resistor_values" must be of the same length')
43 self.server_config = ServerConfiguration(host=host,
44 port=port)
45 result = self.execute('list_devices')
46 if result.status == Status.OK:
47 if device_id not in result.data:
48 raise ValueError('Device "{}" is not found on the DAQ server.'.format(device_id))
49 elif result.status != Status.OKISH:
50 raise HostError('Problem querying DAQ server: {}'.format(result.message))
51
52 self.device_config = DeviceConfiguration(device_id=device_id,
53 v_range=v_range,
54 dv_range=dv_range,
Brendan Jackman0d61ee52017-04-26 16:17:43 +010055 sampling_rate=sample_rate_hz,
Sergei Trofimov4e6afe92015-10-09 09:30:04 +010056 resistor_values=resistor_values,
57 channel_map=channel_map,
58 labels=labels)
Brendan Jackman0d61ee52017-04-26 16:17:43 +010059 self.sample_rate_hz = sample_rate_hz
Sergei Trofimov4e6afe92015-10-09 09:30:04 +010060
61 for label in labels:
62 for kind in ['power', 'voltage']:
63 self.add_channel(label, kind)
64
Sergei Trofimov390a5442016-09-02 14:03:33 +010065 def reset(self, sites=None, kinds=None, channels=None):
66 super(DaqInstrument, self).reset(sites, kinds, channels)
Sergei Trofimov4e6afe92015-10-09 09:30:04 +010067 self.execute('close')
68 result = self.execute('configure', config=self.device_config)
69 if not result.status == Status.OK: # pylint: disable=no-member
70 raise HostError(result.message)
71 self._need_reset = False
Sergei Trofimov823ce712017-08-30 14:55:42 +010072 self._raw_files = []
Sergei Trofimov4e6afe92015-10-09 09:30:04 +010073
74 def start(self):
75 if self._need_reset:
76 self.reset()
77 self.execute('start')
78
79 def stop(self):
80 self.execute('stop')
81 self._need_reset = True
82
83 def get_data(self, outfile): # pylint: disable=R0914
84 tempdir = tempfile.mkdtemp(prefix='daq-raw-')
85 self.execute('get_data', output_directory=tempdir)
86 raw_file_map = {}
87 for entry in os.listdir(tempdir):
88 site = os.path.splitext(entry)[0]
89 path = os.path.join(tempdir, entry)
90 raw_file_map[site] = path
Sergei Trofimov823ce712017-08-30 14:55:42 +010091 self._raw_files.append(path)
Sergei Trofimov4e6afe92015-10-09 09:30:04 +010092
93 active_sites = unique([c.site for c in self.active_channels])
94 file_handles = []
95 try:
96 site_readers = {}
97 for site in active_sites:
98 try:
99 site_file = raw_file_map[site]
100 fh = open(site_file, 'rb')
101 site_readers[site] = csv.reader(fh)
102 file_handles.append(fh)
103 except KeyError:
104 message = 'Could not get DAQ trace for {}; Obtained traces are in {}'
105 raise HostError(message.format(site, tempdir))
106
107 # The first row is the headers
108 channel_order = []
109 for site, reader in site_readers.iteritems():
110 channel_order.extend(['{}_{}'.format(site, kind)
111 for kind in reader.next()])
112
113 def _read_next_rows():
114 parts = []
115 for reader in site_readers.itervalues():
116 try:
117 parts.extend(reader.next())
118 except StopIteration:
119 parts.extend([None, None])
120 return list(chain(parts))
121
122 with open(outfile, 'wb') as wfh:
123 field_names = [c.label for c in self.active_channels]
124 writer = csv.writer(wfh)
125 writer.writerow(field_names)
126 raw_row = _read_next_rows()
127 while any(raw_row):
128 row = [raw_row[channel_order.index(f)] for f in field_names]
129 writer.writerow(row)
130 raw_row = _read_next_rows()
131
Marc Bonnici049b2752017-08-03 16:43:32 +0100132 return MeasurementsCsv(outfile, self.active_channels, self.sample_rate_hz)
Sergei Trofimov4e6afe92015-10-09 09:30:04 +0100133 finally:
134 for fh in file_handles:
135 fh.close()
136
Sergei Trofimov823ce712017-08-30 14:55:42 +0100137 def get_raw(self):
138 return self._raw_files
139
Sergei Trofimov4e6afe92015-10-09 09:30:04 +0100140 def teardown(self):
141 self.execute('close')
142
143 def execute(self, command, **kwargs):
144 return execute_command(self.server_config, command, **kwargs)
145