Skip to content

Commit 4e5b0fa

Browse files
committed
QThread exit fix
1 parent c28b2ea commit 4e5b0fa

File tree

6 files changed

+171
-73
lines changed

6 files changed

+171
-73
lines changed

USB/UsbScripts/Drivers/TinyG2Driver.py

+121-43
Original file line numberDiff line numberDiff line change
@@ -25,34 +25,47 @@
2525
from __future__ import unicode_literals
2626

2727
import io
28-
from PySide.QtCore import QThread, QSemaphore, QWaitCondition, QMutex, QObject, Signal, Slot
28+
from PySide.QtCore import QThread, QSemaphore, QWaitCondition, QMutex
29+
from PySide.QtCore import QObject, Signal, Slot
2930
from FreeCAD import Console
3031

3132

3233
class Buffers():
3334

3435
def __init__(self):
35-
self.free = QSemaphore()
36+
self.free = 0
3637
self.gain = QSemaphore()
3738
self.claim = QSemaphore()
3839

39-
class UsbDriver(QObject):
4040

41+
class Mutex():
42+
43+
def __init__(self):
44+
self.paused = QMutex()
45+
self.buffers = QMutex()
46+
self.upload = QMutex()
47+
self.runreader = QMutex()
48+
self.runuploader = QMutex()
49+
50+
51+
class UsbDriver(QObject):
52+
4153
data = Signal(unicode)
54+
gcode = Signal(unicode)
4255

4356
def __init__(self, pool):
4457
QObject.__init__(self)
4558
self.pool = pool
4659
self.eol = pool.Proxy.getCharEndOfLine(pool)
4760
s = pool.Serials[0]
4861
self.sio = io.TextIOWrapper(io.BufferedRWPair(s, s))
49-
self.mutex = QMutex()
62+
self.mutex = Mutex()
5063
self.toggle = QWaitCondition()
5164
self.buffers = Buffers()
5265

5366
def open(self):
5467
thread = QThread()
55-
self.reader = UsbReader(self.pool, self.buffers)
68+
self.reader = UsbReader(self.pool, self.buffers, self.mutex)
5669
self.reader.data.connect(self.data)
5770
self.reader.on_data.connect(self.on_data)
5871
thread.started.connect(self.reader.process)
@@ -63,16 +76,21 @@ def open(self):
6376
self.reader.moveToThread(thread)
6477
thread.start()
6578
self.thread = [thread]
79+
self.mutex.runreader.lock()
6680
self.reader.run = True
81+
self.mutex.runreader.unlock()
6782

6883
def close(self):
6984
if self.pool.Pause:
85+
self.pool.Pause = False
7086
self.resume()
71-
self.pool.Pause = False
7287
if self.pool.Start:
88+
self.pool.Start = False
7389
self.stop()
74-
self.pool.Start = False
75-
self.reader.run = False
90+
else:
91+
self.mutex.runreader.lock()
92+
self.reader.run = False
93+
self.mutex.runreader.unlock()
7694

7795
@Slot()
7896
def on_close(self):
@@ -85,62 +103,87 @@ def on_close(self):
85103
self.pool.Serials = None
86104

87105
def start(self):
88-
if self.pool.Serials is None:
89-
Console.PrintError("Pool is not connected!\n")
90-
self.pool.Start = False
91-
return
106+
if self.checkStart():
107+
return
92108
if not self.pool.Serials[-1].is_open:
93109
self.pool.Serials[-1].open()
94110
self.uploader = UsbUploader(self.pool, self.buffers, self.toggle, self.mutex)
95111
self.uploader.on_data.connect(self.on_data)
112+
self.uploader.gcode.connect(self.gcode)
96113
thread = QThread()
97114
thread.started.connect(self.uploader.process)
98115
self.uploader.finished.connect(thread.quit)
99116
thread.finished.connect(thread.deleteLater)
100117
thread.finished.connect(self.uploader.deleteLater)
101118
thread.finished.connect(self.on_stop)
102119
self.uploader.moveToThread(thread)
120+
self.mutex.upload.lock()
103121
self.reader.upload = True
122+
self.mutex.upload.unlock()
104123
thread.start()
105124
self.thread.append(thread)
106125

126+
def checkStart(self):
127+
if self.pool.Serials is None:
128+
Console.PrintError("Pool is not connected!\n")
129+
self.pool.Start = False
130+
return True
131+
return False
132+
107133
def stop(self):
134+
self.mutex.runuploader.lock()
108135
self.uploader.run = False
136+
self.mutex.runuploader.unlock()
109137

