chiark / gitweb /
Improve serial communication.
[cura.git] / Cura / util / printerConnection / serialConnection.py
1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
2
3 import threading
4 import time
5 import platform
6 import os
7 import sys
8 import subprocess
9 import json
10
11 from Cura.util import machineCom
12 from Cura.util.printerConnection import printerConnectionBase
13
14 class serialConnectionGroup(printerConnectionBase.printerConnectionGroup):
15         def __init__(self):
16                 super(serialConnectionGroup, self).__init__("USB")
17                 self._connectionMap = {}
18
19         def getAvailableConnections(self):
20                 serialList = machineCom.serialList(True)
21                 for port in machineCom.serialList(True):
22                         if port not in self._connectionMap:
23                                 self._connectionMap[port] = serialConnection(port)
24                 for key in self._connectionMap.keys():
25                         if key not in serialList and not self._connectionMap[key].isActiveConnectionOpen():
26                                 self._connectionMap.pop(key)
27                 return self._connectionMap.values()
28
29         def getIconID(self):
30                 return 6
31
32         def getPriority(self):
33                 return 50
34
35 class serialConnection(printerConnectionBase.printerConnectionBase):
36         def __init__(self, port):
37                 super(serialConnection, self).__init__(port)
38                 self._portName = port
39
40                 self._process = None
41                 self._thread = None
42
43                 self._temperature = []
44
45                 self._lineCount = 0
46                 self._commState = None
47                 self._commStateString = None
48                 self._gcodeData = []
49
50         #Load the data into memory for printing, returns True on success
51         def loadGCodeData(self, dataStream):
52                 if self.isPrinting() is None:
53                         return False
54                 self._gcodeData = []
55                 for line in dataStream:
56                         #Strip out comments, we do not need to send comments
57                         if ';' in line:
58                                 line = line[:line.index(';')]
59                         #Strip out whitespace at the beginning/end this saves data to send.
60                         line = line.strip()
61
62                         if len(line) < 1:
63                                 continue
64                         self._gcodeData.append(line)
65                 return True
66
67         #Start printing the previously loaded file
68         def startPrint(self):
69                 if self.isPrinting() or len(self._gcodeData) < 1 or self._process is None:
70                         return
71                 self._process.stdin.write('STOP\n')
72                 for line in self._gcodeData:
73                         self._process.stdin.write('G:%s\n' % (line))
74                 self._process.stdin.write('START\n')
75
76         #Abort the previously loaded print file
77         def cancelPrint(self):
78                 pass
79
80         def isPrinting(self):
81                 return self._commState == machineCom.MachineCom.STATE_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 True
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                 return "%s" % (self._commStateString)
97
98         #Returns true if we need to establish an active connection. True for serial connections.
99         def hasActiveConnection(self):
100                 return True
101
102         #Open the active connection to the printer so we can send commands
103         def openActiveConnection(self):
104                 self.closeActiveConnection()
105                 self._thread = threading.Thread(target=self._serialCommunicationThread)
106                 self._thread.daemon = True
107                 self._thread.start()
108
109         #Close the active connection to the printer
110         def closeActiveConnection(self):
111                 if self._process is not None:
112                         self._process.terminate()
113                         self._thread.join()
114
115         #Is the active connection open right now.
116         def isActiveConnectionOpen(self):
117                 if self._process is None:
118                         return False
119                 return self._commState == machineCom.MachineCom.STATE_OPERATIONAL or self._commState == machineCom.MachineCom.STATE_PRINTING or self._commState == machineCom.MachineCom.STATE_PAUSED
120
121         def getTemperature(self, extruder):
122                 if extruder >= len(self._temperature):
123                         return None
124                 return self._temperature[extruder]
125
126         def _serialCommunicationThread(self):
127                 if platform.system() == "Darwin" and hasattr(sys, 'frozen'):
128                         cmdList = [os.path.join(os.path.dirname(sys.executable), 'Cura')]
129                 else:
130                         cmdList = [sys.executable, '-m', 'Cura.serialCommunication']
131                 cmdList += [self._portName]
132                 if platform.system() == "Darwin":
133                         if platform.machine() == 'i386':
134                                 cmdList = ['arch', '-i386'] + cmdList
135                 self._process = subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
136                 line = self._process.stdout.readline()
137                 while len(line) > 0:
138                         line = line.strip()
139                         line = line.split(':', 1)
140                         if line[0] == 'log':
141                                 pass
142                         elif line[0] == 'temp':
143                                 line = line[1].split(':', 1)
144                                 self._temperature = json.loads(line[0])
145                                 self._doCallback()
146                         elif line[0] == 'message':
147                                 self._doCallback(line[1])
148                         elif line[0] == 'state':
149                                 line = line[1].split(':', 1)
150                                 self._commState = int(line[0])
151                                 self._commStateString = line[1]
152                                 self._doCallback()
153                         else:
154                                 print line
155                         line = self._process.stdout.readline()
156                 self._process = None