Skip to content

Commit b427e39

Browse files
committed
Add support for custom channel name in DataQ
- Add support for custom channel name in DataQ - Fix timeseries on instruments without selectable channel widget - Rename Flow Control widget to Switch - Fix ADU setup not disabling Relay when first or third are on two-wire setting - Fix Ontrak FlowControl and PumpControl widgets ids - Bump version to 2.9.14.gamma
1 parent a1011f2 commit b427e39

File tree

7 files changed

+141
-67
lines changed

7 files changed

+141
-67
lines changed

inlinino/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import numpy as np
99

1010

11-
__version__ = '2.9.14.beta'
11+
__version__ = '2.9.14.gamma'
1212

1313
# Setup Logger
1414
logging.basicConfig(level=logging.DEBUG)

inlinino/__main__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# Get instrument selected
1313
if len(sys.argv) == 2:
1414
try:
15-
inlinino.start(int(sys.argv[1]))
15+
inlinino.start(sys.argv[1])
1616
except ValueError as e:
1717
# raise e
1818
logging.critical('Invalid arguments.')

inlinino/gui.py

+53-37
Original file line numberDiff line numberDiff line change
@@ -417,48 +417,57 @@ def on_packet_corrupted(self):
417417
self.packets_corrupted_flag = True
418418
self.last_packet_corrupted_timestamp = ts
419419

