X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=Cura%2Fgui%2FfirmwareInstall.py;h=722a293be798b78fd98f04d7eb1d443eba229d7c;hb=730e8f2c1c8dfc9498a1b416cc398606f60940c3;hp=2892b06377fa77990e6f97dc926d0570fb6acd93;hpb=ea87a6efa8b975554cd584e646ca5d62c468fe5f;p=cura.git diff --git a/Cura/gui/firmwareInstall.py b/Cura/gui/firmwareInstall.py index 2892b063..722a293b 100644 --- a/Cura/gui/firmwareInstall.py +++ b/Cura/gui/firmwareInstall.py @@ -1,55 +1,245 @@ -from __future__ import absolute_import __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License" -import os, wx, threading, sys +import os +import wx +import threading +import sys +import time +import serial from Cura.avr_isp import stk500v2 from Cura.avr_isp import ispBase from Cura.avr_isp import intelHex +from Cura.gui.util import taskbar from Cura.util import machineCom from Cura.util import profile from Cura.util import resources -def getDefaultFirmware(): - if profile.getMachineSetting('machine_type') == 'ultimaker': - if profile.getMachineSetting('has_heated_bed') == 'True': +def getDefaultFirmware(machineIndex = None): + firmwareDict = { + 'ultimaker2go':"MarlinUltimaker2go.hex", + 'Witbox':"MarlinWitbox.hex", + 'lulzbot_mini': "Mini-Single-or-Flexystruder-LBHexagon-2015Q2.hex", + 'lulzbot_mini_flexystruder': "Mini-Single-or-Flexystruder-LBHexagon-2015Q2.hex", + 'lulzbot_TAZ_4_SingleV1': "Taz4-5-Single-or-Flexystruder-Budaschnozzle-2014Q3.hex", + 'lulzbot_TAZ_5_SingleV1': "Taz4-5-Single-or-Flexystruder-Budaschnozzle-2014Q3.hex", + 'lulzbot_TAZ_4_05nozzle': "Taz4-Single-Extruder-LBHexagon-2015Q3.hex", + 'lulzbot_TAZ_5_05nozzle': "Taz5-Single-Extruder-LBHexagon-2015Q3.hex", + 'lulzbot_TAZ_4_035nozzle': "Taz4-Single-Extruder-LBHexagon-2015Q3.hex", + 'lulzbot_TAZ_5_035nozzle': "Taz5-Single-Extruder-LBHexagon-2015Q3.hex", + 'lulzbot_TAZ_4_FlexystruderV1': "Taz4-5-Single-or-Flexystruder-Budaschnozzle-2014Q3.hex", + 'lulzbot_TAZ_5_FlexystruderV1': "Taz4-5-Single-or-Flexystruder-Budaschnozzle-2014Q3.hex", + 'lulzbot_TAZ_4_FlexystruderV2': "Taz4-5-Flexystruder-LBHexagon-2015Q3.hex", + 'lulzbot_TAZ_5_FlexystruderV2': "Taz4-5-Flexystruder-LBHexagon-2015Q3.hex", + 'lulzbot_TAZ_4_DualV1': "Taz4-5-Dual-or-FlexyDually-Budaschnozzle-2015Q1.hex", + 'lulzbot_TAZ_5_DualV1': "Taz4-5-Dual-or-FlexyDually-Budaschnozzle-2015Q1.hex", + 'lulzbot_TAZ_4_DualV2': "Taz4-5-Dual-LBHexagon-2015Q3.hex", + 'lulzbot_TAZ_5_DualV2': "Taz4-5-Dual-LBHexagon-2015Q3.hex", + 'lulzbot_TAZ_4_FlexyDuallyV1': "Taz4-5-Dual-or-FlexyDually-Budaschnozzle-2015Q1.hex", + 'lulzbot_TAZ_5_FlexyDuallyV1': "Taz4-5-Dual-or-FlexyDually-Budaschnozzle-2015Q1.hex", + 'lulzbot_TAZ_4_FlexyDuallyV2': "Taz4-5-FlexyDually-LBHexagon-2015Q3.hex", + 'lulzbot_TAZ_5_FlexyDuallyV2': "Taz4-5-FlexyDually-LBHexagon-2015Q3.hex", + 'lulzbot_TAZ_6_SingleTilapia': "Taz6-Single-Tilapia-2015-Q4.hex" + } + machine_type = profile.getMachineSetting('machine_type', machineIndex) + extruders = profile.getMachineSettingFloat('extruder_amount', machineIndex) + heated_bed = profile.getMachineSetting('has_heated_bed', machineIndex) == 'True' + baudrate = 250000 + if sys.platform.startswith('linux'): + baudrate = 115200 + if machine_type == 'ultimaker': + name = 'MarlinUltimaker' + if extruders > 2: return None - if profile.getMachineSettingFloat('extruder_amount') > 2: + if heated_bed: + name += '-HBK' + name += '-%d' % (baudrate) + if extruders > 1: + name += '-dual' + return resources.getPathForFirmware(name + '.hex') + + if machine_type == 'ultimaker_plus': + name = 'MarlinUltimaker-UMOP-%d' % (baudrate) + if extruders > 2: return None - if profile.getMachineSettingFloat('extruder_amount') > 1: - if sys.platform.startswith('linux'): - return resources.getPathForFirmware("MarlinUltimaker-115200-dual.hex") - else: - return resources.getPathForFirmware("MarlinUltimaker-250000-dual.hex") - if sys.platform.startswith('linux'): - return resources.getPathForFirmware("MarlinUltimaker-115200.hex") - else: - return resources.getPathForFirmware("MarlinUltimaker-250000.hex") + if extruders > 1: + name += '-dual' + return resources.getPathForFirmware(name + '.hex') + if machine_type == 'ultimaker2': + if extruders > 2: + return None + if extruders > 1: + return resources.getPathForFirmware("MarlinUltimaker2-dual.hex") + return resources.getPathForFirmware("MarlinUltimaker2.hex") + if machine_type == 'ultimaker2extended': + if extruders > 2: + return None + if extruders > 1: + return resources.getPathForFirmware("MarlinUltimaker2extended-dual.hex") + return resources.getPathForFirmware("MarlinUltimaker2extended.hex") + if firmwareDict.has_key(machine_type): + return resources.getPathForFirmware(firmwareDict[machine_type]) return None -class InstallFirmware(wx.Dialog): - def __init__(self, filename = None, port = None): - super(InstallFirmware, self).__init__(parent=None, title="Firmware install", size=(250, 100)) +def InstallFirmware(parent = None, filename = None, port = None, machineIndex = None): + dlg = InstallFirmwareDialog(parent, filename, port, machineIndex) + result = dlg.Run() + dlg.Destroy() + return result + +class InstallFirmwareDialog(wx.Dialog): + def __init__(self, parent = None, filename = None, port = None, machineIndex = None): + super(InstallFirmwareDialog, self).__init__(parent=parent, title=_("Firmware install for %s") % (profile.getMachineName(machineIndex).title()), size=(250, 100)) if port is None: port = profile.getMachineSetting('serial_port') if filename is None: - filename = getDefaultFirmware() - if filename is None: + filename = getDefaultFirmware(machineIndex) + self._machine_type = profile.getMachineSetting('machine_type', machineIndex) + if self._machine_type == 'reprap': + wx.MessageBox(_("Cura only supports firmware updates for ATMega2560 based hardware.\nSo updating your RepRap with Cura might or might not work."), _("Firmware update"), wx.OK | wx.ICON_INFORMATION) + + sizer = wx.BoxSizer(wx.VERTICAL) + + self.progressLabel = wx.StaticText(self, -1, 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nX\nX') + sizer.Add(self.progressLabel, 0, flag=wx.ALIGN_CENTER|wx.ALL, border=5) + self.progressGauge = wx.Gauge(self, -1) + sizer.Add(self.progressGauge, 0, flag=wx.EXPAND) + self.okButton = wx.Button(self, -1, _("OK")) + self.okButton.Disable() + self.okButton.Bind(wx.EVT_BUTTON, self.OnOk) + sizer.Add(self.okButton, 0, flag=wx.ALIGN_CENTER|wx.ALL, border=5) + self.SetSizer(sizer) + + self.filename = filename + self.port = port + + self.Layout() + self.Fit() + self.success = False + self.show_connect_dialog = False + + def Run(self): + if self.filename is None: wx.MessageBox(_("I am sorry, but Cura does not ship with a default firmware for your machine configuration."), _("Firmware update"), wx.OK | wx.ICON_ERROR) - self.Destroy() + return False + self.success = False + self.thread = threading.Thread(target=self.OnRun) + self.thread.daemon = True + self.thread.start() + + self.ShowModal() + # Creating a MessageBox in a separate thread while main thread is locked inside a ShowModal + # will cause Python to crash with X errors. So we need to show the dialog here instead + if self.show_connect_dialog: + wx.MessageBox(_("Failed to find machine for firmware upgrade\nIs your machine connected to the PC?"), + _("Firmware update"), wx.OK | wx.ICON_ERROR) + return self.success + + def OnRun(self): + wx.CallAfter(self.updateLabel, _("Reading firmware...")) + hexFile = intelHex.readHex(self.filename) + wx.CallAfter(self.updateLabel, _("Connecting to machine...")) + programmer = stk500v2.Stk500v2() + programmer.progressCallback = self.OnProgress + if self.port == 'AUTO': + wx.CallAfter(self.updateLabel, _("Please connect the printer to your\ncomputer with a USB cable and power it on.")) + while not programmer.isConnected(): + for self.port in machineCom.serialList(True): + try: + programmer.connect(self.port) + break + except ispBase.IspError: + programmer.close() + time.sleep(1) + if not self: + #Window destroyed + return + else: + try: + programmer.connect(self.port) + except ispBase.IspError: + programmer.close() + if not self: + #Window destroyed + return + + if not programmer.isConnected(): + self.show_connect_dialog = True + wx.CallAfter(self.Close) return + if self._machine_type == 'ultimaker': + if programmer.hasChecksumFunction(): + wx.CallAfter(self.updateLabel, _("Failed to install firmware:\nThis firmware is not compatible with this machine.\nTrying to install UMO firmware on an UM2 or UMO+?")) + programmer.close() + wx.CallAfter(self.okButton.Enable) + return + if self._machine_type == 'ultimaker_plus' or self._machine_type == 'ultimaker2': + if not programmer.hasChecksumFunction(): + wx.CallAfter(self.updateLabel, _("Failed to install firmware:\nThis firmware is not compatible with this machine.\nTrying to install UM2 or UMO+ firmware on an UMO?")) + programmer.close() + wx.CallAfter(self.okButton.Enable) + return + + wx.CallAfter(self.updateLabel, _("Uploading firmware...")) + try: + programmer.programChip(hexFile) + self.success = True + wx.CallAfter(self.updateLabel, _("Done!\nInstalled firmware: %s") % (os.path.basename(self.filename))) + except ispBase.IspError as e: + wx.CallAfter(self.updateLabel, _("Failed to write firmware.\n") + str(e)) + + programmer.close() + wx.CallAfter(self.okButton.Enable) + + def updateLabel(self, text): + self.progressLabel.SetLabel(text) + self.Layout() + + def OnProgress(self, value, max): + if self: + wx.CallAfter(self.progressGauge.SetRange, max) + wx.CallAfter(self.progressGauge.SetValue, value) + taskbar.setProgress(self.GetParent(), value, max) + + def OnOk(self, e): + self.Close() + taskbar.setBusy(self.GetParent(), False) + + def OnClose(self, e): + self.Destroy() + + +class AutoUpdateFirmware(wx.Dialog): + def __init__(self, parent, filename = None, port = None, machineIndex = None): + super(AutoUpdateFirmware, self).__init__(parent=parent, title=_("Auto Firmware install"), size=(250, 500)) + if port is None: + port = profile.getMachineSetting('serial_port') + self._serial = None + sizer = wx.BoxSizer(wx.VERTICAL) - self.progressLabel = wx.StaticText(self, -1, 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nX') - sizer.Add(self.progressLabel, 0, flag=wx.ALIGN_CENTER) + self.progressLabel = wx.StaticText(self, -1, 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nX\nX') + sizer.Add(self.progressLabel, 0, flag=wx.ALIGN_CENTER|wx.ALL, border=5) self.progressGauge = wx.Gauge(self, -1) sizer.Add(self.progressGauge, 0, flag=wx.EXPAND) self.okButton = wx.Button(self, -1, _("OK")) - self.okButton.Disable() self.okButton.Bind(wx.EVT_BUTTON, self.OnOk) - sizer.Add(self.okButton, 0, flag=wx.ALIGN_CENTER) + sizer.Add(self.okButton, 0, flag=wx.ALIGN_CENTER|wx.ALL, border=5) + + f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) + self._termLog = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.TE_DONTWRAP) + self._termLog.SetFont(f) + self._termLog.SetEditable(0) + self._termLog.SetMinSize((1, 400)) + self._termInput = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER) + self._termInput.SetFont(f) + sizer.Add(self._termLog, 0, flag=wx.ALIGN_CENTER|wx.ALL|wx.EXPAND) + sizer.Add(self._termInput, 0, flag=wx.ALIGN_CENTER|wx.ALL|wx.EXPAND) + + self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self._termInput) + self.SetSizer(sizer) self.filename = filename @@ -58,56 +248,116 @@ class InstallFirmware(wx.Dialog): self.Layout() self.Fit() - threading.Thread(target=self.OnRun).start() + self.thread = threading.Thread(target=self.OnRun) + self.thread.daemon = True + self.thread.start() + + self.read_thread = threading.Thread(target=self.OnSerialRead) + self.read_thread.daemon = True + self.read_thread.start() self.ShowModal() self.Destroy() return + def _addTermLog(self, line): + if self._termLog is not None: + if len(self._termLog.GetValue()) > 10000: + self._termLog.SetValue(self._termLog.GetValue()[-10000:]) + self._termLog.SetInsertionPointEnd() + if type(line) != unicode: + line = unicode(line, 'utf-8', 'replace') + self._termLog.AppendText(line.encode('utf-8', 'replace')) + + def OnTermEnterLine(self, e): + lines = self._termInput.GetValue().split(';') + for line in lines: + if line == '': + continue + self._addTermLog('> %s\n' % (line)) + if self._serial is not None: + self._serial.write(line + '\n') + def OnRun(self): + mtime = 0 + while bool(self): + new_mtime = os.stat(self.filename).st_mtime + if mtime != new_mtime: + mtime = new_mtime + if self._serial is not None: + self._serial.close() + self._serial = None + time.sleep(0.5) + self.OnInstall() + try: + self._serial = serial.Serial(self.port, 115200) + except: + pass + time.sleep(0.5) + + def OnSerialRead(self): + while bool(self): + if self._serial is None: + time.sleep(0.5) + else: + try: + line = self._serial.readline() + wx.CallAfter(self._addTermLog, line) + except: + pass + + def OnInstall(self): + wx.CallAfter(self.okButton.Disable) wx.CallAfter(self.updateLabel, _("Reading firmware...")) hexFile = intelHex.readHex(self.filename) wx.CallAfter(self.updateLabel, _("Connecting to machine...")) programmer = stk500v2.Stk500v2() programmer.progressCallback = self.OnProgress if self.port == 'AUTO': - for self.port in machineCom.serialList(True): - try: - programmer.connect(self.port) - break - except ispBase.IspError: - pass + wx.CallAfter(self.updateLabel, _("Please connect the printer to your\ncomputer with a USB cable and power it on.")) + while not programmer.isConnected(): + for self.port in machineCom.serialList(True): + try: + programmer.connect(self.port) + break + except ispBase.IspError: + pass + time.sleep(1) + if not self: + #Window destroyed + return else: try: programmer.connect(self.port) except ispBase.IspError: pass - if programmer.isConnected(): - wx.CallAfter(self.updateLabel, _("Uploading firmware...")) - try: - programmer.programChip(hexFile) - wx.CallAfter(self.updateLabel, _("Done!\nInstalled firmware: %s") % (os.path.basename(self.filename))) - except ispBase.IspError as e: - wx.CallAfter(self.updateLabel, _("Failed to write firmware.\n") + str(e)) - - programmer.close() - wx.CallAfter(self.okButton.Enable) + if not programmer.isConnected(): + wx.CallAfter(self.updateLabel, _("Failed to connect to programmer.\n")) return - wx.MessageBox(_("Failed to find machine for firmware upgrade\nIs your machine connected to the PC?"), - _("Firmware update"), wx.OK | wx.ICON_ERROR) - wx.CallAfter(self.Close) + + wx.CallAfter(self.updateLabel, _("Uploading firmware...")) + try: + programmer.programChip(hexFile) + wx.CallAfter(self.updateLabel, _("Done!\nInstalled firmware: %s") % (os.path.basename(self.filename))) + except ispBase.IspError as e: + wx.CallAfter(self.updateLabel, _("Failed to write firmware.\n") + str(e)) + + programmer.close() + wx.CallAfter(self.okButton.Enable) def updateLabel(self, text): self.progressLabel.SetLabel(text) - #self.Layout() + self.Layout() def OnProgress(self, value, max): wx.CallAfter(self.progressGauge.SetRange, max) wx.CallAfter(self.progressGauge.SetValue, value) + taskbar.setProgress(self.GetParent(), value, max) def OnOk(self, e): self.Close() + taskbar.setBusy(self.GetParent(), False) def OnClose(self, e): self.Destroy()