1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
5 import httplib as httpclient
9 from Cura.util.printerConnection import printerConnectionBase
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
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):
20 super(doodle3dConnect, self).__init__()
23 self._isAvailable = False
24 self._printing = False
26 self._blockIndex = None
28 self._progressLine = 0
29 self._hotendTemperature = [None] * 4
30 self._bedTemperature = None
32 self.checkThread = threading.Thread(target=self._doodle3Dthread)
33 self.checkThread.daemon = True
34 self.checkThread.start()
36 #Load the file into memory for printing.
37 def loadFile(self, filename):
44 f = open(filename, "r")
46 #Strip out comments, we do not need to send comments
48 line = line[:line.index(';')]
49 #Strip out whitespace at the beginning/end this saves data to send.
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')
60 blockSize += len(line) + 1
62 self._fileBlocks.append('\n'.join(block) + '\n')
65 #Start printing the previously loaded file
67 if self._printing or len(self._fileBlocks) < 1:
69 self._progressLine = 0
73 #Abort the previously loaded print file
74 def cancelPrint(self):
75 if not self._printing:
77 if self._request('POST', '/d3dapi/printer/stop', {'gcode': 'M104 S0\nG28'}):
78 self._printing = False
83 #Amount of progression of the current print file. 0.0 to 1.0
84 def getPrintProgress(self):
85 if self._lineCount < 1:
87 return float(self._progressLine) / float(self._lineCount)
89 # Return if the printer with this connection type is available
90 def isAvailable(self):
91 return self._isAvailable
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"
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]
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
108 def _doodle3Dthread(self):
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
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
121 if not self._isAvailable:
122 self._isAvailable = True
125 if stateReply['data']['state'] == 'idle':
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
131 self._printing = False
134 elif stateReply['data']['state'] == '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
142 #If we are no longer sending new GCode delay a bit so we request the status less often.
144 progress = self._request('GET', '/d3dapi/printer/progress')
146 self._progressLine = progress['data']['current_line']
147 temperature = self._request('GET', '/d3dapi/printer/temperature')
149 self._hotendTemperature[0] = temperature['data']['hotend']
150 self._bedTemperature = temperature['data']['bed']
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')
155 self._printing = True
156 self._blockIndex = len(self._fileBlocks)
157 self._lineCount = progress['data']['total_lines']
160 def _request(self, method, path, postData = None):
161 if self._http is None:
162 self._http = httpclient.HTTPConnection('draw.doodle3d.com')
164 if postData is not None:
165 self._http.request(method, path, urllib.urlencode(postData), {"Content-type": "application/x-www-form-urlencoded"})
167 self._http.request(method, path, headers={"Content-type": "application/x-www-form-urlencoded"})
172 response = self._http.getresponse()
173 responseText = response.read()
178 response = json.loads(responseText)
182 if response['status'] != 'success':
187 if __name__ == '__main__':
188 d = doodle3dConnect()
189 print 'Searching for Doodle3D box'
190 while not d.isAvailable():
193 while d.isPrinting():
194 print 'Doodle3D already printing! Requesting stop!'
198 print 'Doodle3D box found, printing!'
199 d.loadFile("C:/Models/belt-tensioner-wave_export.gcode")
201 while d.isPrinting() and d.isAvailable():
203 print d.getTemperature(0), d.getStatusString(), d.getPrintProgress(), d._progressLine, d._lineCount, d._blockIndex, len(d._fileBlocks)