printrun-src/printrun/printcore.py

changeset 46
cce0af6351f0
parent 15
0bbb006204fc
equal deleted inserted replaced
45:c82943fb205f 46:cce0af6351f0
1 #!/usr/bin/env python
2
3 # This file is part of the Printrun suite. 1 # This file is part of the Printrun suite.
4 # 2 #
5 # Printrun is free software: you can redistribute it and/or modify 3 # Printrun is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by 4 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or 5 # the Free Software Foundation, either version 3 of the License, or
13 # GNU General Public License for more details. 11 # GNU General Public License for more details.
14 # 12 #
15 # You should have received a copy of the GNU General Public License 13 # You should have received a copy of the GNU General Public License
16 # along with Printrun. If not, see <http://www.gnu.org/licenses/>. 14 # along with Printrun. If not, see <http://www.gnu.org/licenses/>.
17 15
18 __version__ = "2015.03.10" 16 __version__ = "2.0.0rc7"
19 17
20 from serialWrapper import Serial, SerialException, PARITY_ODD, PARITY_NONE 18 import sys
19 if sys.version_info.major < 3:
20 print("You need to run this on Python 3")
21 sys.exit(-1)
22
23 from serial import Serial, SerialException, PARITY_ODD, PARITY_NONE
21 from select import error as SelectError 24 from select import error as SelectError
22 import threading 25 import threading
23 from Queue import Queue, Empty as QueueEmpty 26 from queue import Queue, Empty as QueueEmpty
24 import time 27 import time
25 import platform 28 import platform
26 import os 29 import os
27 import sys
28 stdin, stdout, stderr = sys.stdin, sys.stdout, sys.stderr
29 reload(sys).setdefaultencoding('utf8')
30 sys.stdin, sys.stdout, sys.stderr = stdin, stdout, stderr
31 import logging 30 import logging
32 import traceback 31 import traceback
33 import errno 32 import errno
34 import socket 33 import socket
35 import re 34 import re
36 from functools import wraps 35 import selectors
36 from functools import wraps, reduce
37 from collections import deque 37 from collections import deque
38 from printrun import gcoder 38 from printrun import gcoder
39 from .utils import install_locale, decode_utf8 39 from .utils import set_utf8_locale, install_locale, decode_utf8
40 try:
41 set_utf8_locale()
42 except:
43 pass
40 install_locale('pronterface') 44 install_locale('pronterface')
45 from printrun.plugins import PRINTCORE_HANDLER
41 46
42 def locked(f): 47 def locked(f):
43 @wraps(f) 48 @wraps(f)
44 def inner(*args, **kw): 49 def inner(*args, **kw):
45 with inner.lock: 50 with inner.lock:
58 def enable_hup(port): 63 def enable_hup(port):
59 control_ttyhup(port, False) 64 control_ttyhup(port, False)
60 65
61 def disable_hup(port): 66 def disable_hup(port):
62 control_ttyhup(port, True) 67 control_ttyhup(port, True)
68
69 PR_EOF = None #printrun's marker for EOF
70 PR_AGAIN = b'' #printrun's marker for timeout/no data
71 SYS_EOF = b'' #python's marker for EOF
72 SYS_AGAIN = None #python's marker for timeout/no data
63 73
64 class printcore(): 74 class printcore():
65 def __init__(self, port = None, baud = None, dtr=None): 75 def __init__(self, port = None, baud = None, dtr=None):
66 """Initializes a printcore instance. Pass the port and baud rate to 76 """Initializes a printcore instance. Pass the port and baud rate to
67 connect immediately""" 77 connect immediately"""
106 self.read_thread = None 116 self.read_thread = None
107 self.stop_read_thread = False 117 self.stop_read_thread = False
108 self.send_thread = None 118 self.send_thread = None
109 self.stop_send_thread = False 119 self.stop_send_thread = False
110 self.print_thread = None 120 self.print_thread = None
121 self.readline_buf = []
122 self.selector = None
123 self.event_handler = PRINTCORE_HANDLER
124 # Not all platforms need to do this parity workaround, and some drivers
125 # don't support it. Limit it to platforms that actually require it
126 # here to avoid doing redundant work elsewhere and potentially breaking
127 # things.
128 self.needs_parity_workaround = platform.system() == "linux" and os.path.exists("/etc/debian")
129 for handler in self.event_handler:
130 try: handler.on_init()
131 except: logging.error(traceback.format_exc())
111 if port is not None and baud is not None: 132 if port is not None and baud is not None:
112 self.connect(port, baud) 133 self.connect(port, baud)
113 self.xy_feedrate = None 134 self.xy_feedrate = None
114 self.z_feedrate = None 135 self.z_feedrate = None
115 136
137 def addEventHandler(self, handler):
138 '''
139 Adds an event handler.
140
141 @param handler: The handler to be added.
142 '''
143 self.event_handler.append(handler)
144
116 def logError(self, error): 145 def logError(self, error):
146 for handler in self.event_handler:
147 try: handler.on_error(error)
148 except: logging.error(traceback.format_exc())
117 if self.errorcb: 149 if self.errorcb:
118 try: self.errorcb(error) 150 try: self.errorcb(error)
119 except: logging.error(traceback.format_exc()) 151 except: logging.error(traceback.format_exc())
120 else: 152 else:
121 logging.error(error) 153 logging.error(error)
133 if self.print_thread: 165 if self.print_thread:
134 self.printing = False 166 self.printing = False
135 self.print_thread.join() 167 self.print_thread.join()
136 self._stop_sender() 168 self._stop_sender()
137 try: 169 try:
170 if self.selector is not None:
171 self.selector.unregister(self.printer_tcp)
172 self.selector.close()
173 self.selector = None
174 if self.printer_tcp is not None:
175 self.printer_tcp.close()
176 self.printer_tcp = None
138 self.printer.close() 177 self.printer.close()
139 except socket.error: 178 except socket.error:
179 logger.error(traceback.format_exc())
140 pass 180 pass
141 except OSError: 181 except OSError:
182 logger.error(traceback.format_exc())
142 pass 183 pass
184 for handler in self.event_handler:
185 try: handler.on_disconnect()
186 except: logging.error(traceback.format_exc())
143 self.printer = None 187 self.printer = None
144 self.online = False 188 self.online = False
145 self.printing = False 189 self.printing = False
146 190
147 @locked 191 @locked
158 self.dtr = dtr 202 self.dtr = dtr
159 if self.port is not None and self.baud is not None: 203 if self.port is not None and self.baud is not None:
160 # Connect to socket if "port" is an IP, device if not 204 # Connect to socket if "port" is an IP, device if not
161 host_regexp = re.compile("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$") 205 host_regexp = re.compile("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$")
162 is_serial = True 206 is_serial = True
163 if ":" in port: 207 if ":" in self.port:
164 bits = port.split(":") 208 bits = self.port.split(":")
165 if len(bits) == 2: 209 if len(bits) == 2:
166 hostname = bits[0] 210 hostname = bits[0]
167 try: 211 try:
168 port = int(bits[1]) 212 port_number = int(bits[1])
169 if host_regexp.match(hostname) and 1 <= port <= 65535: 213 if host_regexp.match(hostname) and 1 <= port_number <= 65535:
170 is_serial = False 214 is_serial = False
171 except: 215 except:
172 pass 216 pass
173 self.writefailures = 0 217 self.writefailures = 0
174 if not is_serial: 218 if not is_serial:
176 socket.SOCK_STREAM) 220 socket.SOCK_STREAM)
177 self.printer_tcp.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) 221 self.printer_tcp.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
178 self.timeout = 0.25 222 self.timeout = 0.25
179 self.printer_tcp.settimeout(1.0) 223 self.printer_tcp.settimeout(1.0)
180 try: 224 try:
181 self.printer_tcp.connect((hostname, port)) 225 self.printer_tcp.connect((hostname, port_number))
182 self.printer_tcp.settimeout(self.timeout) 226 #a single read timeout raises OSError for all later reads
183 self.printer = self.printer_tcp.makefile() 227 #probably since python 3.5
228 #use non blocking instead
229 self.printer_tcp.settimeout(0)
230 self.printer = self.printer_tcp.makefile('rwb', buffering=0)
231 self.selector = selectors.DefaultSelector()
232 self.selector.register(self.printer_tcp, selectors.EVENT_READ)
184 except socket.error as e: 233 except socket.error as e:
185 if(e.strerror is None): e.strerror="" 234 if(e.strerror is None): e.strerror=""
186 self.logError(_("Could not connect to %s:%s:") % (hostname, port) + 235 self.logError(_("Could not connect to %s:%s:") % (hostname, port_number) +
187 "\n" + _("Socket error %s:") % e.errno + 236 "\n" + _("Socket error %s:") % e.errno +
188 "\n" + e.strerror) 237 "\n" + e.strerror)
189 self.printer = None 238 self.printer = None
190 self.printer_tcp = None 239 self.printer_tcp = None
191 return 240 return
192 else: 241 else:
193 disable_hup(self.port) 242 disable_hup(self.port)
194 self.printer_tcp = None 243 self.printer_tcp = None
195 try: 244 try:
196 self.printer = Serial(port = self.port, 245 if self.needs_parity_workaround:
197 baudrate = self.baud, 246 self.printer = Serial(port = self.port,
198 timeout = 0.25, 247 baudrate = self.baud,
199 parity = PARITY_ODD) 248 timeout = 0.25,
200 self.printer.close() 249 parity = PARITY_ODD)
201 self.printer.parity = PARITY_NONE 250 self.printer.close()
251 self.printer.parity = PARITY_NONE
252 else:
253 self.printer = Serial(baudrate = self.baud,
254 timeout = 0.25,
255 parity = PARITY_NONE)
256 self.printer.port = self.port
202 try: #this appears not to work on many platforms, so we're going to call it but not care if it fails 257 try: #this appears not to work on many platforms, so we're going to call it but not care if it fails
203 self.printer.setDTR(dtr); 258 self.printer.dtr = dtr
204 except: 259 except:
205 #self.logError(_("Could not set DTR on this platform")) #not sure whether to output an error message 260 #self.logError(_("Could not set DTR on this platform")) #not sure whether to output an error message
206 pass 261 pass
207 self.printer.open() 262 self.printer.open()
208 except SerialException as e: 263 except SerialException as e:
213 except IOError as e: 268 except IOError as e:
214 self.logError(_("Could not connect to %s at baudrate %s:") % (self.port, self.baud) + 269 self.logError(_("Could not connect to %s at baudrate %s:") % (self.port, self.baud) +
215 "\n" + _("IO error: %s") % e) 270 "\n" + _("IO error: %s") % e)
216 self.printer = None 271 self.printer = None
217 return 272 return
273 for handler in self.event_handler:
274 try: handler.on_connect()
275 except: logging.error(traceback.format_exc())
218 self.stop_read_thread = False 276 self.stop_read_thread = False
219 self.read_thread = threading.Thread(target = self._listen) 277 self.read_thread = threading.Thread(target = self._listen,
278 name='read thread')
220 self.read_thread.start() 279 self.read_thread.start()
221 self._start_sender() 280 self._start_sender()
222 281
223 def reset(self): 282 def reset(self):
224 """Reset the printer 283 """Reset the printer
225 """ 284 """
226 if self.printer and not self.printer_tcp: 285 if self.printer and not self.printer_tcp:
227 self.printer.setDTR(1) 286 self.printer.dtr = 1
228 time.sleep(0.2) 287 time.sleep(0.2)
229 self.printer.setDTR(0) 288 self.printer.dtr = 0
289
290 def _readline_buf(self):
291 "Try to readline from buffer"
292 if len(self.readline_buf):
293 chunk = self.readline_buf[-1]
294 eol = chunk.find(b'\n')
295 if eol >= 0:
296 line = b''.join(self.readline_buf[:-1]) + chunk[:(eol+1)]
297 self.readline_buf = []
298 if eol + 1 < len(chunk):
299 self.readline_buf.append(chunk[(eol+1):])
300 return line
301 return PR_AGAIN
302
303 def _readline_nb(self):
304 "Non blocking readline. Socket based files do not support non blocking or timeouting readline"
305 if self.printer_tcp:
306 line = self._readline_buf()
307 if line:
308 return line
309 chunk_size = 256
310 while True:
311 chunk = self.printer.read(chunk_size)
312 if chunk is SYS_AGAIN and self.selector.select(self.timeout):
313 chunk = self.printer.read(chunk_size)
314 #print('_readline_nb chunk', chunk, type(chunk))
315 if chunk:
316 self.readline_buf.append(chunk)
317 line = self._readline_buf()
318 if line:
319 return line
320 elif chunk is SYS_AGAIN:
321 return PR_AGAIN
322 else:
323 #chunk == b'' means EOF
324 line = b''.join(self.readline_buf)
325 self.readline_buf = []
326 self.stop_read_thread = True
327 return line if line else PR_EOF
328 else: # serial port
329 return self.printer.readline()
230 330
231 def _readline(self): 331 def _readline(self):
232 try: 332 try:
233 try: 333 line_bytes = self._readline_nb()
234 line = self.printer.readline() 334 if line_bytes is PR_EOF:
235 if self.printer_tcp and not line: 335 self.logError(_("Can't read from printer (disconnected?). line_bytes is None"))
236 raise OSError(-1, "Read EOF from socket") 336 return PR_EOF
237 except socket.timeout: 337 line = line_bytes.decode('utf-8')
238 return ""
239 338
240 if len(line) > 1: 339 if len(line) > 1:
241 self.log.append(line) 340 self.log.append(line)
341 for handler in self.event_handler:
342 try: handler.on_recv(line)
343 except: logging.error(traceback.format_exc())
242 if self.recvcb: 344 if self.recvcb:
243 try: self.recvcb(line) 345 try: self.recvcb(line)
244 except: self.logError(traceback.format_exc()) 346 except: self.logError(traceback.format_exc())
245 if self.loud: logging.info("RECV: %s" % line.rstrip()) 347 if self.loud: logging.info("RECV: %s" % line.rstrip())
246 return line 348 return line
349 except UnicodeDecodeError:
350 self.logError(_("Got rubbish reply from %s at baudrate %s:") % (self.port, self.baud) +
351 "\n" + _("Maybe a bad baudrate?"))
352 return None
247 except SelectError as e: 353 except SelectError as e:
248 if 'Bad file descriptor' in e.args[1]: 354 if 'Bad file descriptor' in e.args[1]:
249 self.logError(_(u"Can't read from printer (disconnected?) (SelectError {0}): {1}").format(e.errno, decode_utf8(e.strerror))) 355 self.logError(_("Can't read from printer (disconnected?) (SelectError {0}): {1}").format(e.errno, decode_utf8(e.strerror)))
250 return None 356 return None
251 else: 357 else:
252 self.logError(_(u"SelectError ({0}): {1}").format(e.errno, decode_utf8(e.strerror))) 358 self.logError(_("SelectError ({0}): {1}").format(e.errno, decode_utf8(e.strerror)))
253 raise 359 raise
254 except SerialException as e: 360 except SerialException as e:
255 self.logError(_(u"Can't read from printer (disconnected?) (SerialException): {0}").format(decode_utf8(str(e)))) 361 self.logError(_("Can't read from printer (disconnected?) (SerialException): {0}").format(decode_utf8(str(e))))
256 return None 362 return None
257 except socket.error as e: 363 except socket.error as e:
258 self.logError(_(u"Can't read from printer (disconnected?) (Socket error {0}): {1}").format(e.errno, decode_utf8(e.strerror))) 364 self.logError(_("Can't read from printer (disconnected?) (Socket error {0}): {1}").format(e.errno, decode_utf8(e.strerror)))
259 return None 365 return None
260 except OSError as e: 366 except OSError as e:
261 if e.errno == errno.EAGAIN: # Not a real error, no data was available 367 if e.errno == errno.EAGAIN: # Not a real error, no data was available
262 return "" 368 return ""
263 self.logError(_(u"Can't read from printer (disconnected?) (OS Error {0}): {1}").format(e.errno, e.strerror)) 369 self.logError(_("Can't read from printer (disconnected?) (OS Error {0}): {1}").format(e.errno, e.strerror))
264 return None 370 return None
265 371
266 def _listen_can_continue(self): 372 def _listen_can_continue(self):
267 if self.printer_tcp: 373 if self.printer_tcp:
268 return not self.stop_read_thread and self.printer 374 return not self.stop_read_thread and self.printer
269 return (not self.stop_read_thread 375 return (not self.stop_read_thread
270 and self.printer 376 and self.printer
271 and self.printer.isOpen()) 377 and self.printer.is_open)
272 378
273 def _listen_until_online(self): 379 def _listen_until_online(self):
274 while not self.online and self._listen_can_continue(): 380 while not self.online and self._listen_can_continue():
275 self._send("M105") 381 self._send("M105")
276 if self.writefailures >= 4: 382 if self.writefailures >= 4:
294 if empty_lines == 15: break 400 if empty_lines == 15: break
295 else: empty_lines = 0 401 else: empty_lines = 0
296 if line.startswith(tuple(self.greetings)) \ 402 if line.startswith(tuple(self.greetings)) \
297 or line.startswith('ok') or "T:" in line: 403 or line.startswith('ok') or "T:" in line:
298 self.online = True 404 self.online = True
405 for handler in self.event_handler:
406 try: handler.on_online()
407 except: logging.error(traceback.format_exc())
299 if self.onlinecb: 408 if self.onlinecb:
300 try: self.onlinecb() 409 try: self.onlinecb()
301 except: self.logError(traceback.format_exc()) 410 except: self.logError(traceback.format_exc())
302 return 411 return
303 412
308 if not self.printing: 417 if not self.printing:
309 self._listen_until_online() 418 self._listen_until_online()
310 while self._listen_can_continue(): 419 while self._listen_can_continue():
311 line = self._readline() 420 line = self._readline()
312 if line is None: 421 if line is None:
422 logging.debug('_readline() is None, exiting _listen()')
313 break 423 break
314 if line.startswith('DEBUG_'): 424 if line.startswith('DEBUG_'):
315 continue 425 continue
316 if line.startswith(tuple(self.greetings)) or line.startswith('ok'): 426 if line.startswith(tuple(self.greetings)) or line.startswith('ok'):
317 self.clear = True 427 self.clear = True
318 if line.startswith('ok') and "T:" in line and self.tempcb: 428 if line.startswith('ok') and "T:" in line:
319 # callback for temp, status, whatever 429 for handler in self.event_handler:
320 try: self.tempcb(line) 430 try: handler.on_temp(line)
321 except: self.logError(traceback.format_exc()) 431 except: logging.error(traceback.format_exc())
432 if self.tempcb:
433 # callback for temp, status, whatever
434 try: self.tempcb(line)
435 except: self.logError(traceback.format_exc())
322 elif line.startswith('Error'): 436 elif line.startswith('Error'):
323 self.logError(line) 437 self.logError(line)
324 # Teststrings for resend parsing # Firmware exp. result 438 # Teststrings for resend parsing # Firmware exp. result
325 # line="rs N2 Expected checksum 67" # Teacup 2 439 # line="rs N2 Expected checksum 67" # Teacup 2
326 if line.lower().startswith("resend") or line.startswith("rs"): 440 if line.lower().startswith("resend") or line.startswith("rs"):
334 break 448 break
335 except: 449 except:
336 pass 450 pass
337 self.clear = True 451 self.clear = True
338 self.clear = True 452 self.clear = True
453 logging.debug('Exiting read thread')
339 454
340 def _start_sender(self): 455 def _start_sender(self):
341 self.stop_send_thread = False 456 self.stop_send_thread = False
342 self.send_thread = threading.Thread(target = self._sender) 457 self.send_thread = threading.Thread(target = self._sender,
458 name = 'send thread')
343 self.send_thread.start() 459 self.send_thread.start()
344 460
345 def _stop_sender(self): 461 def _stop_sender(self):
346 if self.send_thread: 462 if self.send_thread:
347 self.stop_send_thread = True 463 self.stop_send_thread = True
381 if not gcode or not gcode.lines: 497 if not gcode or not gcode.lines:
382 return True 498 return True
383 self.clear = False 499 self.clear = False
384 resuming = (startindex != 0) 500 resuming = (startindex != 0)
385 self.print_thread = threading.Thread(target = self._print, 501 self.print_thread = threading.Thread(target = self._print,
502 name = 'print thread',
386 kwargs = {"resuming": resuming}) 503 kwargs = {"resuming": resuming})
387 self.print_thread.start() 504 self.print_thread.start()
388 return True 505 return True
389 506
390 def cancelprint(self): 507 def cancelprint(self):
411 """ 528 """
412 if not self.printing: return False 529 if not self.printing: return False
413 self.paused = True 530 self.paused = True
414 self.printing = False 531 self.printing = False
415 532
416 # try joining the print thread: enclose it in try/except because we 533 # ';@pause' in the gcode file calls pause from the print thread
417 # might be calling it from the thread itself 534 if not threading.current_thread() is self.print_thread:
418 try: 535 try:
419 self.print_thread.join() 536 self.print_thread.join()
420 except RuntimeError, e: 537 except:
421 if e.message == "cannot join current thread":
422 pass
423 else:
424 self.logError(traceback.format_exc()) 538 self.logError(traceback.format_exc())
425 except:
426 self.logError(traceback.format_exc())
427 539
428 self.print_thread = None 540 self.print_thread = None
429 541
430 # saves the status 542 # saves the status
431 self.pauseX = self.analyzer.abs_x 543 self.pauseX = self.analyzer.abs_x
432 self.pauseY = self.analyzer.abs_y 544 self.pauseY = self.analyzer.abs_y
433 self.pauseZ = self.analyzer.abs_z 545 self.pauseZ = self.analyzer.abs_z
434 self.pauseE = self.analyzer.abs_e 546 self.pauseE = self.analyzer.abs_e
435 self.pauseF = self.analyzer.current_f 547 self.pauseF = self.analyzer.current_f
436 self.pauseRelative = self.analyzer.relative 548 self.pauseRelative = self.analyzer.relative
549 self.pauseRelativeE = self.analyzer.relative_e
437 550
438 def resume(self): 551 def resume(self):
439 """Resumes a paused print. 552 """Resumes a paused print."""
440 """
441 if not self.paused: return False 553 if not self.paused: return False
442 if self.paused: 554 # restores the status
443 # restores the status 555 self.send_now("G90") # go to absolute coordinates
444 self.send_now("G90") # go to absolute coordinates 556
445 557 xyFeed = '' if self.xy_feedrate is None else ' F' + str(self.xy_feedrate)
446 xyFeedString = "" 558 zFeed = '' if self.z_feedrate is None else ' F' + str(self.z_feedrate)
447 zFeedString = "" 559
448 if self.xy_feedrate is not None: 560 self.send_now("G1 X%s Y%s%s" % (self.pauseX, self.pauseY, xyFeed))
449 xyFeedString = " F" + str(self.xy_feedrate) 561 self.send_now("G1 Z" + str(self.pauseZ) + zFeed)
450 if self.z_feedrate is not None: 562 self.send_now("G92 E" + str(self.pauseE))
451 zFeedString = " F" + str(self.z_feedrate) 563
452 564 # go back to relative if needed
453 self.send_now("G1 X%s Y%s%s" % (self.pauseX, self.pauseY, 565 if self.pauseRelative:
454 xyFeedString)) 566 self.send_now("G91")
455 self.send_now("G1 Z" + str(self.pauseZ) + zFeedString) 567 if self.pauseRelativeE:
456 self.send_now("G92 E" + str(self.pauseE)) 568 self.send_now('M83')
457 569 # reset old feed rate
458 # go back to relative if needed 570 self.send_now("G1 F" + str(self.pauseF))
459 if self.pauseRelative: self.send_now("G91")
460 # reset old feed rate
461 self.send_now("G1 F" + str(self.pauseF))
462 571
463 self.paused = False 572 self.paused = False
464 self.printing = True 573 self.printing = True
465 self.print_thread = threading.Thread(target = self._print, 574 self.print_thread = threading.Thread(target = self._print,
575 name = 'print thread',
466 kwargs = {"resuming": True}) 576 kwargs = {"resuming": True})
467 self.print_thread.start() 577 self.print_thread.start()
468 578
469 def send(self, command, wait = 0): 579 def send(self, command, wait = 0):
470 """Adds a command to the checksummed main command queue if printing, or 580 """Adds a command to the checksummed main command queue if printing, or
487 self.logError(_("Not connected to printer.")) 597 self.logError(_("Not connected to printer."))
488 598
489 def _print(self, resuming = False): 599 def _print(self, resuming = False):
490 self._stop_sender() 600 self._stop_sender()
491 try: 601 try:
602 for handler in self.event_handler:
603 try: handler.on_start(resuming)
604 except: logging.error(traceback.format_exc())
492 if self.startcb: 605 if self.startcb:
493 # callback for printing started 606 # callback for printing started
494 try: self.startcb(resuming) 607 try: self.startcb(resuming)
495 except: 608 except:
496 self.logError(_("Print start callback failed with:") + 609 self.logError(_("Print start callback failed with:") +
498 while self.printing and self.printer and self.online: 611 while self.printing and self.printer and self.online:
499 self._sendnext() 612 self._sendnext()
500 self.sentlines = {} 613 self.sentlines = {}
501 self.log.clear() 614 self.log.clear()
502 self.sent = [] 615 self.sent = []
616 for handler in self.event_handler:
617 try: handler.on_end()
618 except: logging.error(traceback.format_exc())
503 if self.endcb: 619 if self.endcb:
504 # callback for printing done 620 # callback for printing done
505 try: self.endcb() 621 try: self.endcb()
506 except: 622 except:
507 self.logError(_("Print end callback failed with:") + 623 self.logError(_("Print end callback failed with:") +
538 self.resendfrom = -1 654 self.resendfrom = -1
539 if not self.priqueue.empty(): 655 if not self.priqueue.empty():
540 self._send(self.priqueue.get_nowait()) 656 self._send(self.priqueue.get_nowait())
541 self.priqueue.task_done() 657 self.priqueue.task_done()
542 return 658 return
543 if self.printing and self.queueindex < len(self.mainqueue): 659 if self.printing and self.mainqueue.has_index(self.queueindex):
544 (layer, line) = self.mainqueue.idxs(self.queueindex) 660 (layer, line) = self.mainqueue.idxs(self.queueindex)
545 gline = self.mainqueue.all_layers[layer][line] 661 gline = self.mainqueue.all_layers[layer][line]
662 if self.queueindex > 0:
663 (prev_layer, prev_line) = self.mainqueue.idxs(self.queueindex - 1)
664 if prev_layer != layer:
665 for handler in self.event_handler:
666 try: handler.on_layerchange(layer)
667 except: logging.error(traceback.format_exc())
546 if self.layerchangecb and self.queueindex > 0: 668 if self.layerchangecb and self.queueindex > 0:
547 (prev_layer, prev_line) = self.mainqueue.idxs(self.queueindex - 1) 669 (prev_layer, prev_line) = self.mainqueue.idxs(self.queueindex - 1)
548 if prev_layer != layer: 670 if prev_layer != layer:
549 try: self.layerchangecb(layer) 671 try: self.layerchangecb(layer)
550 except: self.logError(traceback.format_exc()) 672 except: self.logError(traceback.format_exc())
673 for handler in self.event_handler:
674 try: handler.on_preprintsend(gline, self.queueindex, self.mainqueue)
675 except: logging.error(traceback.format_exc())
551 if self.preprintsendcb: 676 if self.preprintsendcb:
552 if self.queueindex + 1 < len(self.mainqueue): 677 if self.mainqueue.has_index(self.queueindex + 1):
553 (next_layer, next_line) = self.mainqueue.idxs(self.queueindex + 1) 678 (next_layer, next_line) = self.mainqueue.idxs(self.queueindex + 1)
554 next_gline = self.mainqueue.all_layers[next_layer][next_line] 679 next_gline = self.mainqueue.all_layers[next_layer][next_line]
555 else: 680 else:
556 next_gline = None 681 next_gline = None
557 gline = self.preprintsendcb(gline, next_gline) 682 gline = self.preprintsendcb(gline, next_gline)
569 # Strip comments 694 # Strip comments
570 tline = gcoder.gcode_strip_comment_exp.sub("", tline).strip() 695 tline = gcoder.gcode_strip_comment_exp.sub("", tline).strip()
571 if tline: 696 if tline:
572 self._send(tline, self.lineno, True) 697 self._send(tline, self.lineno, True)
573 self.lineno += 1 698 self.lineno += 1
699 for handler in self.event_handler:
700 try: handler.on_printsend(gline)
701 except: logging.error(traceback.format_exc())
574 if self.printsendcb: 702 if self.printsendcb:
575 try: self.printsendcb(gline) 703 try: self.printsendcb(gline)
576 except: self.logError(traceback.format_exc()) 704 except: self.logError(traceback.format_exc())
577 else: 705 else:
578 self.clear = True 706 self.clear = True
601 except: 729 except:
602 logging.warning(_("Could not analyze command %s:") % command + 730 logging.warning(_("Could not analyze command %s:") % command +
603 "\n" + traceback.format_exc()) 731 "\n" + traceback.format_exc())
604 if self.loud: 732 if self.loud:
605 logging.info("SENT: %s" % command) 733 logging.info("SENT: %s" % command)
734
735 for handler in self.event_handler:
736 try: handler.on_send(command, gline)
737 except: logging.error(traceback.format_exc())
606 if self.sendcb: 738 if self.sendcb:
607 try: self.sendcb(command, gline) 739 try: self.sendcb(command, gline)
608 except: self.logError(traceback.format_exc()) 740 except: self.logError(traceback.format_exc())
609 try: 741 try:
610 self.printer.write(str(command + "\n")) 742 self.printer.write((command + "\n").encode('ascii'))
611 if self.printer_tcp: 743 if self.printer_tcp:
612 try: 744 try:
613 self.printer.flush() 745 self.printer.flush()
614 except socket.timeout: 746 except socket.timeout:
615 pass 747 pass
616 self.writefailures = 0 748 self.writefailures = 0
617 except socket.error as e: 749 except socket.error as e:
618 if e.errno is None: 750 if e.errno is None:
619 self.logError(_(u"Can't write to printer (disconnected ?):") + 751 self.logError(_("Can't write to printer (disconnected ?):") +
620 "\n" + traceback.format_exc()) 752 "\n" + traceback.format_exc())
621 else: 753 else:
622 self.logError(_(u"Can't write to printer (disconnected?) (Socket error {0}): {1}").format(e.errno, decode_utf8(e.strerror))) 754 self.logError(_("Can't write to printer (disconnected?) (Socket error {0}): {1}").format(e.errno, decode_utf8(e.strerror)))
623 self.writefailures += 1 755 self.writefailures += 1
624 except SerialException as e: 756 except SerialException as e:
625 self.logError(_(u"Can't write to printer (disconnected?) (SerialException): {0}").format(decode_utf8(str(e)))) 757 self.logError(_("Can't write to printer (disconnected?) (SerialException): {0}").format(decode_utf8(str(e))))
626 self.writefailures += 1 758 self.writefailures += 1
627 except RuntimeError as e: 759 except RuntimeError as e:
628 self.logError(_(u"Socket connection broken, disconnected. ({0}): {1}").format(e.errno, decode_utf8(e.strerror))) 760 self.logError(_("Socket connection broken, disconnected. ({0}): {1}").format(e.errno, decode_utf8(e.strerror)))
629 self.writefailures += 1 761 self.writefailures += 1

mercurial