110138
@Slot()
111139
def on_stop(self):
140+
self.mutex.upload.lock()
112141
self.reader.upload = False
113-
self.buffers.gain.release()
142+
self.mutex.upload.unlock()
143+
self.buffers.gain.release(2)
114144
while self.buffers.gain.available():
115145
self.buffers.gain.acquire()
116-
if self.pool.Serials[-1].is_open:
117-
self.pool.Serials[-1].close()
118-
146+
if not self.pool.Open:
147+
self.mutex.runreader.lock()
148+
self.reader.run = False
149+
self.mutex.runreader.unlock()
119150

120151
def pause(self):
152+
if self.checkPause():
153+
return
154+
self.mutex.paused.lock()
155+
self.uploader.paused = True
156+
self.mutex.paused.unlock()
157+
158+
def checkPause(self):
121159
if self.pool.Serials is None:
122160
Console.PrintError("Pool is not connected!\n")
123161
self.pool.Pause = False
124-
return
125-
if not self.pool.Start and self.pool.Pause:
162+
return True
163+
if not self.pool.Start and not self.pool.Pause:
126164
Console.PrintError("File upload is not started!\n")
127165
self.pool.Pause = False
128-
return
129-
self.mutex.lock()
130-
self.uploader.paused = True
131-
self.mutex.unlock()
132-
166+
return True
167+
return False
168+
133169
def resume(self):
134-
if self.pool.Serials is None:
135-
Console.PrintError("Pool is not connected!\n")
170+
if self.checkResume():
136171
return
137-
if not self.pool.Start:
138-
Console.PrintError("File upload is not started!\n")
139-
return
140-
self.mutex.lock()
172+
self.mutex.paused.lock()
141173
self.uploader.paused = False
142-
self.mutex.unlock()
174+
self.mutex.paused.unlock()
143175
self.toggle.wakeAll()
176+
177+
def checkResume(self):
178+
if self.pool.Serials is None:
179+
Console.PrintError("Pool is not connected!\n")
180+
self.pool.Pause = False
181+
return True
182+
if not self.pool.Start and self.pool.Pause:
183+
Console.PrintError("File upload is not started!*****\n")
184+
self.pool.Pause = False
185+
return True
186+
return False
144187

145188
@Slot(unicode)
146189
def on_data(self, data):
@@ -155,10 +198,11 @@ def on_data(self, data):
155198
class UsbReader(QObject):
156199

157200
finished = Signal()
201+
ctrl = Signal(unicode)
158202
data = Signal(unicode)
159203
on_data = Signal(unicode)
160204

161-
def __init__(self, pool, buffers):
205+
def __init__(self, pool, buffers, mutex):
162206
QObject.__init__(self)
163207
self.run = True
164208
self.upload = False
@@ -167,32 +211,49 @@ def __init__(self, pool, buffers):
167211
self.sio = io.TextIOWrapper(io.BufferedRWPair(s, s), newline=eol)
168212
self.pool = pool
169213
self.buffers = buffers
214+
self.mutex = mutex
170215

171216
@Slot()
172217
def process(self):
173218
""" Loop and copy PySerial -> Terminal """
174219
port = self.pool.Serials[0].port
220+
msg = "{} UsbReader thread start on port {}... done\n"
221+
Console.PrintLog(msg.format(self.pool.Name, port))
175222
try:
223+
self.mutex.runreader.lock()
176224
while self.run:
225+
self.mutex.runreader.unlock()
177226
line = self.sio.readline()
178227
if len(line):
228+
self.mutex.upload.lock()
179229
if self.upload:
230+
self.mutex.upload.unlock()
180231
if line.startswith("qr:"):
181232
b = int(line.split(":")[-1])
182233
buffers = b - self.pool.Buffers if b > self.pool.Buffers else 0
183-
while self.buffers.free.available() < buffers:
184-
self.buffers.free.release()
185-
while self.buffers.free.available() > buffers:
186-
self.buffers.free.acquire()
187-
if self.buffers.free.available():
234+
self.mutex.buffers.lock()
235+
self.buffers.free = buffers
236+
if self.buffers.free > 0:
237+
self.mutex.buffers.unlock()
188238
self.buffers.claim.release()
189239
self.buffers.gain.acquire()
190240
else:
241+
self.mutex.buffers.unlock()
191242
self.on_data.emit("$qr")
192-
continue
193-
elif self.buffers.free.available():
243+
#continue
244+
if line.startswith("pos"):
245+
self.ctrl.emit(line)
246+
self.mutex.buffers.lock()
247+
if self.buffers.free > 0:
248+
self.mutex.buffers.unlock()
194249
self.buffers.gain.acquire()
250+
else:
251+
self.mutex.buffers.unlock()
252+
else:
253+
self.mutex.upload.unlock()
195254
self.data.emit(line)
255+
self.mutex.runreader.lock()
256+
self.mutex.runreader.unlock()
196257
except Exception as e:
197258
msg = "Error occurred in UsbReader thread process: {}\n"
198259
Console.PrintError(msg.format(e))
@@ -205,7 +266,8 @@ def process(self):
205266
class UsbUploader(QObject):
206267

