Commit ee43a57e authored by Guillaume Seguin's avatar Guillaume Seguin

Merge branch 'errorcommand'

parents 5204811f cf8cff2b
......@@ -107,6 +107,13 @@ class printcore():
self.z_feedrate = None
self.pronterface = None
def logError(self, error):
if self.errorcb:
try: self.errorcb(error)
except: pass
else:
logging.error(error)
@locked
def disconnect(self):
"""Disconnects from printer and pauses the print
......@@ -165,7 +172,7 @@ class printcore():
self.printer_tcp.settimeout(self.timeout)
self.printer = self.printer_tcp.makefile()
except socket.error as e:
logging.error(_("Could not connect to %s:%s:") % (hostname, port) +
self.logError(_("Could not connect to %s:%s:") % (hostname, port) +
"\n" + _("Socket error %s:") % e.errno +
"\n" + e.strerror)
self.printer = None
......@@ -179,7 +186,7 @@ class printcore():
baudrate = self.baud,
timeout = 0.25)
except SerialException as e:
logging.error(_("Could not connect to %s at baudrate %s:") % (self.port, self.baud) +
self.logError(_("Could not connect to %s at baudrate %s:") % (self.port, self.baud) +
"\n" + _("Serial error: %s") % e)
self.printer = None
return
......@@ -214,21 +221,21 @@ class printcore():
return line
except SelectError as e:
if 'Bad file descriptor' in e.args[1]:
logging.error(_(u"Can't read from printer (disconnected?) (SelectError {0}): {1}").format(e.errno, decode_utf8(e.strerror)))
self.logError(_(u"Can't read from printer (disconnected?) (SelectError {0}): {1}").format(e.errno, decode_utf8(e.strerror)))
return None
else:
logging.error(_(u"SelectError ({0}): {1}").format(e.errno, decode_utf8(e.strerror)))
self.logError(_(u"SelectError ({0}): {1}").format(e.errno, decode_utf8(e.strerror)))
raise
except SerialException as e:
logging.error(_(u"Can't read from printer (disconnected?) (SerialException): {0}").format(decode_utf8(str(e))))
self.logError(_(u"Can't read from printer (disconnected?) (SerialException): {0}").format(decode_utf8(str(e))))
return None
except socket.error as e:
logging.error(_(u"Can't read from printer (disconnected?) (Socket error {0}): {1}").format(e.errno, decode_utf8(e.strerror)))
self.logError(_(u"Can't read from printer (disconnected?) (Socket error {0}): {1}").format(e.errno, decode_utf8(e.strerror)))
return None
except OSError as e:
if e.errno == errno.EAGAIN: # Not a real error, no data was available
return ""
logging.error(_(u"Can't read from printer (disconnected?) (OS Error {0}): {1}").format(e.errno, e.strerror))
self.logError(_(u"Can't read from printer (disconnected?) (OS Error {0}): {1}").format(e.errno, e.strerror))
return None
def _listen_can_continue(self):
......@@ -288,10 +295,7 @@ class printcore():
try: self.tempcb(line)
except: pass
elif line.startswith('Error'):
if self.errorcb:
#callback for errors
try: self.errorcb(line)
except: pass
self.logError(line)
# Teststrings for resend parsing # Firmware exp. result
# line="rs N2 Expected checksum 67" # Teacup 2
if line.lower().startswith("resend") or line.startswith("rs"):
......@@ -437,7 +441,7 @@ class printcore():
else:
self.priqueue.put_nowait(command)
else:
logging.error(_("Not connected to printer."))
self.logError(_("Not connected to printer."))
def send_now(self, command, wait = 0):
"""Sends a command to the printer ahead of the command queue, without a
......@@ -445,7 +449,7 @@ class printcore():
if self.online:
self.priqueue.put_nowait(command)
else:
logging.error(_("Not connected to printer."))
self.logError(_("Not connected to printer."))
def _print(self, resuming = False):
self._stop_sender()
......@@ -454,7 +458,7 @@ class printcore():
#callback for printing started
try: self.startcb(resuming)
except:
logging.error(_("Print start callback failed with:") +
self.logError(_("Print start callback failed with:") +
"\n" + traceback.format_exc())
while self.printing and self.printer and self.online:
self._sendnext()
......@@ -465,10 +469,10 @@ class printcore():
#callback for printing done
try: self.endcb()
except:
logging.error(_("Print end callback failed with:") +
self.logError(_("Print end callback failed with:") +
"\n" + traceback.format_exc())
except:
logging.error(_("Print thread died due to the following error:") +
self.logError(_("Print thread died due to the following error:") +
"\n" + traceback.format_exc())
finally:
self.print_thread = None
......@@ -556,8 +560,8 @@ class printcore():
# run the command through the analyzer
try: self.analyzer.Analyze(command)
except:
logging.error(_("Warning: could not analyze command %s:") % command +
"\n" + traceback.format_exc())
logging.warning(_("Could not analyze command %s:") % command +
"\n" + traceback.format_exc())
if self.loud:
logging.info("SENT: %s" % command)
if self.sendcb:
......@@ -568,11 +572,11 @@ class printcore():
if self.printer_tcp: self.printer.flush()
self.writefailures = 0
except socket.error as e:
logging.error(_(u"Can't write to printer (disconnected?) (Socket error {0}): {1}").format(e.errno, decode_utf8(e.strerror)))
self.logError(_(u"Can't write to printer (disconnected?) (Socket error {0}): {1}").format(e.errno, decode_utf8(e.strerror)))
self.writefailures += 1
except SerialException as e:
logging.error(_(u"Can't write to printer (disconnected?) (SerialException): {0}").format(decode_utf8(str(e))))
self.logError(_(u"Can't write to printer (disconnected?) (SerialException): {0}").format(decode_utf8(str(e))))
self.writefailures += 1
except RuntimeError as e:
logging.error(_(u"Socket connection broken, disconnected. ({0}): {1}").format(e.errno, decode_utf8(e.strerror)))
self.logError(_(u"Socket connection broken, disconnected. ({0}): {1}").format(e.errno, decode_utf8(e.strerror)))
self.writefailures += 1
......@@ -16,6 +16,9 @@
import os
import sys
import gettext
import datetime
import subprocess
import shlex
# Set up Internationalization using gettext
# searching for installed locales on /usr/share; uses relative folder if not
......@@ -77,6 +80,24 @@ def decode_utf8(s):
pass
return s
def format_time(timestamp):
return datetime.datetime.fromtimestamp(timestamp).strftime("%H:%M:%S")
def format_duration(delta):
return str(datetime.timedelta(seconds = int(delta)))
def run_command(command, replaces = None, stdout = subprocess.STDOUT, stderr = subprocess.STDOUT, blocking = False):
command = shlex.split(command.replace("\\", "\\\\").encode())
if replaces is not None:
replaces["$python"] = sys.executable
for pattern, rep in replaces.items():
command = [bit.replace(pattern, rep) for bit in command]
command = [bit.encode() for bit in command]
if blocking:
return subprocess.call(command)
else:
return subprocess.Popen(command, stderr = stderr, stdout = stdout)
class RemainingTimeEstimator(object):
drift = None
......
......@@ -22,13 +22,12 @@ import time
import sys
import subprocess
import codecs
import shlex
import argparse
import locale
import logging
from . import printcore
from printrun.printrun_utils import install_locale
from printrun.printrun_utils import install_locale, format_time, format_duration, run_command
install_locale('pronterface')
from printrun import gcoder
......@@ -247,6 +246,7 @@ class Settings(object):
self._add(StringSetting("slicecommand", "python skeinforge/skeinforge_application/skeinforge_utilities/skeinforge_craft.py $s", _("Slice command"), _("Slice command"), "External"))
self._add(StringSetting("sliceoptscommand", "python skeinforge/skeinforge_application/skeinforge.py", _("Slicer options command"), _("Slice settings command"), "External"))
self._add(StringSetting("final_command", "", _("Final command"), _("Executable to run when the print is finished"), "External"))
self._add(StringSetting("error_command", "", _("Error command"), _("Executable to run when an error occurs"), "External"))
self._add(HiddenSetting("project_offset_x", 0.0))
self._add(HiddenSetting("project_offset_y", 0.0))
......@@ -361,9 +361,12 @@ class pronsole(cmd.Cmd):
self.dynamic_temp = False
self.p = printcore.printcore()
self.p.recvcb = self.recvcb
self.p.startcb = self.startcb
self.p.endcb = self.endcb
self.recvlisteners = []
self.in_macro = False
self.p.onlinecb = self.online
self.p.errorcb = self.logError
self.fgcode = None
self.listing = 0
self.sdfiles = []
......@@ -384,6 +387,8 @@ class pronsole(cmd.Cmd):
self.settings._bedtemp_abs_cb = self.set_temp_preset
self.settings._bedtemp_pla_cb = self.set_temp_preset
self.monitoring = 0
self.starttime = 0
self.extra_print_time = 0
self.silent = False
self.commandprefixes = 'MGT$'
self.promptstrs = {"offline": "%(bold)suninitialized>%(normal)s ",
......@@ -403,7 +408,14 @@ class pronsole(cmd.Cmd):
print u"".join(unicode(i) for i in msg)
def logError(self, *msg):
logging.error(u"".join(unicode(i) for i in msg))
msg = u"".join(unicode(i) for i in msg)
logging.error(msg)
if not self.settings.error_command:
return
run_command(self.settings.error_command,
{"$m": msg},
stderr = subprocess.STDOUT, stdout = subprocess.PIPE,
blocking = False)
def promptf(self):
"""A function to generate prompts so that we can do dynamic prompts. """
......@@ -1049,6 +1061,29 @@ class pronsole(cmd.Cmd):
for i in self.recvlisteners:
i(l)
def startcb(self, resuming = False):
self.starttime = time.time()
if resuming:
print _("Print resumed at: %s") % format_time(self.starttime)
else:
print _("Print started at: %s") % format_time(self.starttime)
def endcb(self):
if self.p.queueindex == 0:
print_duration = int(time.time() - self.starttime + self.extra_print_time)
print _("Print ended at: %(end_time)s and took %(duration)s") % {"end_time": format_time(time.time()),
"duration": format_duration(print_duration)}
self.p.runSmallScript(self.endScript)
if not self.settings.final_command:
return
run_command(self.settings.final_command,
{"$s": str(self.filename),
"$t": format_duration(print_duration)},
stderr = subprocess.STDOUT, stdout = subprocess.PIPE,
blocking = False)
def help_shell(self):
self.log("Executes a python command. Example:")
self.log("! os.listdir('.')")
......@@ -1389,14 +1424,18 @@ class pronsole(cmd.Cmd):
return
try:
if settings:
param = self.expandcommand(self.settings.sliceoptscommand).replace("\\", "\\\\").encode()
self.log(_("Entering slicer settings: %s") % param)
subprocess.call(shlex.split(param))
command = self.settings.sliceoptscommand
self.log(_("Entering slicer settings: %s") % command)
run_command(command, blocking = True)
else:
param = self.expandcommand(self.settings.slicecommand).encode()
self.log(_("Slicing: ") % param)
params = [i.replace("$s", l[0]).replace("$o", l[0].replace(".stl", "_export.gcode").replace(".STL", "_export.gcode")).encode() for i in shlex.split(param.replace("\\", "\\\\").encode())]
subprocess.call(params)
command = self.settings.slicecommand
self.log(_("Slicing: ") % command)
stl_name = l[0]
gcode_name = stl_name.replace(".stl", "_export.gcode").replace(".STL", "_export.gcode")
run_command(command,
{"$s": stl_name,
"$o": gcode_name},
blocking = True)
self.log(_("Loading sliced file."))
self.do_load(l[0].replace(".stl", "_export.gcode"))
except Exception, e:
......
......@@ -20,7 +20,6 @@ import Queue
import re
import sys
import time
import datetime
import threading
import traceback
import cStringIO as StringIO
......@@ -49,7 +48,7 @@ layerindex = 0
if os.name == "nt":
winsize = (800, 530)
from printrun.printrun_utils import iconfile, configfile
from printrun.printrun_utils import iconfile, configfile, format_time, format_duration
from printrun.gui import MainWindow
from printrun.excluder import Excluder
from pronsole import dosify, wxSetting, HiddenSetting, StringSetting, SpinSetting, FloatSpinSetting, BooleanSetting, StaticTextSetting
......@@ -61,12 +60,6 @@ def parse_temperature_report(report):
matches = tempreport_exp.findall(report)
return dict((m[0], (m[1], m[2])) for m in matches)
def format_time(timestamp):
return datetime.datetime.fromtimestamp(timestamp).strftime("%H:%M:%S")
def format_duration(delta):
return str(datetime.timedelta(seconds = int(delta)))
class Tee(object):
def __init__(self, target):
self.stdout = sys.stdout
......@@ -323,8 +316,6 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.p.startcb = self.startcb
self.p.endcb = self.endcb
self.compute_eta = None
self.starttime = 0
self.extra_print_time = 0
self.curlayer = 0
self.cur_button = None
self.predisconnect_mainqueue = None
......@@ -348,31 +339,18 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.autoconnect = args.autoconnect
def startcb(self, resuming = False):
self.starttime = time.time()
if resuming:
print _("Print resumed at: %s") % format_time(self.starttime)
else:
print _("Print started at: %s") % format_time(self.starttime)
pronsole.pronsole.startcb(self, resuming)
if not resuming:
self.compute_eta = RemainingTimeEstimator(self.fgcode)
if self.settings.lockbox and self.settings.lockonstart:
wx.CallAfter(self.lock, force = True)
def endcb(self):
pronsole.pronsole.endcb(self)
if self.p.queueindex == 0:
print_duration = int(time.time() - self.starttime + self.extra_print_time)
print _("Print ended at: %(end_time)s and took %(duration)s") % {"end_time": format_time(time.time()),
"duration": format_duration(print_duration)}
wx.CallAfter(self.pausebtn.Disable)
wx.CallAfter(self.printbtn.SetLabel, _("Print"))
self.p.runSmallScript(self.endScript)
param = self.settings.final_command
if not param:
return
pararray = [i.replace("$s", str(self.filename)).replace("$t", format_duration(print_duration)).encode() for i in shlex.split(param.replace("\\", "\\\\").encode())]
self.finalp = subprocess.Popen(pararray, stderr = subprocess.STDOUT, stdout = subprocess.PIPE)
def online(self):
print _("Printer is now online.")
wx.CallAfter(self.online_gui)
......@@ -1477,7 +1455,7 @@ Printrun. If not, see <http://www.gnu.org/licenses/>."""
wx.CallAfter(self.gviz.Refresh)
if self.p.online:
if self.p.writefailures >= 4:
logging.error(_("Disconnecting after 4 failed writes."))
self.logError(_("Disconnecting after 4 failed writes."))
self.status_thread = None
self.disconnect()
return
......@@ -1840,19 +1818,19 @@ Printrun. If not, see <http://www.gnu.org/licenses/>."""
except SerialException as e:
# Currently, there is no errno, but it should be there in the future
if e.errno == 2:
logging.error(_("Error: You are trying to connect to a non-existing port."))
self.logError(_("Error: You are trying to connect to a non-existing port."))
elif e.errno == 8:
logging.error(_("Error: You don't have permission to open %s.") % port)
logging.error(_("You might need to add yourself to the dialout group."))
self.logError(_("Error: You don't have permission to open %s.") % port)
self.logError(_("You might need to add yourself to the dialout group."))
else:
logging.error(traceback.format_exc())
self.logError(traceback.format_exc())
# Kill the scope anyway
return
except OSError as e:
if e.errno == 2:
logging.error(_("Error: You are trying to connect to a non-existing port."))
self.logError(_("Error: You are trying to connect to a non-existing port."))
else:
logging.error(traceback.format_exc())
self.logError(traceback.format_exc())
return
self.statuscheck = True
if port != self.settings.port:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment