-
-
Notifications
You must be signed in to change notification settings - Fork 35
/
serial_ui.py
245 lines (204 loc) · 8.54 KB
/
serial_ui.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
"""Serial UI module.
Regroup all required classes to manage the Serial Connection UI
and the link with the communication module.
Module : serial_ui
Authors : yannick
"""
from concurrent.futures import process
import PyQt6.QtGui
import PyQt6.QtSerialPort
import PyQt6.QtCore
import PyQt6.QtWidgets
import base_ui
import main
import helper
class SerialChooser(base_ui.WidgetUI, base_ui.CommunicationHandler):
"""This classe is the main Serial Chooser manager.
*) Display the UI
*) Manage the user interraction : connect/disconnect
*) Manage the serial port status
"""
OFFICIAL_VID_PID = [(0x1209, 0xFFB0)] # Highlighted in serial selector
connected = PyQt6.QtCore.pyqtSignal(bool)
shown = PyQt6.QtCore.pyqtSignal()
hidden = PyQt6.QtCore.pyqtSignal()
visible = PyQt6.QtCore.pyqtSignal(bool)
def __init__(self, serial: PyQt6.QtSerialPort.QSerialPort, main_ui: main.MainUi):
"""Initialize the manager with the QSerialPort for serial commmunication and the mainUi."""
base_ui.WidgetUI.__init__(self, main_ui, "serialchooser.ui")
base_ui.CommunicationHandler.__init__(self)
self._serial = serial
self.main = main_ui
self.main_id = None
self._classes = []
self._class_ids = {}
self._port = None
self._ports = []
self.pushButton_refresh.clicked.connect(self.get_ports)
self.pushButton_connect.clicked.connect(self.serial_connect_button)
self.pushButton_send.clicked.connect(self.send_line)
self.lineEdit_cmd.returnPressed.connect(self.send_line)
self.pushButton_ok.clicked.connect(self.main_btn)
self.update()
def showEvent(self, event): # pylint: disable=unused-argument, invalid-name
"""On show event, init the param.
Connect the communication module with the history widget to load the board response.
"""
self.get_raw_reply().connect(self.serial_log)
self.shown.emit()
# Tab is hidden
def hideEvent(self, event): # pylint: disable=unused-argument, invalid-name
"""On hide event, disconnect the event.
Disconnect the communication module with the history widget
to stop to log the board response.
"""
self.get_raw_reply().disconnect(self.serial_log)
self.hidden.emit()
def serial_log(self, txt):
"""Add a new text in the history widget."""
if isinstance(txt, list):
txt = "\n".join(txt)
else:
txt = str(txt)
self.serialLogBox.append(txt)
def send_line(self):
"""Read the command input text, display it in history widget and send it to the board."""
cmd = self.lineEdit_cmd.text() + "\n"
self.serial_log(">" + cmd)
self.serial_write_raw(cmd)
def write(self, data):
"""Write data to the serial port."""
self._serial.write(data)
def update(self):
"""Update the UI when a connection is successfull.
Disable connection button, dropbox, etc.
Emit for all the UI the [connected] event.
"""
if self._serial.isOpen():
self.pushButton_connect.setText(self.tr("Disconnect"))
self.comboBox_port.setEnabled(False)
self.pushButton_refresh.setEnabled(False)
self.pushButton_send.setEnabled(True)
self.lineEdit_cmd.setEnabled(True)
self.connected.emit(True)
self.get_main_classes()
else:
self.pushButton_connect.setText(self.tr("Connect"))
self.comboBox_port.setEnabled(True)
self.pushButton_refresh.setEnabled(True)
self.pushButton_send.setEnabled(False)
self.lineEdit_cmd.setEnabled(False)
self.connected.emit(False)
self.groupBox_system.setEnabled(False)
def serial_connect_button(self):
"""Check if it's not connected, and call start the serial connection."""
if not self._serial.isOpen() and self._port is not None:
self.serial_connect()
else:
self._serial.close()
self.update()
def serial_connect(self):
"""Check if port is not open and open it with right settings."""
self.select_port(self.comboBox_port.currentIndex())
if not self._serial.isOpen() and self._port is not None:
self.main.log("Connecting...")
self._serial.setPort(self._port)
self._serial.setBaudRate(115200)
self._serial.open(PyQt6.QtCore.QIODevice.OpenModeFlag.ReadWrite)
if not self._serial.isOpen():
self.main.log("Can not open port")
else:
self._serial.setDataTerminalReady(True)
def select_port(self, port_id):
"""Change the selected port."""
if port_id != -1 and len(self._ports) != 0:
self._port = self._ports[port_id]
else:
self._port = None
def get_ports(self):
"""Get all the serial port available on the computer.
If the VID.VIP is compatible with openFFBoard color the text in green,
else put it in red.
"""
oldport = self._port if self._port else None
self._ports = PyQt6.QtSerialPort.QSerialPortInfo().availablePorts()
self.comboBox_port.clear()
sel_idx = 0
nb_compatible_device = 0
for i, port in enumerate(self._ports):
supported_vid_pid = (
port.vendorIdentifier(),
port.productIdentifier(),
) in self.OFFICIAL_VID_PID
name = port.portName() + " : " + port.description()
if supported_vid_pid and not name.startswith("cu."):
name += " (FFBoard device)"
else:
name += " (Unsupported device)"
self.comboBox_port.addItem(name)
if supported_vid_pid and not name.startswith("cu."):
sel_idx = i
nb_compatible_device = nb_compatible_device + 1
self.comboBox_port.setItemData(
i,
PyQt6.QtGui.QColor("green"),
PyQt6.QtCore.Qt.ItemDataRole.ForegroundRole,
)
else:
self.comboBox_port.setItemData(
i,
PyQt6.QtGui.QColor("red"),
PyQt6.QtCore.Qt.ItemDataRole.ForegroundRole,
)
plist = [p.portName() for p in self._ports]
if (
(oldport is not None)
and (
(oldport.vendorIdentifier(), oldport.productIdentifier())
in self.OFFICIAL_VID_PID
)
and (oldport.portName() in plist)
):
self.comboBox_port.setCurrentIndex(plist.index(oldport.portName()))
else:
self.comboBox_port.setCurrentIndex(sel_idx) # preselect found entry
self.select_port(self.comboBox_port.currentIndex())
self.update()
return nb_compatible_device
def auto_connect(self, nb_compatible_device):
if (nb_compatible_device == 1) :
self.serial_connect_button()
def update_mains(self, dat):
"""Parse the list of main classes received from board, and update the combobox."""
self.comboBox_main.clear()
self._class_ids, self._classes = helper.classlistToIds(dat)
if self.main_id is None:
# self.main.resetPort()
self.groupBox_system.setEnabled(False)
return
self.groupBox_system.setEnabled(True)
helper.updateClassComboBox(
self.comboBox_main, self._class_ids, self._classes, self.main_id
)
self.main.log("Detected mode: " + self.comboBox_main.currentText())
self.main.update_tabs()
def get_main_classes(self):
"""Get the main classes available from the board in Async."""
def fct(i):
"""Store the main currently selected to refresh the UI."""
self.main_id = i
self.get_value_async("main", "id", fct, conversion=int, delete=True)
self.get_value_async("sys", "lsmain", self.update_mains, delete=True)
def main_btn(self):
"""Read the select main class in the combobox.
Push it to the board and display the reload warning.
"""
index = self._classes[self.comboBox_main.currentIndex()][0]
self.send_value("sys", "main", index)
self.main.reconnect()
msg = PyQt6.QtWidgets.QMessageBox(
PyQt6.QtWidgets.QMessageBox.Icon.Information,
"Main class changed",
"Chip is rebooting. Please reconnect.",
)
msg.exec()