Skip to content

Commit a22c488

Browse files
Turned class WaveGeneratorArduino_qdev into module
1 parent b2cd5b8 commit a22c488

File tree

4 files changed

+222
-286
lines changed

4 files changed

+222
-286
lines changed

WaveGeneratorArduino_qdev.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
"""Provides class `WaveGeneratorArduino_qdev`.
4+
"""
5+
__author__ = "Dennis van Gils"
6+
__authoremail__ = "vangils.dennis@gmail.com"
7+
__url__ = "https://github.com/Dennis-van-Gils/DvG_Arduino_PyQt_multithread_demo"
8+
__date__ = "11-06-2024"
9+
__version__ = "9.0"
10+
11+
from typing import Union, Callable
12+
13+
from qtpy import QtCore
14+
15+
from dvg_qdeviceio import QDeviceIO, DAQ_TRIGGER
16+
from WaveGeneratorArduino import WaveGeneratorArduino, FakeWaveGeneratorArduino
17+
18+
# ------------------------------------------------------------------------------
19+
# WaveGeneratorArduino_qdev
20+
# ------------------------------------------------------------------------------
21+
22+
23+
class WaveGeneratorArduino_qdev(QDeviceIO):
24+
"""Manages multithreaded communication and periodical data acquisition
25+
for an Arduino that is programmed as a wave generator, referred to as the
26+
'device'."""
27+
28+
def __init__(
29+
self,
30+
dev: Union[WaveGeneratorArduino, FakeWaveGeneratorArduino],
31+
DAQ_function: Union[Callable[[], bool], None] = None,
32+
DAQ_interval_ms=10,
33+
DAQ_timer_type=QtCore.Qt.TimerType.PreciseTimer,
34+
critical_not_alive_count=1,
35+
debug=False,
36+
**kwargs,
37+
):
38+
super().__init__(dev, **kwargs) # Pass kwargs onto QtCore.QObject()
39+
self.dev: WaveGeneratorArduino # Enforce type: removes `_NoDevice()`
40+
41+
self.create_worker_DAQ(
42+
DAQ_trigger=DAQ_TRIGGER.INTERNAL_TIMER,
43+
DAQ_function=DAQ_function,
44+
DAQ_interval_ms=DAQ_interval_ms,
45+
DAQ_timer_type=DAQ_timer_type,
46+
critical_not_alive_count=critical_not_alive_count,
47+
debug=debug,
48+
)
49+
self.create_worker_jobs(debug=debug)
50+
51+
def request_set_waveform_to_sine(self):
52+
"""Request sending out a new instruction to the Arduino to change to a
53+
sine wave."""
54+
self.send(self.dev.set_waveform_to_sine)
55+
56+
def request_set_waveform_to_square(self):
57+
"""Request sending out a new instruction to the Arduino to change to a
58+
square wave."""
59+
self.send(self.dev.set_waveform_to_square)
60+
61+
def request_set_waveform_to_sawtooth(self):
62+
"""Request sending out a new instruction to the Arduino to change to a
63+
sawtooth wave."""
64+
self.send(self.dev.set_waveform_to_sawtooth)

demo_A_GUI_full.py

Lines changed: 68 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import os
1414
import sys
1515
import time
16-
from typing import Union
1716

1817
import qtpy
1918
from qtpy import QtCore, QtGui, QtWidgets as QtWid
@@ -26,17 +25,20 @@
2625
from dvg_pyqtgraph_threadsafe import HistoryChartCurve, PlotManager
2726
from dvg_pyqt_filelogger import FileLogger
2827
import dvg_pyqt_controls as controls
29-
from dvg_qdeviceio import QDeviceIO, DAQ_TRIGGER
3028

3129
from WaveGeneratorArduino import WaveGeneratorArduino, FakeWaveGeneratorArduino
30+
from WaveGeneratorArduino_qdev import WaveGeneratorArduino_qdev
3231

33-
# fmt: off
3432
# Constants
35-
DAQ_INTERVAL_MS = 10 # 10 [ms]
36-
CHART_INTERVAL_MS = 20 # 20 [ms]
37-
CHART_HISTORY_TIME = 10 # 10 [s]
33+
DAQ_INTERVAL_MS = 10
34+
"""[ms] Update interval for the data acquisition (DAQ)"""
35+
CHART_INTERVAL_MS = 20
36+
"""[ms] Update interval for the chart"""
37+
CHART_HISTORY_TIME = 10
38+
"""[s] History length of the chart"""
3839

