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