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