3940
# Global flags
41+
# fmt: off
4042
TRY_USING_OPENGL = True
4143
USE_LARGER_TEXT = False # For demonstration on a beamer
4244
USE_PC_TIME = True # Use Arduino time or PC time?
@@ -83,86 +85,6 @@ def current_date_time_strings():
8385
)
8486

8587

86-
# ------------------------------------------------------------------------------
87-
# WaveGeneratorArduino_qdev
88-
# ------------------------------------------------------------------------------
89-
90-
91-
class WaveGeneratorArduino_qdev(QDeviceIO):
92-
"""Manages multithreaded communication and periodical data acquisition for
93-
a wave generator Arduino, referred to as the 'device'."""
94-
95-
def __init__(
96-
self,
97-
dev: Union[WaveGeneratorArduino, FakeWaveGeneratorArduino],
98-
DAQ_interval_ms=DAQ_INTERVAL_MS,
99-
DAQ_timer_type=QtCore.Qt.TimerType.PreciseTimer,
100-
critical_not_alive_count=1,
101-
debug=False,
102-
**kwargs,
103-
):
104-
super().__init__(dev, **kwargs) # Pass kwargs onto QtCore.QObject()
105-
self.dev: WaveGeneratorArduino # Enforce type: removes `_NoDevice()`
106-
107-
self.create_worker_DAQ(
108-
DAQ_trigger=DAQ_TRIGGER.INTERNAL_TIMER,
109-
DAQ_function=self.DAQ_function,
110-
DAQ_interval_ms=DAQ_interval_ms,
111-
DAQ_timer_type=DAQ_timer_type,
112-
critical_not_alive_count=critical_not_alive_count,
113-
debug=debug,
114-
)
115-
self.create_worker_jobs(debug=debug)
116-
117-
def set_waveform_to_sine(self):
118-
self.send(self.dev.set_waveform_to_sine)
119-
120-
def set_waveform_to_square(self):
121-
self.send(self.dev.set_waveform_to_square)
122-
123-
def set_waveform_to_sawtooth(self):
124-
self.send(self.dev.set_waveform_to_sawtooth)
125-
126-
# --------------------------------------------------------------------------
127-
# DAQ_function
128-
# --------------------------------------------------------------------------
129-
130-
def DAQ_function(self) -> bool:
131-
# Query the Arduino for its state
132-
success, tmp_state = self.dev.query_ascii_values("?", delimiter="\t")
133-
if not success:
134-
str_cur_date, str_cur_time = current_date_time_strings()
135-
dprint(
136-
f"'{self.dev.name}' reports IOError @ "
137-
f"{str_cur_date} {str_cur_time}"
138-
)
139-
return False
140-
141-
# Parse readings into separate state variables
142-
try:
143-
self.dev.state.time, self.dev.state.reading_1 = tmp_state
144-
self.dev.state.time /= 1000
145-
except Exception as err: # pylint: disable=broad-except
146-
pft(err, 3)
147-
str_cur_date, str_cur_time = current_date_time_strings()
148-
dprint(
149-
f"'{self.dev.name}' reports IOError @ "
150-
f"{str_cur_date} {str_cur_time}"
151-
)
152-
return False
153-
154-
# Use Arduino time or PC time?
155-
now = time.perf_counter() if USE_PC_TIME else self.dev.state.time
156-
if self.update_counter_DAQ == 1:
157-
self.dev.state.time_0 = now
158-
self.dev.state.time = 0
159-
else:
160-
self.dev.state.time = now - self.dev.state.time_0
161-
162-
# Return success
163-
return True
164-
165-
16688
# ------------------------------------------------------------------------------
16789
# MainWindow
16890
# ------------------------------------------------------------------------------
@@ -340,12 +262,16 @@ def __init__(
340262

341263
# 'Wave type'
342264
self.qpbt_wave_sine = QtWid.QPushButton("Sine")
343-
self.qpbt_wave_sine.clicked.connect(self.qdev.set_waveform_to_sine)
265+
self.qpbt_wave_sine.clicked.connect(
266+
self.qdev.request_set_waveform_to_sine
267+
)
344268
self.qpbt_wave_square = QtWid.QPushButton("Square")
345-
self.qpbt_wave_square.clicked.connect(self.qdev.set_waveform_to_square)
269+
self.qpbt_wave_square.clicked.connect(
270+
self.qdev.request_set_waveform_to_square
271+
)
346272
self.qpbt_wave_sawtooth = QtWid.QPushButton("Sawtooth")
347273
self.qpbt_wave_sawtooth.clicked.connect(
348-
self.qdev.set_waveform_to_sawtooth
274+
self.qdev.request_set_waveform_to_sawtooth
349275
)
350276

351277
# fmt: off
@@ -508,7 +434,55 @@ def update_chart(self):
508434
# Set up multithreaded communication with the Arduino
509435
# --------------------------------------------------------------------------
510436

511-
ard_qdev = WaveGeneratorArduino_qdev(dev=ard, debug=DEBUG)
437+
def DAQ_function() -> bool:
438+
# Query the Arduino for its state
439+
success, tmp_state = ard.query_ascii_values("?", delimiter="\t")
440+
if not success:
441+
str_cur_date, str_cur_time = current_date_time_strings()
442+
dprint(
443+
f"'{ard.name}' reports IOError @ "
444+
f"{str_cur_date} {str_cur_time}"
445+
)
446+
return False
447+
448+
# Parse readings into separate state variables
449+
try:
450+
ard.state.time, ard.state.reading_1 = tmp_state
451+
ard.state.time /= 1000
452+
except Exception as err: # pylint: disable=broad-except
453+
pft(err, 3)
454+
str_cur_date, str_cur_time = current_date_time_strings()
455+
dprint(
456+
f"'{ard.name}' reports IOError @ "
457+
f"{str_cur_date} {str_cur_time}"
458+
)
459+
return False
460+
461+
# Use Arduino time or PC time?
462+
now = time.perf_counter() if USE_PC_TIME else ard.state.time
463+
if ard_qdev.update_counter_DAQ == 1:
464+
ard.state.time_0 = now
465+
ard.state.time = 0
466+
else:
467+
ard.state.time = now - ard.state.time_0
468+
469+
# Add readings to chart history
470+
window.history_chart_curve.appendData(
471+
ard.state.time, ard.state.reading_1
472+
)
473+
474+
# Add readings to the log
475+
log.update()
476+
477+
# Return success
478+
return True
479+
480+
ard_qdev = WaveGeneratorArduino_qdev(
481+
dev=ard,
482+
DAQ_function=DAQ_function,
483+
DAQ_interval_ms=DAQ_INTERVAL_MS,
484+
debug=DEBUG,
485+
)
512486

513487
# --------------------------------------------------------------------------
514488
# File logger
@@ -538,19 +512,6 @@ def write_data_to_log():
538512
lambda: window.qpbt_record.setText("Click to start recording to file")
539513
)
540514

541-
# --------------------------------------------------------------------------
542-
# postprocess_DAQ_updated
543-
# --------------------------------------------------------------------------
544-
545-
@Slot()
546-
def postprocess_DAQ_updated():
547-
# Add readings to chart history
548-
window.history_chart_curve.appendData(
549-
ard.state.time, ard.state.reading_1
550-
)
551-
# Add readings to the log
552-
log.update()
553-
554515
# --------------------------------------------------------------------------
555516
# Program termination routines
556517
# --------------------------------------------------------------------------
@@ -588,14 +549,12 @@ def notify_connection_lost():
588549
# Start the main GUI event loop
589550
# --------------------------------------------------------------------------
590551

591-
ard_qdev.signal_DAQ_updated.connect(postprocess_DAQ_updated)
592-
ard_qdev.signal_connection_lost.connect(notify_connection_lost)
593-
ard_qdev.start(DAQ_priority=QtCore.QThread.Priority.TimeCriticalPriority)
594-
595-
app.aboutToQuit.connect(about_to_quit)
596-
597552
window = MainWindow(qdev=ard_qdev, qlog=log)
598553
window.timer_chart.start(CHART_INTERVAL_MS)
599554
window.show()
600555

556+
ard_qdev.signal_connection_lost.connect(notify_connection_lost)
557+
ard_qdev.start(DAQ_priority=QtCore.QThread.Priority.TimeCriticalPriority)
558+
559+
app.aboutToQuit.connect(about_to_quit)
601560
sys.exit(app.exec())

0 commit comments

Comments
 (0)