Commit ee43a57e authored by Guillaume Seguin's avatar Guillaume Seguin

Merge branch 'errorcommand'

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