chiark / gitweb /
Add "Reload platform" to refresh all the objects on the platform from their files
[cura.git] / Cura / gui / tools / pidDebugger.py
1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
3
4 import wx
5 import time
6
7 from Cura.util import machineCom
8
9 class debuggerWindow(wx.Frame):
10         def __init__(self, parent):
11                 super(debuggerWindow, self).__init__(parent, title='Cura - PID Debugger')
12
13                 self.machineCom = None
14                 self.machineCom = machineCom.MachineCom(callbackObject=self)
15                 self.coolButton = wx.Button(self, -1, '0C')
16                 self.heatupButton = wx.Button(self, -1, '200C')
17                 self.heatupButton2 = wx.Button(self, -1, '260C')
18                 self.heatupButton3 = wx.Button(self, -1, '300C')
19                 self.fanOn = wx.Button(self, -1, 'Fan ON')
20                 self.fanOn50 = wx.Button(self, -1, 'Fan ON 50%')
21                 self.fanOff = wx.Button(self, -1, 'Fan OFF')
22                 self.graph = temperatureGraph(self)
23                 self.targetTemp = 0
24                 self.pValue = wx.TextCtrl(self, -1, '0')
25                 self.iValue = wx.TextCtrl(self, -1, '0')
26                 self.dValue = wx.TextCtrl(self, -1, '0')
27
28                 self.sizer = wx.GridBagSizer(0, 0)
29                 self.SetSizer(self.sizer)
30                 self.sizer.Add(self.graph, pos=(0, 0), span=(1, 8), flag=wx.EXPAND)
31                 self.sizer.Add(self.coolButton, pos=(1, 0), flag=wx.EXPAND)
32                 self.sizer.Add(self.heatupButton, pos=(1, 1), flag=wx.EXPAND)
33                 self.sizer.Add(self.heatupButton2, pos=(1, 2), flag=wx.EXPAND)
34                 self.sizer.Add(self.heatupButton3, pos=(1, 3), flag=wx.EXPAND)
35                 self.sizer.Add(self.fanOn, pos=(1, 4), flag=wx.EXPAND)
36                 self.sizer.Add(self.fanOn50, pos=(1, 5), flag=wx.EXPAND)
37                 self.sizer.Add(self.fanOff, pos=(1, 6), flag=wx.EXPAND)
38                 self.sizer.Add(self.pValue, pos=(2, 0), flag=wx.EXPAND)
39                 self.sizer.Add(self.iValue, pos=(2, 1), flag=wx.EXPAND)
40                 self.sizer.Add(self.dValue, pos=(2, 2), flag=wx.EXPAND)
41                 self.sizer.AddGrowableCol(7)
42                 self.sizer.AddGrowableRow(0)
43
44                 wx.EVT_CLOSE(self, self.OnClose)
45                 self.Bind(wx.EVT_BUTTON, lambda e: self.setTemp(0), self.coolButton)
46                 self.Bind(wx.EVT_BUTTON, lambda e: self.setTemp(200), self.heatupButton)
47                 self.Bind(wx.EVT_BUTTON, lambda e: self.setTemp(260), self.heatupButton2)
48                 self.Bind(wx.EVT_BUTTON, lambda e: self.setTemp(300), self.heatupButton3)
49                 self.Bind(wx.EVT_BUTTON, lambda e: self.machineCom.sendCommand('M106'), self.fanOn)
50                 self.Bind(wx.EVT_BUTTON, lambda e: self.machineCom.sendCommand('M106 S128'), self.fanOn50)
51                 self.Bind(wx.EVT_BUTTON, lambda e: self.machineCom.sendCommand('M107'), self.fanOff)
52                 self.Bind(wx.EVT_TEXT, self.updatePID, self.pValue)
53                 self.Bind(wx.EVT_TEXT, self.updatePID, self.iValue)
54                 self.Bind(wx.EVT_TEXT, self.updatePID, self.dValue)
55
56                 self.Layout()
57                 self.Fit()
58
59         def updatePID(self, e):
60                 try:
61                         p = float(self.pValue.GetValue())
62                         i = float(self.iValue.GetValue())
63                         d = float(self.dValue.GetValue())
64                 except:
65                         return
66                 self.machineCom.sendCommand("M301 P%f I%f D%f" % (p, i, d))
67
68         def setTemp(self, temp):
69                 self.targetTemp = temp
70                 self.machineCom.sendCommand("M104 S%d" % (temp))
71
72         def OnClose(self, e):
73                 self.machineCom.close()
74                 self.Destroy()
75
76         def mcLog(self, message):
77                 pass
78
79         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
80                 pass
81
82         def mcStateChange(self, state):
83                 if self.machineCom is not None and self.machineCom.isOperational():
84                         self.machineCom.sendCommand("M503\n")
85                         self.machineCom.sendCommand("M503\n")
86
87         def mcMessage(self, message):
88                 if 'PIDDEBUG' in message:
89                         #echo: PIDDEBUG 0: Input 40.31 Output 0.00 pTerm 0.00 iTerm 0.00 dTerm 0.00
90                         message = message.strip().split()
91                         temperature = float(message[message.index("Input")+1])
92                         heater_output = float(message[message.index("Output")+1])
93                         pTerm = float(message[message.index("pTerm")+1])
94                         iTerm = float(message[message.index("iTerm")+1])
95                         dTerm = float(message[message.index("dTerm")+1])
96
97                         self.graph.addPoint(temperature, heater_output, pTerm, iTerm, dTerm, self.targetTemp)
98                 elif 'M301' in message:
99                         for m in message.strip().split():
100                                 if m[0] == 'P':
101                                         wx.CallAfter(self.pValue.SetValue, m[1:])
102                                 if m[0] == 'I':
103                                         wx.CallAfter(self.iValue.SetValue, m[1:])
104                                 if m[0] == 'D':
105                                         wx.CallAfter(self.dValue.SetValue, m[1:])
106
107         def mcProgress(self, lineNr):
108                 pass
109
110         def mcZChange(self, newZ):
111                 pass
112
113 class temperatureGraph(wx.Panel):
114         def __init__(self, parent):
115                 super(temperatureGraph, self).__init__(parent)
116
117                 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
118                 self.Bind(wx.EVT_SIZE, self.OnSize)
119                 self.Bind(wx.EVT_PAINT, self.OnDraw)
120
121                 self.lastDraw = time.time() - 1.0
122                 self.points = []
123                 self.backBuffer = None
124                 self.SetMinSize((320, 200))
125                 self.addPoint(0,0,0,0,0,0)
126
127         def OnEraseBackground(self, e):
128                 pass
129
130         def OnSize(self, e):
131                 if self.backBuffer is None or self.GetSize() != self.backBuffer.GetSize():
132                         self.backBuffer = wx.EmptyBitmap(*self.GetSizeTuple())
133                         self.UpdateDrawing(True)
134
135         def OnDraw(self, e):
136                 dc = wx.BufferedPaintDC(self, self.backBuffer)
137
138         def _drawBackgroundForLine(self, dc, color, f):
139                 w, h = self.GetSizeTuple()
140                 color = wx.Pen(color)
141                 dc.SetPen(color)
142                 x0 = 0
143                 v0 = 0
144                 for p in self.points:
145                         x1 = int(w - (self.now - p[0]) * self.timeScale)
146                         value = f(p)
147                         for x in xrange(x0, x1 + 1):
148                                 v = float(x - x0) / float(x1 - x0 + 1) * (value - v0) + v0
149                                 dc.DrawLine(x, h, x, h - (v * h / 300))
150                         v0 = value
151                         x0 = x1 + 1
152
153         def _drawLine(self, dc, color, f):
154                 w, h = self.GetSizeTuple()
155                 color = wx.Pen(color)
156                 dc.SetPen(color)
157                 x0 = 0
158                 v0 = 0
159                 for p in self.points:
160                         x1 = int(w - (self.now - p[0]) * self.timeScale)
161                         value = f(p)
162                         dc.DrawLine(x0, h - (v0 * h / 300), x1, h - (value * h / 300), )
163                         dc.DrawPoint(x1, h - (value * h / 300), )
164                         v0 = value
165                         x0 = x1 + 1
166
167         def UpdateDrawing(self, force=False):
168                 now = time.time()
169                 self.timeScale = 10
170                 self.now = now
171                 if not force and now - self.lastDraw < 0.1:
172                         return
173                 self.lastDraw = now
174                 dc = wx.MemoryDC()
175                 dc.SelectObject(self.backBuffer)
176                 dc.Clear()
177                 dc.SetFont(wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT))
178                 w, h = self.GetSizeTuple()
179                 bgLinePen = wx.Pen('#A0A0A0')
180
181                 #Draw the background up to the current temperatures.
182                 self._drawBackgroundForLine(dc, '#FFD0D0', lambda p: p[1])#temp
183                 self._drawBackgroundForLine(dc, '#D0D0FF', lambda p: p[3])#pTerm
184                 self._drawBackgroundForLine(dc, '#D0FFD0', lambda p: abs(p[5]))#dTerm
185
186                 #Draw the grid
187                 for x in xrange(w, 0, -5 * self.timeScale):
188                         dc.SetPen(bgLinePen)
189                         dc.DrawLine(x, 0, x, h)
190                 tmpNr = 0
191                 for y in xrange(h - 1, 0, -h * 50 / 300):
192                         dc.SetPen(bgLinePen)
193                         dc.DrawLine(0, y, w, y)
194                         dc.DrawText(str(tmpNr), 0, y - dc.GetFont().GetPixelSize().GetHeight())
195                         tmpNr += 50
196                 dc.DrawLine(0, 0, w, 0)
197                 dc.DrawLine(0, 0, 0, h)
198                 if len(self.points) > 10:
199                         tempAvg = 0.0
200                         heaterAvg = 0.0
201                         for n in xrange(0, 10):
202                                 tempAvg += self.points[-n-1][1]
203                                 heaterAvg += self.points[-n-1][2]
204                         dc.DrawText("Temp: %d Heater: %d" % (tempAvg / 10, heaterAvg * 100 / 255 / 10), 0, 0)
205
206                 #Draw the main lines
207                 self._drawLine(dc, '#404040', lambda p: p[6])#target
208                 self._drawLine(dc, '#40FFFF', lambda p: p[3])#pTerm
209                 self._drawLine(dc, '#FF40FF', lambda p: p[4])#iTerm
210                 self._drawLine(dc, '#FFFF40', lambda p: p[5])#dTerm
211                 self._drawLine(dc, '#4040FF', lambda p: -p[5])#dTerm
212                 self._drawLine(dc, '#FF4040', lambda p: p[1])#temp
213                 self._drawLine(dc, '#40FF40', lambda p: p[2])#heater
214
215                 del dc
216                 self.Refresh(eraseBackground=False)
217                 self.Update()
218
219                 if len(self.points) > 0 and (time.time() - self.points[0][0]) > (w + 20) / self.timeScale:
220                         self.points.pop(0)
221
222         def addPoint(self, temperature, heater_output, pTerm, iTerm, dTerm, targetTemp):
223                 self.points.append([time.time(), temperature, heater_output, pTerm, iTerm, dTerm, targetTemp])
224                 wx.CallAfter(self.UpdateDrawing)