420+
421+
def reset_ts(self, data, reset):
422+
n = len(data)
423+
if reset or len(self._buffer_data) != len(data) or self.reset_ts_trace:
424+
self.reset_ts_trace = False
425+
# Init legend
426+
if hasattr(self.instrument, 'widget_active_timeseries_variables_selected'):
427+
legend = self.instrument.widget_active_timeseries_variables_selected[
428+
:] # Shallow copy to prevent update
429+
else:
430+
legend = [f"{name} ({units})" for name, units in
431+
zip(self.instrument.variable_names, self.instrument.variable_units)]
432+
# Check data is correct length
433+
if len(legend) != n:
434+
# Possible in rare instance widget_active_timeseries_variables_selected is updated by user after the data is received and updated
435+
logger.warning('Variables selected do not match data received. Skip timeseries update.')
436+
self.reset_ts_trace = True
437+
return
438+
# Init buffers
439+
self._buffer_timestamp = RingBuffer(self.BUFFER_LENGTH)
440+
self._buffer_data = [RingBuffer(self.BUFFER_LENGTH) for i in range(len(data))]
441+
# Re-initialize Plot (need to do so when number of curve changes)
442+
self.timeseries_plot_widget.clear()
443+
# Init curves
444+
for i in range(n):
445+
self.timeseries_plot_widget.plotItem.addItem(
446+
pg.PlotCurveItem(pen=pg.mkPen(color=self.PEN_COLORS[i % len(self.PEN_COLORS)], width=2),
447+
name=legend[i])
448+
)
449+
420450
@QtCore.pyqtSlot(list, float)
421451
@QtCore.pyqtSlot(np.ndarray, float)
422452
@QtCore.pyqtSlot(list, float, bool)
423453
@QtCore.pyqtSlot(np.ndarray, float, bool)
424454
def on_new_ts_data(self, data, timestamp, reset=False):
425-
if self.instrument.active_timeseries_variables_lock.acquire(timeout=0.25):
426-
try:
427-
n = len(data)
455+
if self.instrument.widget_select_channel_enabled:
456+
# Select channel widget enabled, hence user can change channels while receiving data
457+
if self.instrument.active_timeseries_variables_lock.acquire(timeout=0.25):
458+
try:
459+
self.reset_ts(data, reset)
460+
finally:
461+
self.instrument.active_timeseries_variables_lock.release()
462+
else:
428463
if reset or len(self._buffer_data) != len(data) or self.reset_ts_trace:
429-
self.reset_ts_trace = False
430-
# Init legend
431-
if hasattr(self.instrument, 'widget_active_timeseries_variables_selected'):
432-
legend = self.instrument.widget_active_timeseries_variables_selected[
433-
:] # Shallow copy to prevent update
434-
else:
435-
legend = [f"{name} ({units})" for name, units in
436-
zip(self.instrument.variable_names, self.instrument.variable_units)]
437-
# Check data is correct length
438-
if len(legend) != n:
439-
# Possible in rare instance widget_active_timeseries_variables_selected is updated by user after the data is received and updated
440-
logger.warning('Variables selected do not match data received. Skip timeseries update.')
441-
self.reset_ts_trace = True
442-
return
443-
# Init buffers
444-
self._buffer_timestamp = RingBuffer(self.BUFFER_LENGTH)
445-
self._buffer_data = [RingBuffer(self.BUFFER_LENGTH) for i in range(len(data))]
446-
# Re-initialize Plot (need to do so when number of curve changes)
447-
self.timeseries_plot_widget.clear()
448-
# Init curves
449-
for i in range(n):
450-
self.timeseries_plot_widget.plotItem.addItem(
451-
pg.PlotCurveItem(pen=pg.mkPen(color=self.PEN_COLORS[i % len(self.PEN_COLORS)], width=2),
452-
name=legend[i])
453-
)
454-
finally:
455-
self.instrument.active_timeseries_variables_lock.release()
464+
logger.warning('Unable to acquire lock to update timeseries variables.')
465+
self.reset_ts_trace = True
466+
return
456467
else:
457-
if reset or len(self._buffer_data) != len(data) or self.reset_ts_trace:
458-
logger.warning('Unable to acquire lock to update timeseries variables.')
459-
self.reset_ts_trace = True
460-
return
461-
n = len(data)
468+
# No select channel widget, hence user can't change channels while receiving data
469+
self.reset_ts(data, reset)
470+
n = len(data)
462471
# Update buffers
463472
self._buffer_timestamp.extend(timestamp)
464473
for i in range(n):
@@ -973,9 +982,11 @@ def act_save(self):
973982
self.notification('No data will be logged.')
974983
elif self.cfg['module'] == 'dataq':
975984
self.cfg['channels_enabled'] = []
985+
self.cfg['channels_names'] = [None] * 8
976986
for c in range(8):
977987
if getattr(self, 'checkbox_channel%d_enabled' % (c+1)).isChecked():
978988
self.cfg['channels_enabled'].append(c)
989+
self.cfg['channels_names'][c] = getattr(self, 'optional_le_channel%d_name' % (c+1)).text()
979990
if not self.cfg['channels_enabled']:
980991
self.notification('At least one channel must be enabled.', 'Nothing to log if no channels are enabled.')
981992
return
@@ -1146,6 +1157,10 @@ def __init__(self, uuid, parent=None):
11461157
if self.cfg['module'] == 'dataq':
11471158
for c in self.cfg['channels_enabled']:
11481159
getattr(self, 'checkbox_channel%d_enabled' % (c + 1)).setChecked(True)
1160+
if 'channels_names' in self.cfg:
1161+
for c, name in enumerate(self.cfg['channels_names']):
1162+
if name is not None:
1163+
getattr(self, 'optional_le_channel%d_name' % (c + 1)).setText(name)
11491164
# Handle legacy configuration
11501165
for k in [k for k in self.cfg.keys() if k.startswith('variable_')]:
11511166
if len(self.cfg[k]) == 1 and self.cfg[k][0] == '':
@@ -1157,15 +1172,15 @@ def __init__(self, uuid, parent=None):
11571172
.index(self.cfg['model']))
11581173
except ValueError:
11591174
logger.warning('Configured model not available in GUI. Interface set to GUI default.')
1160-
self.act_activate_fields_for_adu_model()
11611175
for r in range(4):
11621176
try:
11631177
cb_relay_mode = getattr(self, f'combobox_relay{r}_mode')
11641178
cb_relay_mode.setCurrentIndex([cb_relay_mode.itemText(i) for i in range(cb_relay_mode.count())]
11651179
.index(self.cfg[f'relay{r}_mode']))
11661180
except ValueError:
11671181
logger.warning(f'Configured relay{r}_mode not available in GUI. Interface set to GUI default.')
1168-
getattr(self, f'checkbox_relay{r}_enabled').setChecked(self.cfg[f'relay{r}_enabled'])
1182+
getattr(self, f'checkbox_relay{r}_enabled').setChecked(
1183+
self.cfg[f'relay{r}_enabled'] if f'relay{r}_enabled' in self.cfg.keys() else False)
11691184
for c, g in zip(self.cfg['event_counter_channels_enabled'], self.cfg['event_counter_k_factors']):
11701185
getattr(self, 'checkbox_event_counter_channel%d_enabled' % (c)).setChecked(True)
11711186
getattr(self, 'spinbox_event_counter_channel%d_k_factor' % (c)).setValue(g)
@@ -1174,6 +1189,7 @@ def __init__(self, uuid, parent=None):
11741189
for c, g in zip(self.cfg['analog_channels_enabled'], self.cfg['analog_channels_gains']):
11751190
getattr(self, 'checkbox_analog_channel%d_enabled' % (c)).setChecked(True)
11761191
getattr(self, 'spinbox_analog_channel%d_gain' % (c)).setValue(g)
1192+
self.act_activate_fields_for_adu_model()
11771193
if hasattr(self, 'combobox_interface'):
11781194
if 'interface' in self.cfg.keys():
11791195
try:

