chiark / gitweb /
7abc4631b7c633537ef360dc9e67f1d695a29ded
[cura.git] / Cura / util / printerConnection / doodle3dConnect.py
1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
2
3 import threading
4 import json
5 import httplib as httpclient
6 import urllib
7 import time
8
9 from Cura.util.printerConnection import printerConnectionBase
10
11 #Always LAN IP: 192.168.5.1
12 #Request: http://connect.doodle3d.com/api/list.php
13 #       {"status":"success","data":[{"id":"62.216.8.197\/10.0.0.35","0":"62.216.8.197\/10.0.0.35","remoteip":"62.216.8.197","1":"62.216.8.197","localip":"10.0.0.35","2":"10.0.0.35","wifiboxid":"Doodle3D-87354C","3":"Doodle3D-87354C","hidden":"0","4":"0","date":"2013-11-26 22:02:45","5":"2013-11-26 22:02:45"}],"debug":{"time":1385499835,"hourago":1385496235,"remoteip":"62.216.8.197"}}
14 #TODO: api/info/status
15
16 #Class to connect and print files with the doodle3d.com wifi box
17 # Auto-detects if the Doodle3D box is available with a printer
18 class doodle3dConnect(printerConnectionBase.printerConnectionBase):
19         def __init__(self):
20                 super(doodle3dConnect, self).__init__()
21
22                 self._http = None
23                 self._isAvailable = False
24                 self._printing = False
25                 self._fileBlocks = []
26                 self._blockIndex = None
27                 self._lineCount = 0
28                 self._progressLine = 0
29                 self._hotendTemperature = [None] * 4
30                 self._bedTemperature = None
31
32                 self.checkThread = threading.Thread(target=self._doodle3Dthread)
33                 self.checkThread.daemon = True
34                 self.checkThread.start()
35
36         #Load the file into memory for printing.
37         def loadFile(self, filename):
38                 if self._printing:
39                         return
40                 self._fileBlocks = []
41                 self._lineCount = 0
42                 block = []
43                 blockSize = 0
44                 f = open(filename, "r")
45                 for line in f:
46                         #Strip out comments, we do not need to send comments
47                         if ';' in line:
48                                 line = line[:line.index(';')]
49                         #Strip out whitespace at the beginning/end this saves data to send.
50                         line = line.strip()
51
52                         if len(line) < 1:
53                                 continue
54                         self._lineCount += 1
55                         #Put the lines in 2k sized blocks, so we can send those blocks as http requests.
56                         if blockSize + len(line) > 2048:
57                                 self._fileBlocks.append('\n'.join(block) + '\n')
58                                 block = []
59                                 blockSize = 0
60                         blockSize += len(line) + 1
61                         block.append(line)
62                 self._fileBlocks.append('\n'.join(block) + '\n')
63                 f.close()
64
65         #Start printing the previously loaded file
66         def startPrint(self):
67                 if self._printing or len(self._fileBlocks) < 1:
68                         return
69                 self._progressLine = 0
70                 self._blockIndex = 0
71                 self._printing = True
72
73         #Abort the previously loaded print file
74         def cancelPrint(self):
75                 if not self._printing:
76                         return
77                 if self._request('POST', '/d3dapi/printer/stop', {'gcode': 'M104 S0\nG28'}):
78                         self._printing = False
79
80         def isPrinting(self):
81                 return self._printing
82
83         #Amount of progression of the current print file. 0.0 to 1.0
84         def getPrintProgress(self):
85                 if self._lineCount < 1:
86                         return 0.0
87                 return float(self._progressLine) / float(self._lineCount)
88
89         # Return if the printer with this connection type is available
90         def isAvailable(self):
91                 return self._isAvailable
92
93         # Get the connection status string. This is displayed to the user and can be used to communicate
94         #  various information to the user.
95         def getStatusString(self):
96                 if not self._isAvailable:
97                         return "Doodle3D box not found"
98                 return "TODO"
99
100         #Get the temperature of an extruder, returns None is no temperature is known for this extruder
101         def getTemperature(self, extruder):
102                 return self._hotendTemperature[extruder]
103
104         #Get the temperature of the heated bed, returns None is no temperature is known for the heated bed
105         def getBedTemperature(self):
106                 return self._bedTemperature
107
108         def _doodle3Dthread(self):
109                 while True:
110                         stateReply = self._request('GET', '/d3dapi/printer/state')
111                         if stateReply is None:  #No API, wait 15 seconds before looking for Doodle3D again.
112                                 self._isAvailable = False
113                                 self._doCallback()
114                                 time.sleep(15)
115                                 continue
116                         if not stateReply:              #API gave back an error (this can happen if the Doodle3D box is connecting to the printer)
117                                 self._isAvailable = False
118                                 self._doCallback()
119                                 time.sleep(5)
120                                 continue
121                         if not self._isAvailable:
122                                 self._isAvailable = True
123                                 self._doCallback()
124
125                         if stateReply['data']['state'] == 'idle':
126                                 if self._printing:
127                                         if self._blockIndex < len(self._fileBlocks):
128                                                 if self._request('POST', '/d3dapi/printer/print', {'gcode': self._fileBlocks[self._blockIndex], 'start': 'True', 'first': 'True'}):
129                                                         self._blockIndex += 1
130                                         else:
131                                                 self._printing = False
132                                 else:
133                                         time.sleep(5)
134                         elif stateReply['data']['state'] == 'printing':
135                                 if self._printing:
136                                         if self._blockIndex < len(self._fileBlocks):
137                                                 for n in xrange(0, 5):
138                                                         if self._blockIndex < len(self._fileBlocks):
139                                                                 if self._request('POST', '/d3dapi/printer/print', {'gcode': self._fileBlocks[self._blockIndex]}):
140                                                                         self._blockIndex += 1
141                                         else:
142                                                 #If we are no longer sending new GCode delay a bit so we request the status less often.
143                                                 time.sleep(5)
144                                         progress = self._request('GET', '/d3dapi/printer/progress')
145                                         if progress:
146                                                 self._progressLine = progress['data']['current_line']
147                                         temperature = self._request('GET', '/d3dapi/printer/temperature')
148                                         if temperature:
149                                                 self._hotendTemperature[0] = temperature['data']['hotend']
150                                                 self._bedTemperature = temperature['data']['bed']
151                                 else:
152                                         #Got a printing state without us having send the print file, set the state to printing, but make sure we never send anything.
153                                         progress = self._request('GET', '/d3dapi/printer/progress')
154                                         if progress:
155                                                 self._printing = True
156                                                 self._blockIndex = len(self._fileBlocks)
157                                                 self._lineCount = progress['data']['total_lines']
158                                         time.sleep(5)
159
160         def _request(self, method, path, postData = None):
161                 if self._http is None:
162                         self._http = httpclient.HTTPConnection('draw.doodle3d.com')
163                 try:
164                         if postData is not None:
165                                 self._http.request(method, path, urllib.urlencode(postData), {"Content-type": "application/x-www-form-urlencoded"})
166                         else:
167                                 self._http.request(method, path, headers={"Content-type": "application/x-www-form-urlencoded"})
168                 except:
169                         self._http.close()
170                         return None
171                 try:
172                         response = self._http.getresponse()
173                         responseText = response.read()
174                 except:
175                         self._http.close()
176                         return None
177                 try:
178                         response = json.loads(responseText)
179                 except ValueError:
180                         self._http.close()
181                         return None
182                 if response['status'] != 'success':
183                         return False
184
185                 return response
186
187 if __name__ == '__main__':
188         d = doodle3dConnect()
189         print 'Searching for Doodle3D box'
190         while not d.isAvailable():
191                 time.sleep(1)
192
193         while d.isPrinting():
194                 print 'Doodle3D already printing! Requesting stop!'
195                 d.cancelPrint()
196                 time.sleep(5)
197
198         print 'Doodle3D box found, printing!'
199         d.loadFile("C:/Models/belt-tensioner-wave_export.gcode")
200         d.startPrint()
201         while d.isPrinting() and d.isAvailable():
202                 time.sleep(1)
203                 print d.getTemperature(0), d.getStatusString(), d.getPrintProgress(), d._progressLine, d._lineCount, d._blockIndex, len(d._fileBlocks)
204         print 'Done'