|
1 #!/usr/bin/env python |
|
2 |
|
3 # This file is part of the Printrun suite. |
|
4 # |
|
5 # 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 |
|
7 # the Free Software Foundation, either version 3 of the License, or |
|
8 # (at your option) any later version. |
|
9 # |
|
10 # Printrun is distributed in the hope that it will be useful, |
|
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 # GNU General Public License for more details. |
|
14 # |
|
15 # You should have received a copy of the GNU General Public License |
|
16 # along with Printrun. If not, see <http://www.gnu.org/licenses/>. |
|
17 |
|
18 __version__ = "2015.03.10" |
|
19 |
|
20 from serialWrapper import Serial, SerialException, PARITY_ODD, PARITY_NONE |
|
21 from select import error as SelectError |
|
22 import threading |
|
23 from Queue import Queue, Empty as QueueEmpty |
|
24 import time |
|
25 import platform |
|
26 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 |
|
32 import traceback |
|
33 import errno |
|
34 import socket |
|
35 import re |
|
36 from functools import wraps |
|
37 from collections import deque |
|
38 from printrun import gcoder |
|
39 from .utils import install_locale, decode_utf8 |
|
40 install_locale('pronterface') |
|
41 |
|
42 def locked(f): |
|
43 @wraps(f) |
|
44 def inner(*args, **kw): |
|
45 with inner.lock: |
|
46 return f(*args, **kw) |
|
47 inner.lock = threading.Lock() |
|
48 return inner |
|
49 |
|
50 def control_ttyhup(port, disable_hup): |
|
51 """Controls the HUPCL""" |
|
52 if platform.system() == "Linux": |
|
53 if disable_hup: |
|
54 os.system("stty -F %s -hup" % port) |
|
55 else: |
|
56 os.system("stty -F %s hup" % port) |
|
57 |
|
58 def enable_hup(port): |
|
59 control_ttyhup(port, False) |
|
60 |
|
61 def disable_hup(port): |
|
62 control_ttyhup(port, True) |
|
63 |
|
64 class printcore(): |
|
65 def __init__(self, port = None, baud = None, dtr=None): |
|
66 """Initializes a printcore instance. Pass the port and baud rate to |
|
67 connect immediately""" |
|
68 self.baud = None |
|
69 self.dtr = None |
|
70 self.port = None |
|
71 self.analyzer = gcoder.GCode() |
|
72 # Serial instance connected to the printer, should be None when |
|
73 # disconnected |
|
74 self.printer = None |
|
75 # clear to send, enabled after responses |
|
76 # FIXME: should probably be changed to a sliding window approach |
|
77 self.clear = 0 |
|
78 # The printer has responded to the initial command and is active |
|
79 self.online = False |
|
80 # is a print currently running, true if printing, false if paused |
|
81 self.printing = False |
|
82 self.mainqueue = None |
|
83 self.priqueue = Queue(0) |
|
84 self.queueindex = 0 |
|
85 self.lineno = 0 |
|
86 self.resendfrom = -1 |
|
87 self.paused = False |
|
88 self.sentlines = {} |
|
89 self.log = deque(maxlen = 10000) |
|
90 self.sent = [] |
|
91 self.writefailures = 0 |
|
92 self.tempcb = None # impl (wholeline) |
|
93 self.recvcb = None # impl (wholeline) |
|
94 self.sendcb = None # impl (wholeline) |
|
95 self.preprintsendcb = None # impl (wholeline) |
|
96 self.printsendcb = None # impl (wholeline) |
|
97 self.layerchangecb = None # impl (wholeline) |
|
98 self.errorcb = None # impl (wholeline) |
|
99 self.startcb = None # impl () |
|
100 self.endcb = None # impl () |
|
101 self.onlinecb = None # impl () |
|
102 self.loud = False # emit sent and received lines to terminal |
|
103 self.tcp_streaming_mode = False |
|
104 self.greetings = ['start', 'Grbl '] |
|
105 self.wait = 0 # default wait period for send(), send_now() |
|
106 self.read_thread = None |
|
107 self.stop_read_thread = False |
|
108 self.send_thread = None |
|
109 self.stop_send_thread = False |
|
110 self.print_thread = None |
|
111 if port is not None and baud is not None: |
|
112 self.connect(port, baud) |
|
113 self.xy_feedrate = None |
|
114 self.z_feedrate = None |
|
115 |
|
116 def logError(self, error): |
|
117 if self.errorcb: |
|
118 try: self.errorcb(error) |
|
119 except: logging.error(traceback.format_exc()) |
|
120 else: |
|
121 logging.error(error) |
|
122 |
|
123 @locked |
|
124 def disconnect(self): |
|
125 """Disconnects from printer and pauses the print |
|
126 """ |
|
127 if self.printer: |
|
128 if self.read_thread: |
|
129 self.stop_read_thread = True |
|
130 if threading.current_thread() != self.read_thread: |
|
131 self.read_thread.join() |
|
132 self.read_thread = None |
|
133 if self.print_thread: |
|
134 self.printing = False |
|
135 self.print_thread.join() |
|
136 self._stop_sender() |
|
137 try: |
|
138 self.printer.close() |
|
139 except socket.error: |
|
140 pass |
|
141 except OSError: |
|
142 pass |
|
143 self.printer = None |
|
144 self.online = False |
|
145 self.printing = False |
|
146 |
|
147 @locked |
|
148 def connect(self, port = None, baud = None, dtr=None): |
|
149 """Set port and baudrate if given, then connect to printer |
|
150 """ |
|
151 if self.printer: |
|
152 self.disconnect() |
|
153 if port is not None: |
|
154 self.port = port |
|
155 if baud is not None: |
|
156 self.baud = baud |
|
157 if dtr is not None: |
|
158 self.dtr = dtr |
|
159 if self.port is not None and self.baud is not None: |
|
160 # 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])$") |
|
162 is_serial = True |
|
163 if ":" in port: |
|
164 bits = port.split(":") |
|
165 if len(bits) == 2: |
|
166 hostname = bits[0] |
|
167 try: |
|
168 port = int(bits[1]) |
|
169 if host_regexp.match(hostname) and 1 <= port <= 65535: |
|
170 is_serial = False |
|
171 except: |
|
172 pass |
|
173 self.writefailures = 0 |
|
174 if not is_serial: |
|
175 self.printer_tcp = socket.socket(socket.AF_INET, |
|
176 socket.SOCK_STREAM) |
|
177 self.printer_tcp.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) |
|
178 self.timeout = 0.25 |
|
179 self.printer_tcp.settimeout(1.0) |
|
180 try: |
|
181 self.printer_tcp.connect((hostname, port)) |
|
182 self.printer_tcp.settimeout(self.timeout) |
|
183 self.printer = self.printer_tcp.makefile() |
|
184 except socket.error as e: |
|
185 if(e.strerror is None): e.strerror="" |
|
186 self.logError(_("Could not connect to %s:%s:") % (hostname, port) + |
|
187 "\n" + _("Socket error %s:") % e.errno + |
|
188 "\n" + e.strerror) |
|
189 self.printer = None |
|
190 self.printer_tcp = None |
|
191 return |
|
192 else: |
|
193 disable_hup(self.port) |
|
194 self.printer_tcp = None |
|
195 try: |
|
196 self.printer = Serial(port = self.port, |
|
197 baudrate = self.baud, |
|
198 timeout = 0.25, |
|
199 parity = PARITY_ODD) |
|
200 self.printer.close() |
|
201 self.printer.parity = PARITY_NONE |
|
202 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); |
|
204 except: |
|
205 #self.logError(_("Could not set DTR on this platform")) #not sure whether to output an error message |
|
206 pass |
|
207 self.printer.open() |
|
208 except SerialException as e: |
|
209 self.logError(_("Could not connect to %s at baudrate %s:") % (self.port, self.baud) + |
|
210 "\n" + _("Serial error: %s") % e) |
|
211 self.printer = None |
|
212 return |
|
213 except IOError as e: |
|
214 self.logError(_("Could not connect to %s at baudrate %s:") % (self.port, self.baud) + |
|
215 "\n" + _("IO error: %s") % e) |
|
216 self.printer = None |
|
217 return |
|
218 self.stop_read_thread = False |
|
219 self.read_thread = threading.Thread(target = self._listen) |
|
220 self.read_thread.start() |
|
221 self._start_sender() |
|
222 |
|
223 def reset(self): |
|
224 """Reset the printer |
|
225 """ |
|
226 if self.printer and not self.printer_tcp: |
|
227 self.printer.setDTR(1) |
|
228 time.sleep(0.2) |
|
229 self.printer.setDTR(0) |
|
230 |
|
231 def _readline(self): |
|
232 try: |
|
233 try: |
|
234 line = self.printer.readline() |
|
235 if self.printer_tcp and not line: |
|
236 raise OSError(-1, "Read EOF from socket") |
|
237 except socket.timeout: |
|
238 return "" |
|
239 |
|
240 if len(line) > 1: |
|
241 self.log.append(line) |
|
242 if self.recvcb: |
|
243 try: self.recvcb(line) |
|
244 except: self.logError(traceback.format_exc()) |
|
245 if self.loud: logging.info("RECV: %s" % line.rstrip()) |
|
246 return line |
|
247 except SelectError as e: |
|
248 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))) |
|
250 return None |
|
251 else: |
|
252 self.logError(_(u"SelectError ({0}): {1}").format(e.errno, decode_utf8(e.strerror))) |
|
253 raise |
|
254 except SerialException as e: |
|
255 self.logError(_(u"Can't read from printer (disconnected?) (SerialException): {0}").format(decode_utf8(str(e)))) |
|
256 return None |
|
257 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))) |
|
259 return None |
|
260 except OSError as e: |
|
261 if e.errno == errno.EAGAIN: # Not a real error, no data was available |
|
262 return "" |
|
263 self.logError(_(u"Can't read from printer (disconnected?) (OS Error {0}): {1}").format(e.errno, e.strerror)) |
|
264 return None |
|
265 |
|
266 def _listen_can_continue(self): |
|
267 if self.printer_tcp: |
|
268 return not self.stop_read_thread and self.printer |
|
269 return (not self.stop_read_thread |
|
270 and self.printer |
|
271 and self.printer.isOpen()) |
|
272 |
|
273 def _listen_until_online(self): |
|
274 while not self.online and self._listen_can_continue(): |
|
275 self._send("M105") |
|
276 if self.writefailures >= 4: |
|
277 logging.error(_("Aborting connection attempt after 4 failed writes.")) |
|
278 return |
|
279 empty_lines = 0 |
|
280 while self._listen_can_continue(): |
|
281 line = self._readline() |
|
282 if line is None: break # connection problem |
|
283 # workaround cases where M105 was sent before printer Serial |
|
284 # was online an empty line means read timeout was reached, |
|
285 # meaning no data was received thus we count those empty lines, |
|
286 # and once we have seen 15 in a row, we just break and send a |
|
287 # new M105 |
|
288 # 15 was chosen based on the fact that it gives enough time for |
|
289 # Gen7 bootloader to time out, and that the non received M105 |
|
290 # issues should be quite rare so we can wait for a long time |
|
291 # before resending |
|
292 if not line: |
|
293 empty_lines += 1 |
|
294 if empty_lines == 15: break |
|
295 else: empty_lines = 0 |
|
296 if line.startswith(tuple(self.greetings)) \ |
|
297 or line.startswith('ok') or "T:" in line: |
|
298 self.online = True |
|
299 if self.onlinecb: |
|
300 try: self.onlinecb() |
|
301 except: self.logError(traceback.format_exc()) |
|
302 return |
|
303 |
|
304 def _listen(self): |
|
305 """This function acts on messages from the firmware |
|
306 """ |
|
307 self.clear = True |
|
308 if not self.printing: |
|
309 self._listen_until_online() |
|
310 while self._listen_can_continue(): |
|
311 line = self._readline() |
|
312 if line is None: |
|
313 break |
|
314 if line.startswith('DEBUG_'): |
|
315 continue |
|
316 if line.startswith(tuple(self.greetings)) or line.startswith('ok'): |
|
317 self.clear = True |
|
318 if line.startswith('ok') and "T:" in line and self.tempcb: |
|
319 # callback for temp, status, whatever |
|
320 try: self.tempcb(line) |
|
321 except: self.logError(traceback.format_exc()) |
|
322 elif line.startswith('Error'): |
|
323 self.logError(line) |
|
324 # Teststrings for resend parsing # Firmware exp. result |
|
325 # line="rs N2 Expected checksum 67" # Teacup 2 |
|
326 if line.lower().startswith("resend") or line.startswith("rs"): |
|
327 for haystack in ["N:", "N", ":"]: |
|
328 line = line.replace(haystack, " ") |
|
329 linewords = line.split() |
|
330 while len(linewords) != 0: |
|
331 try: |
|
332 toresend = int(linewords.pop(0)) |
|
333 self.resendfrom = toresend |
|
334 break |
|
335 except: |
|
336 pass |
|
337 self.clear = True |
|
338 self.clear = True |
|
339 |
|
340 def _start_sender(self): |
|
341 self.stop_send_thread = False |
|
342 self.send_thread = threading.Thread(target = self._sender) |
|
343 self.send_thread.start() |
|
344 |
|
345 def _stop_sender(self): |
|
346 if self.send_thread: |
|
347 self.stop_send_thread = True |
|
348 self.send_thread.join() |
|
349 self.send_thread = None |
|
350 |
|
351 def _sender(self): |
|
352 while not self.stop_send_thread: |
|
353 try: |
|
354 command = self.priqueue.get(True, 0.1) |
|
355 except QueueEmpty: |
|
356 continue |
|
357 while self.printer and self.printing and not self.clear: |
|
358 time.sleep(0.001) |
|
359 self._send(command) |
|
360 while self.printer and self.printing and not self.clear: |
|
361 time.sleep(0.001) |
|
362 |
|
363 def _checksum(self, command): |
|
364 return reduce(lambda x, y: x ^ y, map(ord, command)) |
|
365 |
|
366 def startprint(self, gcode, startindex = 0): |
|
367 """Start a print, gcode is an array of gcode commands. |
|
368 returns True on success, False if already printing. |
|
369 The print queue will be replaced with the contents of the data array, |
|
370 the next line will be set to 0 and the firmware notified. Printing |
|
371 will then start in a parallel thread. |
|
372 """ |
|
373 if self.printing or not self.online or not self.printer: |
|
374 return False |
|
375 self.queueindex = startindex |
|
376 self.mainqueue = gcode |
|
377 self.printing = True |
|
378 self.lineno = 0 |
|
379 self.resendfrom = -1 |
|
380 self._send("M110", -1, True) |
|
381 if not gcode or not gcode.lines: |
|
382 return True |
|
383 self.clear = False |
|
384 resuming = (startindex != 0) |
|
385 self.print_thread = threading.Thread(target = self._print, |
|
386 kwargs = {"resuming": resuming}) |
|
387 self.print_thread.start() |
|
388 return True |
|
389 |
|
390 def cancelprint(self): |
|
391 self.pause() |
|
392 self.paused = False |
|
393 self.mainqueue = None |
|
394 self.clear = True |
|
395 |
|
396 # run a simple script if it exists, no multithreading |
|
397 def runSmallScript(self, filename): |
|
398 if filename is None: return |
|
399 f = None |
|
400 try: |
|
401 with open(filename) as f: |
|
402 for i in f: |
|
403 l = i.replace("\n", "") |
|
404 l = l[:l.find(";")] # remove comments |
|
405 self.send_now(l) |
|
406 except: |
|
407 pass |
|
408 |
|
409 def pause(self): |
|
410 """Pauses the print, saving the current position. |
|
411 """ |
|
412 if not self.printing: return False |
|
413 self.paused = True |
|
414 self.printing = False |
|
415 |
|
416 # try joining the print thread: enclose it in try/except because we |
|
417 # might be calling it from the thread itself |
|
418 try: |
|
419 self.print_thread.join() |
|
420 except RuntimeError, e: |
|
421 if e.message == "cannot join current thread": |
|
422 pass |
|
423 else: |
|
424 self.logError(traceback.format_exc()) |
|
425 except: |
|
426 self.logError(traceback.format_exc()) |
|
427 |
|
428 self.print_thread = None |
|
429 |
|
430 # saves the status |
|
431 self.pauseX = self.analyzer.abs_x |
|
432 self.pauseY = self.analyzer.abs_y |
|
433 self.pauseZ = self.analyzer.abs_z |
|
434 self.pauseE = self.analyzer.abs_e |
|
435 self.pauseF = self.analyzer.current_f |
|
436 self.pauseRelative = self.analyzer.relative |
|
437 |
|
438 def resume(self): |
|
439 """Resumes a paused print. |
|
440 """ |
|
441 if not self.paused: return False |
|
442 if self.paused: |
|
443 # restores the status |
|
444 self.send_now("G90") # go to absolute coordinates |
|
445 |
|
446 xyFeedString = "" |
|
447 zFeedString = "" |
|
448 if self.xy_feedrate is not None: |
|
449 xyFeedString = " F" + str(self.xy_feedrate) |
|
450 if self.z_feedrate is not None: |
|
451 zFeedString = " F" + str(self.z_feedrate) |
|
452 |
|
453 self.send_now("G1 X%s Y%s%s" % (self.pauseX, self.pauseY, |
|
454 xyFeedString)) |
|
455 self.send_now("G1 Z" + str(self.pauseZ) + zFeedString) |
|
456 self.send_now("G92 E" + str(self.pauseE)) |
|
457 |
|
458 # go back to relative if needed |
|
459 if self.pauseRelative: self.send_now("G91") |
|
460 # reset old feed rate |
|
461 self.send_now("G1 F" + str(self.pauseF)) |
|
462 |
|
463 self.paused = False |
|
464 self.printing = True |
|
465 self.print_thread = threading.Thread(target = self._print, |
|
466 kwargs = {"resuming": True}) |
|
467 self.print_thread.start() |
|
468 |
|
469 def send(self, command, wait = 0): |
|
470 """Adds a command to the checksummed main command queue if printing, or |
|
471 sends the command immediately if not printing""" |
|
472 |
|
473 if self.online: |
|
474 if self.printing: |
|
475 self.mainqueue.append(command) |
|
476 else: |
|
477 self.priqueue.put_nowait(command) |
|
478 else: |
|
479 self.logError(_("Not connected to printer.")) |
|
480 |
|
481 def send_now(self, command, wait = 0): |
|
482 """Sends a command to the printer ahead of the command queue, without a |
|
483 checksum""" |
|
484 if self.online: |
|
485 self.priqueue.put_nowait(command) |
|
486 else: |
|
487 self.logError(_("Not connected to printer.")) |
|
488 |
|
489 def _print(self, resuming = False): |
|
490 self._stop_sender() |
|
491 try: |
|
492 if self.startcb: |
|
493 # callback for printing started |
|
494 try: self.startcb(resuming) |
|
495 except: |
|
496 self.logError(_("Print start callback failed with:") + |
|
497 "\n" + traceback.format_exc()) |
|
498 while self.printing and self.printer and self.online: |
|
499 self._sendnext() |
|
500 self.sentlines = {} |
|
501 self.log.clear() |
|
502 self.sent = [] |
|
503 if self.endcb: |
|
504 # callback for printing done |
|
505 try: self.endcb() |
|
506 except: |
|
507 self.logError(_("Print end callback failed with:") + |
|
508 "\n" + traceback.format_exc()) |
|
509 except: |
|
510 self.logError(_("Print thread died due to the following error:") + |
|
511 "\n" + traceback.format_exc()) |
|
512 finally: |
|
513 self.print_thread = None |
|
514 self._start_sender() |
|
515 |
|
516 def process_host_command(self, command): |
|
517 """only ;@pause command is implemented as a host command in printcore, but hosts are free to reimplement this method""" |
|
518 command = command.lstrip() |
|
519 if command.startswith(";@pause"): |
|
520 self.pause() |
|
521 |
|
522 def _sendnext(self): |
|
523 if not self.printer: |
|
524 return |
|
525 while self.printer and self.printing and not self.clear: |
|
526 time.sleep(0.001) |
|
527 # Only wait for oks when using serial connections or when not using tcp |
|
528 # in streaming mode |
|
529 if not self.printer_tcp or not self.tcp_streaming_mode: |
|
530 self.clear = False |
|
531 if not (self.printing and self.printer and self.online): |
|
532 self.clear = True |
|
533 return |
|
534 if self.resendfrom < self.lineno and self.resendfrom > -1: |
|
535 self._send(self.sentlines[self.resendfrom], self.resendfrom, False) |
|
536 self.resendfrom += 1 |
|
537 return |
|
538 self.resendfrom = -1 |
|
539 if not self.priqueue.empty(): |
|
540 self._send(self.priqueue.get_nowait()) |
|
541 self.priqueue.task_done() |
|
542 return |
|
543 if self.printing and self.queueindex < len(self.mainqueue): |
|
544 (layer, line) = self.mainqueue.idxs(self.queueindex) |
|
545 gline = self.mainqueue.all_layers[layer][line] |
|
546 if self.layerchangecb and self.queueindex > 0: |
|
547 (prev_layer, prev_line) = self.mainqueue.idxs(self.queueindex - 1) |
|
548 if prev_layer != layer: |
|
549 try: self.layerchangecb(layer) |
|
550 except: self.logError(traceback.format_exc()) |
|
551 if self.preprintsendcb: |
|
552 if self.queueindex + 1 < len(self.mainqueue): |
|
553 (next_layer, next_line) = self.mainqueue.idxs(self.queueindex + 1) |
|
554 next_gline = self.mainqueue.all_layers[next_layer][next_line] |
|
555 else: |
|
556 next_gline = None |
|
557 gline = self.preprintsendcb(gline, next_gline) |
|
558 if gline is None: |
|
559 self.queueindex += 1 |
|
560 self.clear = True |
|
561 return |
|
562 tline = gline.raw |
|
563 if tline.lstrip().startswith(";@"): # check for host command |
|
564 self.process_host_command(tline) |
|
565 self.queueindex += 1 |
|
566 self.clear = True |
|
567 return |
|
568 |
|
569 # Strip comments |
|
570 tline = gcoder.gcode_strip_comment_exp.sub("", tline).strip() |
|
571 if tline: |
|
572 self._send(tline, self.lineno, True) |
|
573 self.lineno += 1 |
|
574 if self.printsendcb: |
|
575 try: self.printsendcb(gline) |
|
576 except: self.logError(traceback.format_exc()) |
|
577 else: |
|
578 self.clear = True |
|
579 self.queueindex += 1 |
|
580 else: |
|
581 self.printing = False |
|
582 self.clear = True |
|
583 if not self.paused: |
|
584 self.queueindex = 0 |
|
585 self.lineno = 0 |
|
586 self._send("M110", -1, True) |
|
587 |
|
588 def _send(self, command, lineno = 0, calcchecksum = False): |
|
589 # Only add checksums if over serial (tcp does the flow control itself) |
|
590 if calcchecksum and not self.printer_tcp: |
|
591 prefix = "N" + str(lineno) + " " + command |
|
592 command = prefix + "*" + str(self._checksum(prefix)) |
|
593 if "M110" not in command: |
|
594 self.sentlines[lineno] = command |
|
595 if self.printer: |
|
596 self.sent.append(command) |
|
597 # run the command through the analyzer |
|
598 gline = None |
|
599 try: |
|
600 gline = self.analyzer.append(command, store = False) |
|
601 except: |
|
602 logging.warning(_("Could not analyze command %s:") % command + |
|
603 "\n" + traceback.format_exc()) |
|
604 if self.loud: |
|
605 logging.info("SENT: %s" % command) |
|
606 if self.sendcb: |
|
607 try: self.sendcb(command, gline) |
|
608 except: self.logError(traceback.format_exc()) |
|
609 try: |
|
610 self.printer.write(str(command + "\n")) |
|
611 if self.printer_tcp: |
|
612 try: |
|
613 self.printer.flush() |
|
614 except socket.timeout: |
|
615 pass |
|
616 self.writefailures = 0 |
|
617 except socket.error as e: |
|
618 if e.errno is None: |
|
619 self.logError(_(u"Can't write to printer (disconnected ?):") + |
|
620 "\n" + traceback.format_exc()) |
|
621 else: |
|
622 self.logError(_(u"Can't write to printer (disconnected?) (Socket error {0}): {1}").format(e.errno, decode_utf8(e.strerror))) |
|
623 self.writefailures += 1 |
|
624 except SerialException as e: |
|
625 self.logError(_(u"Can't write to printer (disconnected?) (SerialException): {0}").format(decode_utf8(str(e)))) |
|
626 self.writefailures += 1 |
|
627 except RuntimeError as e: |
|
628 self.logError(_(u"Socket connection broken, disconnected. ({0}): {1}").format(e.errno, decode_utf8(e.strerror))) |
|
629 self.writefailures += 1 |