inlinino/instruments/dataq.py

+9-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import numpy as np # Needed to compute advanced products
22

3-
from inlinino.instruments import Instrument
3+
from inlinino.instruments import Instrument, InterfaceException
44
from time import time, sleep
55

66
class DATAQ(Instrument):
@@ -38,7 +38,9 @@ def setup(self, cfg):
3838
if len(cfg[k]) == 1 and cfg[k][0] == '':
3939
del cfg[k]
4040
# Overload cfg with DATAQ specific parameters
41-
cfg['variable_names'] = ['C%d' % (c+1) for c in self.channels_enabled] + \
41+
channels_names = cfg['channels_names'] if 'channels_names' in cfg.keys() else [None] * 8
42+
channels_names = [('_' + c.strip().replace(' ', '_')) if c is not None and len(c.strip()) > 0 else '' for c in channels_names]
43+
cfg['variable_names'] = ['C%d%s' % (c+1, channels_names[c]) for c in self.channels_enabled] + \
4244
(cfg['variable_names'] if 'variable_names' in cfg.keys() else [])
4345
cfg['variable_units'] = ['V'] * len(self.channels_enabled) + \
4446
(cfg['variable_units'] if 'variable_units' in cfg.keys() else [])
@@ -55,8 +57,11 @@ def setup(self, cfg):
5557

5658
def close(self, *args, **kwargs):
5759
if self.alive:
58-
self.send_cmd('stop')
59-
sleep(self._interface.timeout)
60+
try:
61+
self.send_cmd('stop')
62+
sleep(self._interface.timeout)
63+
except InterfaceException as e:
64+
self.logger.warning(f'unable to stop instrument, {e}')
6065
super().close(*args, **kwargs)
6166

6267
def send_cmd(self, command):

inlinino/instruments/ontrak.py

+9-12
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def __init__(self, uuid, cfg, signal, *args, **kwargs):
104104
# Init Flow Control Widget
105105
self.widget_flow_controls_enabled: Union[List[bool], List[None]] = [None] * 4
106106
self.widget_pump_controls_enabled: Union[List[bool], List[None]] = [None] * 4
107-
self.widgets_to_load: List[str] = ['FlowControlWidget', 'PumpControlWidget'] * 4
107+
self.widgets_to_load: List[str] = ['FlowControlWidget'] * 4 + ['PumpControlWidget'] * 4
108108
self.widgets_to_load_kwargs: List[Dict] = [{'id': k} for k in range(4)] * 2
109109
# Setup
110110
self.setup(cfg)
@@ -123,17 +123,14 @@ def setup(self, cfg, raw_logger=LogText):
123123
self.relays_enabled, self.relays_gui_mode, self.relays = [], [None]*4, [None]*4
124124
self.widget_flow_controls_enabled, self.widget_pump_controls_enabled = [None]*4, [None]*4
125125
for r in range(n_relays):
126-
if f'relay{r}_enabled' in cfg.keys():
127-
if cfg[f'relay{r}_enabled']:
128-
self.relays_enabled.append(r)
129-
else:
130-
self.relays_gui_mode[r] = None
131-
self.relays[r] = None
132-
self.widget_flow_controls_enabled[r] = False
133-
self.widget_pump_controls_enabled[r] = False
134-
continue
135-
elif r == 0:
136-
raise ValueError('Missing field relay0 enabled')
126+
if f'relay{r}_enabled' in cfg.keys() and cfg[f'relay{r}_enabled']:
127+
self.relays_enabled.append(r)
128+
else:
129+
self.relays_gui_mode[r] = None
130+
self.relays[r] = None
131+
self.widget_flow_controls_enabled[r] = False
132+
self.widget_pump_controls_enabled[r] = False
133+
continue
137134
if f'relay{r}_mode' in cfg.keys():
138135
if cfg[f'relay{r}_mode'] not in relay_mode_supported:
139136
raise ValueError(f"{cfg[f'relay{r}_mode']} not supported by {cfg['model']}.")

inlinino/resources/setup_dataq.ui

