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