207268
finished = Signal()
208-
on_data = Signal(unicode)
269+
on_data = Signal(unicode)
270+
gcode = Signal(unicode)
209271

210272
def __init__(self, pool, buffers, toggle, mutex):
211273
QObject.__init__(self)
@@ -228,24 +290,40 @@ def process(self):
228290
msg = "{} UsbUploader thread start on port {}... done\n"
229291
Console.PrintLog(msg.format(self.pool.Name, port))
230292
self.buffers.claim.acquire()
293+
i = 0
231294
with open(self.pool.UploadFile) as f:
232295
for line in f:
233-
if not self.buffers.free.tryAcquire(1):
296+
i += 1
297+
self.mutex.buffers.lock()
298+
self.buffers.free -= 1
299+
if self.buffers.free == 0:
300+
self.mutex.buffers.unlock()
234301
self.on_data.emit("$qr")
235302
self.buffers.gain.release()
236303
self.buffers.claim.acquire()
304+
else:
305+
self.mutex.buffers.unlock()
306+
line = line.strip()
237307
sio.write(line + eol)
238308
sio.flush()
239-
self.mutex.lock()
309+
self.gcode.emit("{}\t{}".format(i, line))
310+
self.mutex.paused.lock()
240311
if self.paused:
241-
self.toggle.wait(self.mutex)
242-
self.mutex.unlock()
312+
self.toggle.wait(self.mutex.paused)
313+
self.mutex.paused.unlock()
314+
self.mutex.runuploader.lock()
243315
if not self.run:
316+
self.mutex.runuploader.unlock()
244317
break
318+
self.mutex.runuploader.unlock()
245319
except Exception as e:
246320
msg = "Error occurred in UsbUploader thread process: {}\n"
247321
Console.PrintError(msg.format(e))
248322
else:
249323
msg = "{} UsbUploader thread stop on port {}... done\n"
250324
Console.PrintLog(msg.format(self.pool.Name, port))
325+
self.mutex.runuploader.lock()
326+
self.run = False
327+
self.mutex.runuploader.unlock()
328+
self.pool.Start = False
251329
self.finished.emit()

USB/UsbScripts/Drivers/UsbDriver.py

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
class UsbDriver(QObject):
3333

3434
data = Signal(unicode)
35+
gcode = Signal(unicode)
3536

3637
def __init__(self, pool):
3738
QObject.__init__(self)

USB/UsbScripts/TerminalDock.py

+23-6
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
""" GUI Terminal Dock object """
2525
from __future__ import unicode_literals
2626

27-
from PySide.QtGui import QDockWidget, QSplitter, QPlainTextEdit, QTextOption, QTextCursor
27+
from PySide.QtGui import QDockWidget, QWidget, QSplitter, QPlainTextEdit
28+
from PySide.QtGui import QTextOption, QTextCursor, QLabel, QVBoxLayout
2829
from PySide.QtCore import Qt, Signal, Slot
2930

3031

@@ -132,16 +133,29 @@ def on_data(self, data):
132133
self.ensureCursorVisible()
133134

134135

135-
class TerminalWidget(OutPutTextEdit, InPutTextEdit):
136+
class TextEditWidget(OutPutTextEdit, InPutTextEdit):
136137

137-
def __init__(self, driver):
138+
def __init__(self):
138139
InPutTextEdit.__init__(self)
139-
driver.data.connect(self.on_data)
140-
self.data.connect(driver.on_data)
140+
141+
142+
class TerminalWidget(QWidget):
143+
144+
def __init__(self, driver):
145+
QWidget.__init__(self)
146+
layout = QVBoxLayout(self)
147+
layout.setContentsMargins(0, 0, 0, 0)
148+
textEdit = TextEditWidget()
149+
layout.addWidget(textEdit)
150+
driver.data.connect(textEdit.on_data)
151+
textEdit.data.connect(driver.on_data)
152+
outputLabel = QLabel()
153+
layout.addWidget(outputLabel)
154+
driver.gcode.connect(outputLabel.setText)
141155

142156

143157
class DualTerminalWidget(QSplitter):
144-
158+
145159
def __init__(self, driver):
146160
QSplitter.__init__(self, Qt.Vertical)
147161
outputText = OutPutTextEdit()
@@ -150,6 +164,9 @@ def __init__(self, driver):
150164
self.addWidget(inputText)
151165
driver.data.connect(outputText.on_data)
152166
inputText.data.connect(driver.on_data)
167+
outputLabel = QLabel()
168+
self.addWidget(outputLabel)
169+
driver.gcode.connect(outputLabel.setText)
153170

154171

155172
class TerminalDock(QDockWidget):

0 commit comments

Comments
 (0)