+67-11
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
<rect>
1010
<x>0</x>
1111
<y>0</y>
12-
<width>390</width>
13-
<height>510</height>
12+
<width>503</width>
13+
<height>588</height>
1414
</rect>
1515
</property>
1616
<property name="windowTitle">
@@ -87,63 +87,119 @@
8787
<property name="alignment">
8888
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
8989
</property>
90-
<layout class="QVBoxLayout" name="verticalLayout">
91-
<item>
90+
<layout class="QFormLayout" name="formLayout">
91+
<item row="0" column="0">
9292
<widget class="QCheckBox" name="checkbox_channel1_enabled">
9393
<property name="text">
9494
<string>Channel 1</string>
9595
</property>
9696
</widget>
9797
</item>
98-
<item>
98+
<item row="1" column="0">
9999
<widget class="QCheckBox" name="checkbox_channel2_enabled">
100100
<property name="text">
101101
<string>Channel 2</string>
102102
</property>
103103
</widget>
104104
</item>
105-
<item>
105+
<item row="2" column="0">
106106
<widget class="QCheckBox" name="checkbox_channel3_enabled">
107107
<property name="text">
108108
<string>Channel 3</string>
109109
</property>
110110
</widget>
111111
</item>
112-
<item>
112+
<item row="3" column="0">
113113
<widget class="QCheckBox" name="checkbox_channel4_enabled">
114114
<property name="text">
115115
<string>Channel 4</string>
116116
</property>
117117
</widget>
118118
</item>
119-
<item>
119+
<item row="4" column="0">
120120
<widget class="QCheckBox" name="checkbox_channel5_enabled">
121121
<property name="text">
122122
<string>Channel 5</string>
123123
</property>
124124
</widget>
125125
</item>
126-
<item>
126+
<item row="5" column="0">
127127
<widget class="QCheckBox" name="checkbox_channel6_enabled">
128128
<property name="text">
129129
<string>Channel 6</string>
130130
</property>
131131
</widget>
132132
</item>
133-
<item>
133+
<item row="6" column="0">
134134
<widget class="QCheckBox" name="checkbox_channel7_enabled">
135135
<property name="text">
136136
<string>Channel 7</string>
137137
</property>
138138
</widget>
139139
</item>
140-
<item>
140+
<item row="7" column="0">
141141
<widget class="QCheckBox" name="checkbox_channel8_enabled">
142142
<property name="text">
143143
<string>Channel 8</string>
144144
</property>
145145
</widget>
146146
</item>
147+
<item row="0" column="1">
148+
<widget class="QLineEdit" name="optional_le_channel1_name">
149+
<property name="placeholderText">
150+
<string>Instr. Model S/N</string>
151+
</property>
152+
</widget>
153+
</item>
154+
<item row="1" column="1">
155+
<widget class="QLineEdit" name="optional_le_channel2_name">
156+
<property name="placeholderText">
157+
<string>Instr. Model S/N</string>
158+
</property>
159+
</widget>
160+
</item>
161+
<item row="2" column="1">
162+
<widget class="QLineEdit" name="optional_le_channel3_name">
163+
<property name="placeholderText">
164+
<string>Instr. Model S/N</string>
165+
</property>
166+
</widget>
167+
</item>
168+
<item row="3" column="1">
169+
<widget class="QLineEdit" name="optional_le_channel4_name">
170+
<property name="placeholderText">
171+
<string>Instr. Model S/N</string>
172+
</property>
173+
</widget>
174+
</item>
175+
<item row="4" column="1">
176+
<widget class="QLineEdit" name="optional_le_channel5_name">
177+
<property name="placeholderText">
178+
<string>Instr. Model S/N</string>
179+
</property>
180+
</widget>
181+
</item>
182+
<item row="5" column="1">
183+
<widget class="QLineEdit" name="optional_le_channel6_name">
184+
<property name="placeholderText">
185+
<string>Instr. Model S/N</string>
186+
</property>
187+
</widget>
188+
</item>
189+
<item row="6" column="1">
190+
<widget class="QLineEdit" name="optional_le_channel7_name">
191+
<property name="placeholderText">
192+
<string>Instr. Model S/N</string>
193+
</property>
194+
</widget>
195+
</item>
196+
<item row="7" column="1">
197+
<widget class="QLineEdit" name="optional_le_channel8_name">
198+
<property name="placeholderText">
199+
<string>Instr. Model S/N</string>
200+
</property>
201+
</widget>
202+
</item>
147203
</layout>
148204
</widget>
149205
</item>

inlinino/widgets/flow_control.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class FlowControlWidget(GenericWidget):
99
def __init__(self, instrument, id=0):
1010
self.id = id
1111
super().__init__(instrument)
12-
self.group_box_instrument_control.setTitle(f'Flow Control {self.id}')
12+
self.group_box_instrument_control.setTitle(f'Switch {self.id}')
1313
self.radio_instrument_control_filter.clicked.connect(self.set_switch_mode)
1414
self.radio_instrument_control_total.clicked.connect(self.set_switch_mode)
1515
self.radio_instrument_control_hourly.clicked.connect(self.set_switch_mode)

0 commit comments

Comments
 (0)