Fri, 03 Jun 2016 09:16:07 +0200
Added printrun sourcecode from
https://github.com/kliment/Printrun
03.06.2016 09:10
15 | 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 |