chiark / gitweb /
Add quickprint settings for TAZ 5 0.5mm nozzle
[cura.git] / Cura / util / printerConnection / serialConnection.py
1 """
2 The serial/USB printer connection. Uses a 2nd python process to connect to the printer so we never
3 have locking problems where other threads in python can block the USB printing.
4 """
5 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
6
7 import threading
8 import time
9 import platform
10 import os
11 import sys
12 import subprocess
13 import json
14
15 from Cura.util import profile
16 from Cura.util import machineCom
17 from Cura.util.printerConnection import printerConnectionBase
18
19 class serialConnectionGroup(printerConnectionBase.printerConnectionGroup):
20         """
21         The serial connection group. Keeps track of all available serial ports,
22         and builds a serialConnection for each port.
23         """
24         def __init__(self):
25                 super(serialConnectionGroup, self).__init__("USB")
26                 self._connectionMap = {}
27
28         def getAvailableConnections(self):
29                 if profile.getMachineSetting('serial_port') == 'AUTO':
30                         serialList = machineCom.serialList(True)
31                 else:
32                         serialList = [profile.getMachineSetting('serial_port')]
33                 for port in serialList:
34                         if port not in self._connectionMap:
35                                 self._connectionMap[port] = serialConnection(port)
36                 for key in self._connectionMap.keys():
37                         if key not in serialList and not self._connectionMap[key].isActiveConnectionOpen():
38                                 self._connectionMap.pop(key)
39                 return self._connectionMap.values()
40
41         def getIconID(self):
42                 return 6
43
44         def getPriority(self):
45                 return 50
46
47 class serialConnection(printerConnectionBase.printerConnectionBase):
48         """
49         A serial connection. Needs to build an active-connection.
50         When an active connection is created, a 2nd python process is spawned which handles the actual serial communication.
51
52         This class communicates with the Cura.serialCommunication module trough stdin/stdout pipes.
53         """
54         def __init__(self, port):
55                 super(serialConnection, self).__init__(port)
56                 self._portName = port
57
58                 self._process = None
59                 self._thread = None
60
61                 self._temperature = []
62                 self._targetTemperature = []
63                 self._bedTemperature = 0
64                 self._targetBedTemperature = 0
65                 self._log = []
66
67                 self._commState = None
68                 self._commStateString = None
69                 self._gcodeData = []
70
71         #Load the data into memory for printing, returns True on success
72         def loadGCodeData(self, dataStream):
73                 if self.isPrinting() is None:
74                         return False
75                 self._gcodeData = []
76                 for line in dataStream:
77                         #Strip out comments, we do not need to send comments
78                         if ';' in line:
79                                 line = line[:line.index(';')]
80                         #Strip out whitespace at the beginning/end this saves data to send.
81                         line = line.strip()
82
83                         if len(line) < 1:
84                                 continue
85                         self._gcodeData.append(line)
86                 return True
87
88         #Start printing the previously loaded file
89         def startPrint(self):
90                 if self.isPrinting() or len(self._gcodeData) < 1 or self._process is None:
91                         return
92                 self._process.stdin.write('STOP\n')
93                 for line in self._gcodeData:
94                         self._process.stdin.write('G:%s\n' % (line))
95                 self._process.stdin.write('START\n')
96                 self._printProgress = 0
97
98         #Abort the previously loaded print file
99         def cancelPrint(self):
100                 if not self.isPrinting()or self._process is None:
101                         return
102                 self._process.stdin.write('STOP\n')
103                 self._printProgress = 0
104
105         def isPrinting(self):
106                 return self._commState == machineCom.MachineCom.STATE_PRINTING
107
108         #Amount of progression of the current print file. 0.0 to 1.0
109         def getPrintProgress(self):
110                 if len(self._gcodeData) < 1:
111                         return 0.0
112                 return float(self._printProgress) / float(len(self._gcodeData))
113
114         # Return if the printer with this connection type is available
115         def isAvailable(self):
116                 return True
117
118         # Get the connection status string. This is displayed to the user and can be used to communicate
119         #  various information to the user.
120         def getStatusString(self):
121                 return "%s" % (self._commStateString)
122
123         #Returns true if we need to establish an active connection. True for serial connections.
124         def hasActiveConnection(self):
125                 return True
126
127         #Open the active connection to the printer so we can send commands
128         def openActiveConnection(self):
129                 self.closeActiveConnection()
130                 self._thread = threading.Thread(target=self._serialCommunicationThread)
131                 self._thread.daemon = True
132                 self._thread.start()
133
134         #Close the active connection to the printer
135         def closeActiveConnection(self):
136                 if self._process is not None:
137                         self._process.terminate()
138                         self._thread.join()
139
140         #Is the active connection open right now.
141         def isActiveConnectionOpen(self):
142                 if self._process is None:
143                         return False
144                 return self._commState == machineCom.MachineCom.STATE_OPERATIONAL or self._commState == machineCom.MachineCom.STATE_PRINTING or self._commState == machineCom.MachineCom.STATE_PAUSED
145
146         #Are we trying to open an active connection right now.
147         def isActiveConnectionOpening(self):
148                 if self._process is None:
149                         return False
150                 return self._commState == machineCom.MachineCom.STATE_OPEN_SERIAL or self._commState == machineCom.MachineCom.STATE_CONNECTING or self._commState == machineCom.MachineCom.STATE_DETECT_SERIAL or self._commState == machineCom.MachineCom.STATE_DETECT_BAUDRATE
151
152         def getTemperature(self, extruder):
153                 if extruder >= len(self._temperature):
154                         return None
155                 return self._temperature[extruder]
156
157         def getBedTemperature(self):
158                 return self._bedTemperature
159
160         #Are we able to send a direct command with sendCommand at this moment in time.
161         def isAbleToSendDirectCommand(self):
162                 return self.isActiveConnectionOpen()
163
164         #Directly send a command to the printer.
165         def sendCommand(self, command):
166                 if self._process is None:
167                         return
168                 self._process.stdin.write('C:%s\n' % (command))
169
170         #Returns true if we got some kind of error. The getErrorLog returns all the information to diagnose the problem.
171         def isInErrorState(self):
172                 return self._commState == machineCom.MachineCom.STATE_ERROR or self._commState == machineCom.MachineCom.STATE_CLOSED_WITH_ERROR
173
174         #Returns the error log in case there was an error.
175         def getErrorLog(self):
176                 return '\n'.join(self._log)
177
178         def _serialCommunicationThread(self):
179                 if platform.system() == "Darwin" and hasattr(sys, 'frozen'):
180                         cmdList = [os.path.join(os.path.dirname(sys.executable), 'Cura'), '--serialCommunication']
181                         cmdList += [self._portName + ':' + profile.getMachineSetting('serial_baud')]
182                 else:
183                         cmdList = [sys.executable, '-m', 'Cura.serialCommunication']
184                         cmdList += [self._portName, profile.getMachineSetting('serial_baud')]
185                 if platform.system() == "Darwin":
186                         if platform.machine() == 'i386':
187                                 cmdList = ['arch', '-i386'] + cmdList
188                 self._process = subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
189                 line = self._process.stdout.readline()
190                 while len(line) > 0:
191                         line = line.strip()
192                         line = line.split(':', 1)
193                         if line[0] == '':
194                                 pass
195                         elif line[0] == 'log':
196                                 self._log.append(line[1])
197                                 if len(self._log) > 30:
198                                         self._log.pop(0)
199                         elif line[0] == 'temp':
200                                 line = line[1].split(':')
201                                 self._temperature = json.loads(line[0])
202                                 self._targetTemperature = json.loads(line[1])
203                                 self._bedTemperature = float(line[2])
204                                 self._targetBedTemperature = float(line[3])
205                                 self._doCallback()
206                         elif line[0] == 'message':
207                                 self._doCallback(line[1])
208                         elif line[0] == 'state':
209                                 line = line[1].split(':', 1)
210                                 self._commState = int(line[0])
211                                 self._commStateString = line[1]
212                                 self._doCallback()
213                         elif line[0] == 'progress':
214                                 self._printProgress = int(line[1])
215                                 self._doCallback()
216                         else:
217                                 print line
218                         line = self._process.stdout.readline()
219                 self._process = None