chiark / gitweb /
Update the doodle3d class to auto-detect local network boxes, as well as access point...
[cura.git] / Cura / util / printerConnection / doodle3dConnect.py
index e2f89f53c1488209413e2a12669f2e5d74e925f3..0cb6203a1d1f3b9ea07052f98db14e1122d97faa 100644 (file)
@@ -11,20 +11,24 @@ from Cura.util.printerConnection import printerConnectionBase
 #Class to connect and print files with the doodle3d.com wifi box
 # Auto-detects if the Doodle3D box is available with a printer
 class doodle3dConnect(printerConnectionBase.printerConnectionBase):
+       PRINTER_LIST_HOST = 'connect.doodle3d.com'
+       PRINTER_LIST_PATH = '/api/list.php'
+
        def __init__(self):
                super(doodle3dConnect, self).__init__()
 
                self._http = None
+               self._host = None
                self._isAvailable = False
                self._printing = False
                self._fileBlocks = []
                self._blockIndex = None
                self._lineCount = 0
                self._progressLine = 0
-               self._hotendTemperature = [0] * 4
-               self._bedTemperature = 0
+               self._hotendTemperature = [None] * 4
+               self._bedTemperature = None
 
-               self.checkThread = threading.Thread(target=self._doodle3Dthread)
+               self.checkThread = threading.Thread(target=self._doodle3DThread)
                self.checkThread.daemon = True
                self.checkThread.start()
 
@@ -38,13 +42,16 @@ class doodle3dConnect(printerConnectionBase.printerConnectionBase):
                blockSize = 0
                f = open(filename, "r")
                for line in f:
+                       #Strip out comments, we do not need to send comments
                        if ';' in line:
                                line = line[:line.index(';')]
+                       #Strip out whitespace at the beginning/end this saves data to send.
                        line = line.strip()
 
                        if len(line) < 1:
                                continue
                        self._lineCount += 1
+                       #Put the lines in 2k sized blocks, so we can send those blocks as http requests.
                        if blockSize + len(line) > 2048:
                                self._fileBlocks.append('\n'.join(block) + '\n')
                                block = []
@@ -72,6 +79,12 @@ class doodle3dConnect(printerConnectionBase.printerConnectionBase):
        def isPrinting(self):
                return self._printing
 
+       #Amount of progression of the current print file. 0.0 to 1.0
+       def getPrintProgress(self):
+               if self._lineCount < 1:
+                       return 0.0
+               return float(self._progressLine) / float(self._lineCount)
+
        # Return if the printer with this connection type is available
        def isAvailable(self):
                return self._isAvailable
@@ -79,61 +92,109 @@ class doodle3dConnect(printerConnectionBase.printerConnectionBase):
        # Get the connection status string. This is displayed to the user and can be used to communicate
        #  various information to the user.
        def getStatusString(self):
+               if not self._isAvailable:
+                       return "Doodle3D box not found"
                return "TODO"
 
-       def _doodle3Dthread(self):
+       #Get the temperature of an extruder, returns None is no temperature is known for this extruder
+       def getTemperature(self, extruder):
+               return self._hotendTemperature[extruder]
+
+       #Get the temperature of the heated bed, returns None is no temperature is known for the heated bed
+       def getBedTemperature(self):
+               return self._bedTemperature
+
+       def _doodle3DThread(self):
                while True:
-                       stateReply = self._request('GET', '/d3dapi/printer/state')
-                       if stateReply is None:  #No API, wait 15 seconds before looking for Doodle3D again.
-                               self._isAvailable = False
-                               time.sleep(15)
-                               continue
-                       if not stateReply:              #API gave back an error (this can happen if the Doodle3D box is connecting to the printer)
+                       while self._host is None:
+                               printerList = self._request('GET', self.PRINTER_LIST_PATH, host=self.PRINTER_LIST_HOST)
+                               if not printerList or type(printerList) is not dict or 'data' not in printerList or type(printerList['data']) is not list:
+                                       #Check if we are connected to the Doodle3D box in access point mode, as this gives an
+                                       # invalid reply on the printer list API
+                                       printerList = {'data': [{'localip': 'draw.doodle3d.com'}]}
+
+                               #Add the 192.168.5.1 IP to the list of printers to check, as this is the LAN port IP, which could also be available.
+                               # (connect.doodle3d.com also checks for this IP in the javascript code)
+                               printerList['data'].append({'localip': '192.168.5.1'})
+
+                               #Check the status of each possible IP, if we find a valid box with a printer connected. Use that IP.
+                               for possiblePrinter in printerList['data']:
+                                       status = self._request('GET', '/d3dapi/info/status', host=possiblePrinter['localip'])
+                                       if status and 'data' in status and status['data']['state'] == 'idle':
+                                               self._host = possiblePrinter['localip']
+                                               break
+
+                               if self._host is None:
+                                       #If we cannot find a doodle3d box, delay a minute and request the list again.
+                                       # This so we do not stress the connect.doodle3d.com api too much
+                                       time.sleep(60)
+
+                       stateReply = self._request('GET', '/d3dapi/info/status')
+                       if stateReply is None or not stateReply or stateReply['data']['state'] == 'disconnected':
+                               # No API, wait 5 seconds before looking for Doodle3D again.
+                               # API gave back an error (this can happen if the Doodle3D box is connecting to the printer)
+                               # Or no printer connected
+                               self._host = None
                                self._isAvailable = False
