chiark / gitweb /
Initial fakeouts for Mangrove
[cura.git] / Cura / gui / firmwareInstall.py
1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
2
3 import os
4 import wx
5 import threading
6 import sys
7 import time
8 import serial
9
10 from Cura.avr_isp import stk500v2
11 from Cura.avr_isp import ispBase
12 from Cura.avr_isp import intelHex
13
14 from Cura.gui.util import taskbar
15 from Cura.util import machineCom
16 from Cura.util import profile
17 from Cura.util import resources
18
19 def getDefaultFirmware(machineIndex = None):
20         firmwareDict = {
21                         'ultimaker2go':"MarlinUltimaker2go.hex",
22                         'Witbox':"MarlinWitbox.hex",
23                         'lulzbot_mini': "Mini-Single-or-Flexystruder-LBHexagon-2015Q2.hex",
24                         'lulzbot_mini_flexystruder': "Mini-Single-or-Flexystruder-LBHexagon-2015Q2.hex",
25                         'lulzbot_TAZ_4_SingleV1': "Taz4-5-Single-or-Flexystruder-Budaschnozzle-2014Q3.hex",
26                         'lulzbot_TAZ_5_SingleV1': "Taz4-5-Single-or-Flexystruder-Budaschnozzle-2014Q3.hex",
27                         'lulzbot_TAZ_4_05nozzle': "Taz4-Single-Extruder-LBHexagon-2015Q3.hex",
28                         'lulzbot_TAZ_5_05nozzle': "Taz5-Single-Extruder-LBHexagon-2015Q3.hex",
29                         'lulzbot_TAZ_4_035nozzle': "Taz4-Single-Extruder-LBHexagon-2015Q3.hex",
30                         'lulzbot_TAZ_5_035nozzle': "Taz5-Single-Extruder-LBHexagon-2015Q3.hex",
31                         'lulzbot_TAZ_4_FlexystruderV1': "Taz4-5-Single-or-Flexystruder-Budaschnozzle-2014Q3.hex",
32                         'lulzbot_TAZ_5_FlexystruderV1': "Taz4-5-Single-or-Flexystruder-Budaschnozzle-2014Q3.hex",
33                         'lulzbot_TAZ_4_FlexystruderV2': "Taz4-5-Flexystruder-LBHexagon-2015Q3.hex",
34                         'lulzbot_TAZ_5_FlexystruderV2': "Taz4-5-Flexystruder-LBHexagon-2015Q3.hex",
35                         'lulzbot_TAZ_4_DualV1': "Taz4-5-Dual-or-FlexyDually-Budaschnozzle-2015Q1.hex",
36                         'lulzbot_TAZ_5_DualV1': "Taz4-5-Dual-or-FlexyDually-Budaschnozzle-2015Q1.hex",
37                         'lulzbot_TAZ_4_DualV2': "Taz4-5-Dual-LBHexagon-2015Q3.hex",
38                         'lulzbot_TAZ_5_DualV2': "Taz4-5-Dual-LBHexagon-2015Q3.hex",
39                         'lulzbot_TAZ_4_FlexyDuallyV1': "Taz4-5-Dual-or-FlexyDually-Budaschnozzle-2015Q1.hex",
40                         'lulzbot_TAZ_5_FlexyDuallyV1': "Taz4-5-Dual-or-FlexyDually-Budaschnozzle-2015Q1.hex",
41                         'lulzbot_TAZ_4_FlexyDuallyV2': "Taz4-5-FlexyDually-LBHexagon-2015Q3.hex",
42                         'lulzbot_TAZ_5_FlexyDuallyV2': "Taz4-5-FlexyDually-LBHexagon-2015Q3.hex",
43                         'lulzbot_TAZ_6_SingleTilapia': "Taz6-Single-Tilapia-2015-Q4.hex"
44         }
45         machine_type = profile.getMachineSetting('machine_type', machineIndex)
46         extruders = profile.getMachineSettingFloat('extruder_amount', machineIndex)
47         heated_bed = profile.getMachineSetting('has_heated_bed', machineIndex) == 'True'
48         baudrate = 250000
49         if sys.platform.startswith('linux'):
50                 baudrate = 115200
51         if machine_type == 'ultimaker':
52                 name = 'MarlinUltimaker'
53                 if extruders > 2:
54                         return None
55                 if heated_bed:
56                         name += '-HBK'
57                 name += '-%d' % (baudrate)
58                 if extruders > 1:
59                         name += '-dual'
60                 return resources.getPathForFirmware(name + '.hex')
61
62         if machine_type == 'ultimaker_plus':
63                 name = 'MarlinUltimaker-UMOP-%d' % (baudrate)
64                 if extruders > 2:
65                         return None
66                 if extruders > 1:
67                         name += '-dual'
68                 return resources.getPathForFirmware(name + '.hex')
69         if machine_type == 'ultimaker2':
70                 if extruders > 2:
71                         return None
72                 if extruders > 1:
73                         return resources.getPathForFirmware("MarlinUltimaker2-dual.hex")
74                 return resources.getPathForFirmware("MarlinUltimaker2.hex")
75         if machine_type == 'ultimaker2extended':
76                 if extruders > 2:
77                         return None
78                 if extruders > 1:
79                         return resources.getPathForFirmware("MarlinUltimaker2extended-dual.hex")
80                 return resources.getPathForFirmware("MarlinUltimaker2extended.hex")
81         if firmwareDict.has_key(machine_type):
82                 return resources.getPathForFirmware(firmwareDict[machine_type])
83         return None
84
85 def InstallFirmware(parent = None, filename = None, port = None, machineIndex = None):
86         dlg = InstallFirmwareDialog(parent, filename, port, machineIndex)
87         result = dlg.Run()
88         dlg.Destroy()
89         return result
90
91 class InstallFirmwareDialog(wx.Dialog):
92         def __init__(self, parent = None, filename = None, port = None, machineIndex = None):
93                 super(InstallFirmwareDialog, self).__init__(parent=parent, title=_("Firmware install for %s") % (profile.getMachineName(machineIndex).title()), size=(250, 100))
94                 if port is None:
95                         port = profile.getMachineSetting('serial_port')
96                 if filename is None:
97                         filename = getDefaultFirmware(machineIndex)
98                 self._machine_type = profile.getMachineSetting('machine_type', machineIndex)
99                 if self._machine_type == 'reprap':
100                         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)
101
102                 sizer = wx.BoxSizer(wx.VERTICAL)
103
104                 self.progressLabel = wx.StaticText(self, -1, 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nX\nX')
105                 sizer.Add(self.progressLabel, 0, flag=wx.ALIGN_CENTER|wx.ALL, border=5)
106                 self.progressGauge = wx.Gauge(self, -1)
107                 sizer.Add(self.progressGauge, 0, flag=wx.EXPAND)
108                 self.okButton = wx.Button(self, -1, _("OK"))
109                 self.okButton.Disable()
110                 self.okButton.Bind(wx.EVT_BUTTON, self.OnOk)
111                 sizer.Add(self.okButton, 0, flag=wx.ALIGN_CENTER|wx.ALL, border=5)
112                 self.SetSizer(sizer)
113
114                 self.filename = filename
115                 self.port = port
116
117                 self.Layout()
118                 self.Fit()
119                 self.success = False
120                 self.show_connect_dialog = False
121
122         def Run(self):
123                 if self.filename is None:
124                         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)
125                         return False
126                 self.success = False
127                 self.thread = threading.Thread(target=self.OnRun)
128                 self.thread.daemon = True
129                 self.thread.start()
130
131                 self.ShowModal()
132                 # Creating a MessageBox in a separate thread while main thread is locked inside a ShowModal
133                 # will cause Python to crash with X errors. So we need to show the dialog here instead
134                 if self.show_connect_dialog:
135                         wx.MessageBox(_("Failed to find machine for firmware upgrade\nIs your machine connected to the PC?"),
136                                                   _("Firmware update"), wx.OK | wx.ICON_ERROR)
137                 return self.success
138
139         def OnRun(self):
140                 wx.CallAfter(self.updateLabel, _("Reading firmware..."))
141                 hexFile = intelHex.readHex(self.filename)
142                 wx.CallAfter(self.updateLabel, _("Connecting to machine..."))
143                 programmer = stk500v2.Stk500v2()
144                 programmer.progressCallback = self.OnProgress
145                 if self.port == 'AUTO':
146                         wx.CallAfter(self.updateLabel, _("Please connect the printer to your\ncomputer with a USB cable and power it on."))
147                         while not programmer.isConnected():
148                                 for self.port in machineCom.serialList(True):
149                                         try:
150                                                 programmer.connect(self.port)
151                                                 break
152                                         except ispBase.IspError:
153                                                 programmer.close()
154                                 time.sleep(1)
155                                 if not self:
156                                         #Window destroyed
157                                         return
158                 else:
159                         try:
160                                 programmer.connect(self.port)
161                         except ispBase.IspError:
162                                 programmer.close()
163                         if not self:
164                                 #Window destroyed
165                                 return
166
167                 if not programmer.isConnected():
168                         self.show_connect_dialog = True
169                         wx.CallAfter(self.Close)
170                         return
171
172                 if self._machine_type == 'ultimaker':
173                         if programmer.hasChecksumFunction():
174                                 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+?"))
175                                 programmer.close()
176                                 wx.CallAfter(self.okButton.Enable)
177                                 return
178                 if self._machine_type == 'ultimaker_plus' or self._machine_type == 'ultimaker2':
179                         if not programmer.hasChecksumFunction():
180                                 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?"))
181                                 programmer.close()
182                                 wx.CallAfter(self.okButton.Enable)
183                                 return
184
185                 wx.CallAfter(self.updateLabel, _("Uploading firmware..."))
186                 try:
187                         programmer.programChip(hexFile)
188                         self.success = True
189                         wx.CallAfter(self.updateLabel, _("Done!\nInstalled firmware: %s") % (os.path.basename(self.filename)))
190                 except ispBase.IspError as e:
191                         wx.CallAfter(self.updateLabel, _("Failed to write firmware.\n") + str(e))
192
193                 programmer.close()
194                 wx.CallAfter(self.okButton.Enable)
195
196         def updateLabel(self, text):
197                 self.progressLabel.SetLabel(text)
198                 self.Layout()
199
200         def OnProgress(self, value, max):
201                 if self:
202                         wx.CallAfter(self.progressGauge.SetRange, max)
203                         wx.CallAfter(self.progressGauge.SetValue, value)
204                         taskbar.setProgress(self.GetParent(), value, max)
205
206         def OnOk(self, e):
207                 self.Close()
208                 taskbar.setBusy(self.GetParent(), False)
209
210         def OnClose(self, e):
211                 self.Destroy()
212
213
214 class AutoUpdateFirmware(wx.Dialog):
215         def __init__(self, parent, filename = None, port = None, machineIndex = None):
216                 super(AutoUpdateFirmware, self).__init__(parent=parent, title=_("Auto Firmware install"), size=(250, 500))
217                 if port is None:
218                         port = profile.getMachineSetting('serial_port')
219                 self._serial = None
220
221                 sizer = wx.BoxSizer(wx.VERTICAL)
222
223                 self.progressLabel = wx.StaticText(self, -1, 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nX\nX')
224                 sizer.Add(self.progressLabel, 0, flag=wx.ALIGN_CENTER|wx.ALL, border=5)
225                 self.progressGauge = wx.Gauge(self, -1)
226                 sizer.Add(self.progressGauge, 0, flag=wx.EXPAND)
227                 self.okButton = wx.Button(self, -1, _("OK"))
228                 self.okButton.Bind(wx.EVT_BUTTON, self.OnOk)
229                 sizer.Add(self.okButton, 0, flag=wx.ALIGN_CENTER|wx.ALL, border=5)
230
231                 f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
232                 self._termLog = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.TE_DONTWRAP)
233                 self._termLog.SetFont(f)
234                 self._termLog.SetEditable(0)
235                 self._termLog.SetMinSize((1, 400))
236                 self._termInput = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER)
237                 self._termInput.SetFont(f)
238                 sizer.Add(self._termLog, 0, flag=wx.ALIGN_CENTER|wx.ALL|wx.EXPAND)
239                 sizer.Add(self._termInput, 0, flag=wx.ALIGN_CENTER|wx.ALL|wx.EXPAND)
240
241                 self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self._termInput)
242
243                 self.SetSizer(sizer)
244
245                 self.filename = filename
246                 self.port = port
247
248                 self.Layout()
249                 self.Fit()
250
251                 self.thread = threading.Thread(target=self.OnRun)
252                 self.thread.daemon = True
253                 self.thread.start()
254
255                 self.read_thread = threading.Thread(target=self.OnSerialRead)
256                 self.read_thread.daemon = True
257                 self.read_thread.start()
258
259                 self.ShowModal()
260                 self.Destroy()
261                 return
262
263         def _addTermLog(self, line):
264                 if self._termLog is not None:
265                         if len(self._termLog.GetValue()) > 10000:
266                                 self._termLog.SetValue(self._termLog.GetValue()[-10000:])
267                         self._termLog.SetInsertionPointEnd()
268                         if type(line) != unicode:
269                                 line = unicode(line, 'utf-8', 'replace')
270                         self._termLog.AppendText(line.encode('utf-8', 'replace'))
271
272         def OnTermEnterLine(self, e):
273                 lines = self._termInput.GetValue().split(';')
274                 for line in lines:
275                         if line == '':
276                                 continue
277                         self._addTermLog('> %s\n' % (line))
278                         if self._serial is not None:
279                                 self._serial.write(line + '\n')
280
281         def OnRun(self):
282                 mtime = 0
283                 while bool(self):
284                         new_mtime = os.stat(self.filename).st_mtime
285                         if mtime != new_mtime:
286                                 mtime = new_mtime
287                                 if self._serial is not None:
288                                         self._serial.close()
289                                         self._serial = None
290                                 time.sleep(0.5)
291                                 self.OnInstall()
292                                 try:
293                                         self._serial = serial.Serial(self.port, 115200)
294                                 except:
295                                         pass
296                         time.sleep(0.5)
297
298         def OnSerialRead(self):
299                 while bool(self):
300                         if self._serial is None:
301                                 time.sleep(0.5)
302                         else:
303                                 try:
304                                         line = self._serial.readline()
305                                         wx.CallAfter(self._addTermLog, line)
306                                 except:
307                                         pass
308
309         def OnInstall(self):
310                 wx.CallAfter(self.okButton.Disable)
311                 wx.CallAfter(self.updateLabel, _("Reading firmware..."))
312                 hexFile = intelHex.readHex(self.filename)
313                 wx.CallAfter(self.updateLabel, _("Connecting to machine..."))
314                 programmer = stk500v2.Stk500v2()
315                 programmer.progressCallback = self.OnProgress
316                 if self.port == 'AUTO':
317                         wx.CallAfter(self.updateLabel, _("Please connect the printer to your\ncomputer with a USB cable and power it on."))
318                         while not programmer.isConnected():
319                                 for self.port in machineCom.serialList(True):
320                                         try:
321                                                 programmer.connect(self.port)
322                                                 break
323                                         except ispBase.IspError:
324                                                 pass
325                                 time.sleep(1)
326                                 if not self:
327                                         #Window destroyed
328                                         return
329                 else:
330                         try:
331                                 programmer.connect(self.port)
332                         except ispBase.IspError:
333                                 pass
334
335                 if not programmer.isConnected():
336                         wx.CallAfter(self.updateLabel, _("Failed to connect to programmer.\n"))
337                         return
338
339                 wx.CallAfter(self.updateLabel, _("Uploading firmware..."))
340                 try:
341                         programmer.programChip(hexFile)
342                         wx.CallAfter(self.updateLabel, _("Done!\nInstalled firmware: %s") % (os.path.basename(self.filename)))
343                 except ispBase.IspError as e:
344                         wx.CallAfter(self.updateLabel, _("Failed to write firmware.\n") + str(e))
345
346                 programmer.close()
347                 wx.CallAfter(self.okButton.Enable)
348
349         def updateLabel(self, text):
350                 self.progressLabel.SetLabel(text)
351                 self.Layout()
352
353         def OnProgress(self, value, max):
354                 wx.CallAfter(self.progressGauge.SetRange, max)
355                 wx.CallAfter(self.progressGauge.SetValue, value)
356                 taskbar.setProgress(self.GetParent(), value, max)
357
358         def OnOk(self, e):
359                 self.Close()
360                 taskbar.setBusy(self.GetParent(), False)
361
362         def OnClose(self, e):
363                 self.Destroy()
364