+                               self._doCallback()
                                time.sleep(5)
                                continue
-                       self._isAvailable = True
+
+                       #We got a valid status, set the doodle3d printer as available.
+                       if not self._isAvailable:
+                               self._isAvailable = True
+
+                       if 'hotend' in stateReply['data']:
+                               self._hotendTemperature[0] = stateReply['data']['hotend']
+                       if 'bed' in stateReply['data']:
+                               self._bedTemperature = stateReply['data']['bed']
 
                        if stateReply['data']['state'] == 'idle':
                                if self._printing:
                                        if self._blockIndex < len(self._fileBlocks):
                                                if self._request('POST', '/d3dapi/printer/print', {'gcode': self._fileBlocks[self._blockIndex], 'start': 'True', 'first': 'True'}):
                                                        self._blockIndex += 1
+                                               else:
+                                                       time.sleep(1)
                                        else:
                                                self._printing = False
-                       if stateReply['data']['state'] == 'printing':
+                               else:
+                                       time.sleep(5)
+                       elif stateReply['data']['state'] == 'printing':
                                if self._printing:
                                        if self._blockIndex < len(self._fileBlocks):
                                                for n in xrange(0, 5):
                                                        if self._blockIndex < len(self._fileBlocks):
                                                                if self._request('POST', '/d3dapi/printer/print', {'gcode': self._fileBlocks[self._blockIndex]}):
                                                                        self._blockIndex += 1
+                                                               else:
+                                                                       time.sleep(1)
                                        else:
                                                #If we are no longer sending new GCode delay a bit so we request the status less often.
                                                time.sleep(1)
-                                       progress = self._request('GET', '/d3dapi/printer/progress')
-                                       if progress:
-                                               self._progressLine = progress['data']['current_line']
-                                       temperature = self._request('GET', '/d3dapi/printer/temperature')
-                                       if temperature:
-                                               self._hotendTemperature[0] = temperature['data']['hotend']
-                                               self._bedTemperature = temperature['data']['bed']
+                                       if 'current_line' in stateReply['data']:
+                                               self._progressLine = stateReply['data']['current_line']
                                else:
                                        #Got a printing state without us having send the print file, set the state to printing, but make sure we never send anything.
-                                       progress = self._request('GET', '/d3dapi/printer/progress')
-                                       if progress:
+                                       if 'current_line' in stateReply['data'] and 'total_lines' in stateReply['data']:
                                                self._printing = True
                                                self._blockIndex = len(self._fileBlocks)
-                                               self._lineCount = progress['data']['total_lines']
+                                               self._progressLine = stateReply['data']['current_line']
+                                               self._lineCount = stateReply['data']['total_lines']
+                                       time.sleep(1)
+                       self._doCallback()
+
+       def _request(self, method, path, postData = None, host = None):
+               if host is None:
+                       host = self._host
+               if self._http is None or self._http.host != host:
+                       self._http = httpclient.HTTPConnection(host)
 
-       def _request(self, method, path, postData = None):
-               if self._http is None:
-                       self._http = httpclient.HTTPConnection('draw.doodle3d.com')
                try:
                        if postData is not None:
-                               self._http.request(method, path, urllib.urlencode(postData), {"Content-type": "application/x-www-form-urlencoded"})
+                               self._http.request(method, path, urllib.urlencode(postData), {"Content-type": "application/x-www-form-urlencoded", "User-Agent": "Cura Doodle3D connection"})
                        else:
-                               self._http.request(method, path, headers={"Content-type": "application/x-www-form-urlencoded"})
+                               self._http.request(method, path, headers={"Content-type": "application/x-www-form-urlencoded", "User-Agent": "Cura Doodle3D connection"})
                except:
                        self._http.close()
                        return None
@@ -169,5 +230,5 @@ if __name__ == '__main__':
        d.startPrint()
        while d.isPrinting() and d.isAvailable():
                time.sleep(1)
-               print d._progressLine, d._lineCount, d._blockIndex, len(d._fileBlocks)
+               print d.getTemperature(0), d.getStatusString(), d.getPrintProgress(), d._progressLine, d._lineCount, d._blockIndex, len(d._fileBlocks)
        print 'Done'