Major update on machine communication. Part 1, this breaks realtime speed tuning, pausing, M0/M1, height reporting and timelaps recording. But it is worth it.
parent
1f7924688d
commit
9c47af5797
|
@ -68,11 +68,13 @@ class PrintCommandButton(buttons.GenBitmapButton):
|
||||||
self.Bind(wx.EVT_BUTTON, self.OnClick)
|
self.Bind(wx.EVT_BUTTON, self.OnClick)
|
||||||
|
|
||||||
def OnClick(self, e):
|
def OnClick(self, e):
|
||||||
if self.parent.printIdx != None:
|
if self.parent.machineCom == None or self.parent.machineCom.isPrinting():
|
||||||
return;
|
return;
|
||||||
self.parent.sendCommand("G91")
|
if self.command.startswith('G1'):
|
||||||
self.parent.sendCommand(self.command)
|
self.parent.machineCom.sendCommand("G91")
|
||||||
self.parent.sendCommand("G90")
|
self.parent.machineCom.sendCommand(self.command)
|
||||||
|
if self.command.startswith('G1'):
|
||||||
|
self.parent.machineCom.sendCommand("G90")
|
||||||
e.Skip()
|
e.Skip()
|
||||||
|
|
||||||
class printWindow(wx.Frame):
|
class printWindow(wx.Frame):
|
||||||
|
@ -80,12 +82,9 @@ class printWindow(wx.Frame):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(printWindow, self).__init__(None, -1, title='Printing')
|
super(printWindow, self).__init__(None, -1, title='Printing')
|
||||||
self.machineCom = None
|
self.machineCom = None
|
||||||
self.machineConnected = False
|
|
||||||
self.thread = None
|
|
||||||
self.gcode = None
|
self.gcode = None
|
||||||
self.gcodeList = None
|
self.gcodeList = None
|
||||||
self.sendList = []
|
self.sendList = []
|
||||||
self.printIdx = None
|
|
||||||
self.temp = None
|
self.temp = None
|
||||||
self.bedTemp = None
|
self.bedTemp = None
|
||||||
self.bufferLineCount = 4
|
self.bufferLineCount = 4
|
||||||
|
@ -114,7 +113,7 @@ class printWindow(wx.Frame):
|
||||||
|
|
||||||
sb = wx.StaticBox(self.panel, label="Statistics")
|
sb = wx.StaticBox(self.panel, label="Statistics")
|
||||||
boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)
|
boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)
|
||||||
self.statsText = wx.StaticText(self.panel, -1, "Filament: ####.##m #.##g\nPrint time: #####:##")
|
self.statsText = wx.StaticText(self.panel, -1, "Filament: ####.##m #.##g\nPrint time: #####:##\nMachine state: Detecting baudrate")
|
||||||
boxsizer.Add(self.statsText, flag=wx.LEFT, border=5)
|
boxsizer.Add(self.statsText, flag=wx.LEFT, border=5)
|
||||||
|
|
||||||
self.sizer.Add(boxsizer, pos=(0,0), span=(5,1), flag=wx.EXPAND)
|
self.sizer.Add(boxsizer, pos=(0,0), span=(5,1), flag=wx.EXPAND)
|
||||||
|
@ -276,10 +275,10 @@ class printWindow(wx.Frame):
|
||||||
self.Centre()
|
self.Centre()
|
||||||
|
|
||||||
self.UpdateButtonStates()
|
self.UpdateButtonStates()
|
||||||
self.UpdateProgress()
|
#self.UpdateProgress()
|
||||||
|
|
||||||
def OnCameraTimer(self, e):
|
def OnCameraTimer(self, e):
|
||||||
if self.printIdx != None:
|
if self.machineCom != None and not self.machineCom.isPrinting():
|
||||||
return
|
return
|
||||||
self.cam.takeNewImage()
|
self.cam.takeNewImage()
|
||||||
self.camPage.Refresh()
|
self.camPage.Refresh()
|
||||||
|
@ -296,14 +295,14 @@ class printWindow(wx.Frame):
|
||||||
dc.DrawBitmap(self.cam.getLastImage(), 0, 0)
|
dc.DrawBitmap(self.cam.getLastImage(), 0, 0)
|
||||||
|
|
||||||
def UpdateButtonStates(self):
|
def UpdateButtonStates(self):
|
||||||
self.connectButton.Enable(not self.machineConnected)
|
self.connectButton.Enable(self.machineCom == None or self.machineCom.isClosedOrError())
|
||||||
#self.loadButton.Enable(self.printIdx == None)
|
#self.loadButton.Enable(self.printIdx == None)
|
||||||
self.printButton.Enable(self.machineConnected and self.gcodeList != None and self.printIdx == None)
|
self.printButton.Enable(self.machineCom != None and self.machineCom.isOperational() and not self.machineCom.isPrinting())
|
||||||
self.pauseButton.Enable(self.printIdx != None)
|
self.pauseButton.Enable(self.machineCom != None and self.machineCom.isPrinting())
|
||||||
self.cancelButton.Enable(self.printIdx != None)
|
self.cancelButton.Enable(self.machineCom != None and self.machineCom.isPrinting())
|
||||||
self.temperatureSelect.Enable(self.machineConnected)
|
self.temperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
|
||||||
self.bedTemperatureSelect.Enable(self.machineConnected)
|
self.bedTemperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
|
||||||
self.directControlPanel.Enable(self.machineConnected)
|
self.directControlPanel.Enable(self.machineCom != None and self.machineCom.isOperational())
|
||||||
|
|
||||||
def UpdateProgress(self):
|
def UpdateProgress(self):
|
||||||
status = ""
|
status = ""
|
||||||
|
@ -315,68 +314,62 @@ class printWindow(wx.Frame):
|
||||||
if cost != False:
|
if cost != False:
|
||||||
status += "Filament cost: %s\n" % (cost)
|
status += "Filament cost: %s\n" % (cost)
|
||||||
status += "Print time: %02d:%02d\n" % (int(self.gcode.totalMoveTimeMinute / 60), int(self.gcode.totalMoveTimeMinute % 60))
|
status += "Print time: %02d:%02d\n" % (int(self.gcode.totalMoveTimeMinute / 60), int(self.gcode.totalMoveTimeMinute % 60))
|
||||||
if self.printIdx == None:
|
if self.machineCom == None or not self.machineCom.isPrinting():
|
||||||
self.progress.SetValue(0)
|
self.progress.SetValue(0)
|
||||||
if self.gcodeList != None:
|
if self.gcodeList != None:
|
||||||
status += 'Line: -/%d\n' % (len(self.gcodeList))
|
status += 'Line: -/%d\n' % (len(self.gcodeList))
|
||||||
else:
|
else:
|
||||||
status += 'Line: %d/%d\n' % (self.printIdx, len(self.gcodeList))
|
status += 'Line: %d/%d\n' % (self.machineCom.getPrintPos(), len(self.gcodeList))
|
||||||
status += 'Height: %f\n' % (self.currentZ)
|
status += 'Height: %f\n' % (self.currentZ)
|
||||||
self.progress.SetValue(self.printIdx)
|
self.progress.SetValue(self.machineCom.getPrintPos())
|
||||||
if self.temp != None:
|
if self.machineCom != None:
|
||||||
status += 'Temp: %d\n' % (self.temp)
|
if self.machineCom.getTemp() > 0:
|
||||||
if self.bedTemp != None and self.bedTemp > 0:
|
status += 'Temp: %d\n' % (self.machineCom.getTemp())
|
||||||
status += 'Bed Temp: %d\n' % (self.bedTemp)
|
if self.machineCom.getBedTemp() > 0:
|
||||||
self.bedTemperatureLabel.Show(True)
|
status += 'Bed Temp: %d\n' % (self.machineCom.getBedTemp())
|
||||||
self.bedTemperatureSelect.Show(True)
|
self.bedTemperatureLabel.Show(True)
|
||||||
self.temperaturePanel.Layout()
|
self.bedTemperatureSelect.Show(True)
|
||||||
|
self.temperaturePanel.Layout()
|
||||||
|
status += 'Machine state: %s\n' % (self.machineCom.getStateString())
|
||||||
|
|
||||||
self.statsText.SetLabel(status.strip())
|
self.statsText.SetLabel(status.strip())
|
||||||
#self.Layout()
|
|
||||||
|
|
||||||
def OnConnect(self, e):
|
def OnConnect(self, e):
|
||||||
if self.machineCom != None:
|
if self.machineCom != None:
|
||||||
self.machineCom.close()
|
self.machineCom.close()
|
||||||
self.thread.join()
|
self.machineCom = machineCom.MachineCom(callbackObject=self)
|
||||||
self.machineCom = machineCom.MachineCom()
|
|
||||||
self.thread = threading.Thread(target=self.PrinterMonitor)
|
|
||||||
self.thread.start()
|
|
||||||
self.UpdateButtonStates()
|
self.UpdateButtonStates()
|
||||||
|
|
||||||
def OnLoad(self, e):
|
def OnLoad(self, e):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def OnPrint(self, e):
|
def OnPrint(self, e):
|
||||||
if not self.machineConnected:
|
if self.machineCom == None or not self.machineCom.isOperational():
|
||||||
return
|
return
|
||||||
if self.gcodeList == None:
|
if self.gcodeList == None:
|
||||||
return
|
return
|
||||||
if self.printIdx != None:
|
if self.machineCom.isPrinting():
|
||||||
return
|
return
|
||||||
self.currentZ = -1
|
self.currentZ = -1
|
||||||
if self.cam != None:
|
if self.cam != None:
|
||||||
self.cam.startTimelaps(self.filename[: self.filename.rfind('.')] + ".mpg")
|
self.cam.startTimelaps(self.filename[: self.filename.rfind('.')] + ".mpg")
|
||||||
self.printIdx = 1
|
self.machineCom.printGCode(self.gcodeList)
|
||||||
self.sendLine(0)
|
|
||||||
self.sendCnt = self.bufferLineCount
|
|
||||||
self.UpdateButtonStates()
|
self.UpdateButtonStates()
|
||||||
|
|
||||||
def OnCancel(self, e):
|
def OnCancel(self, e):
|
||||||
if self.cam != None:
|
if self.cam != None:
|
||||||
self.cam.endTimelaps()
|
self.cam.endTimelaps()
|
||||||
self.printIdx = None
|
|
||||||
self.pause = False
|
|
||||||
self.pauseButton.SetLabel('Pause')
|
self.pauseButton.SetLabel('Pause')
|
||||||
self.sendCommand("M84")
|
self.machineCom.cancelPrint()
|
||||||
|
self.machineCom.sendCommand("M84")
|
||||||
self.UpdateButtonStates()
|
self.UpdateButtonStates()
|
||||||
|
|
||||||
def OnPause(self, e):
|
def OnPause(self, e):
|
||||||
if self.pause:
|
if self.machineCom.isPaused():
|
||||||
self.pause = False
|
self.machineCom.setPause(False)
|
||||||
self.sendLine(self.printIdx)
|
|
||||||
self.printIdx += 1
|
|
||||||
self.pauseButton.SetLabel('Pause')
|
self.pauseButton.SetLabel('Pause')
|
||||||
else:
|
else:
|
||||||
self.pause = True
|
self.machineCom.setPause(True)
|
||||||
self.pauseButton.SetLabel('Resume')
|
self.pauseButton.SetLabel('Resume')
|
||||||
|
|
||||||
def OnClose(self, e):
|
def OnClose(self, e):
|
||||||
|
@ -384,14 +377,13 @@ class printWindow(wx.Frame):
|
||||||
printWindowHandle = None
|
printWindowHandle = None
|
||||||
if self.machineCom != None:
|
if self.machineCom != None:
|
||||||
self.machineCom.close()
|
self.machineCom.close()
|
||||||
self.thread.join()
|
|
||||||
self.Destroy()
|
self.Destroy()
|
||||||
|
|
||||||
def OnTempChange(self, e):
|
def OnTempChange(self, e):
|
||||||
self.sendCommand("M104 S%d" % (self.temperatureSelect.GetValue()))
|
self.machineCom.sendCommand("M104 S%d" % (self.temperatureSelect.GetValue()))
|
||||||
|
|
||||||
def OnBedTempChange(self, e):
|
def OnBedTempChange(self, e):
|
||||||
self.sendCommand("M140 S%d" % (self.bedTemperatureSelect.GetValue()))
|
self.machineCom.sendCommand("M140 S%d" % (self.bedTemperatureSelect.GetValue()))
|
||||||
|
|
||||||
def OnSpeedChange(self, e):
|
def OnSpeedChange(self, e):
|
||||||
self.feedrateRatioOuterWall = self.outerWallSpeedSelect.GetValue() / 100.0
|
self.feedrateRatioOuterWall = self.outerWallSpeedSelect.GetValue() / 100.0
|
||||||
|
@ -407,7 +399,7 @@ class printWindow(wx.Frame):
|
||||||
if line == '':
|
if line == '':
|
||||||
return
|
return
|
||||||
self.termLog.AppendText('>%s\n' % (line))
|
self.termLog.AppendText('>%s\n' % (line))
|
||||||
self.sendCommand(line)
|
self.machineCom.sendCommand(line)
|
||||||
self.termHistory.append(line)
|
self.termHistory.append(line)
|
||||||
self.termHistoryIdx = len(self.termHistory)
|
self.termHistoryIdx = len(self.termHistory)
|
||||||
self.termInput.SetValue('')
|
self.termInput.SetValue('')
|
||||||
|
@ -427,7 +419,7 @@ class printWindow(wx.Frame):
|
||||||
e.Skip()
|
e.Skip()
|
||||||
|
|
||||||
def LoadGCodeFile(self, filename):
|
def LoadGCodeFile(self, filename):
|
||||||
if self.printIdx != None:
|
if self.machineCom != None and self.machineCom.isPrinting():
|
||||||
return
|
return
|
||||||
#Send an initial M110 to reset the line counter to zero.
|
#Send an initial M110 to reset the line counter to zero.
|
||||||
lineType = 'CUSTOM'
|
lineType = 'CUSTOM'
|
||||||
|
@ -454,13 +446,6 @@ class printWindow(wx.Frame):
|
||||||
wx.CallAfter(self.UpdateButtonStates)
|
wx.CallAfter(self.UpdateButtonStates)
|
||||||
wx.CallAfter(self.UpdateProgress)
|
wx.CallAfter(self.UpdateProgress)
|
||||||
|
|
||||||
def sendCommand(self, cmd):
|
|
||||||
if self.machineConnected:
|
|
||||||
if self.printIdx == None or self.pause:
|
|
||||||
self.machineCom.sendCommand(cmd)
|
|
||||||
else:
|
|
||||||
self.sendList.append(cmd)
|
|
||||||
|
|
||||||
def sendLine(self, lineNr):
|
def sendLine(self, lineNr):
|
||||||
if lineNr >= len(self.gcodeList):
|
if lineNr >= len(self.gcodeList):
|
||||||
return False
|
return False
|
||||||
|
@ -495,58 +480,22 @@ class printWindow(wx.Frame):
|
||||||
self.machineCom.sendCommand("N%d%s*%d" % (lineNr, line, checksum))
|
self.machineCom.sendCommand("N%d%s*%d" % (lineNr, line, checksum))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def PrinterMonitor(self):
|
def mcLog(self, message):
|
||||||
while True:
|
#print message
|
||||||
line = self.machineCom.readline()
|
pass
|
||||||
if line == None:
|
|
||||||
self.machineConnected = False
|
def mcTempUpdate(self, temp, bedTemp):
|
||||||
wx.CallAfter(self.UpdateButtonStates)
|
self.temperatureGraph.addPoint(temp, self.temperatureSelect.GetValue(), bedTemp, self.bedTemperatureSelect.GetValue())
|
||||||
return
|
|
||||||
if line == '': #When we have a communication "timeout" and we're not sending gcode, then read the temperature.
|
def mcStateChange(self, state):
|
||||||
if self.printIdx == None or self.pause:
|
wx.CallAfter(self.UpdateButtonStates)
|
||||||
self.machineCom.sendCommand("M105")
|
wx.CallAfter(self.UpdateProgress)
|
||||||
else:
|
|
||||||
wx.CallAfter(self.AddTermLog, '!!Comm timeout, forcing next line!!\n')
|
def mcMessage(self, message):
|
||||||
line = 'ok'
|
wx.CallAfter(self.AddTermLog, message)
|
||||||
if self.machineConnected:
|
|
||||||
while self.sendCnt > 0 and not self.pause:
|
def mcProgress(self, lineNr):
|
||||||
self.sendLine(self.printIdx)
|
wx.CallAfter(self.UpdateProgress)
|
||||||
self.printIdx += 1
|
|
||||||
self.sendCnt -= 1
|
|
||||||
if line.startswith("start"):
|
|
||||||
self.machineConnected = True
|
|
||||||
wx.CallAfter(self.UpdateButtonStates)
|
|
||||||
elif 'T:' in line:
|
|
||||||
self.temp = float(re.search("[0-9\.]*", line.split('T:')[1]).group(0))
|
|
||||||
if 'B:' in line:
|
|
||||||
self.bedTemp = float(re.search("[0-9\.]*", line.split('B:')[1]).group(0))
|
|
||||||
self.temperatureGraph.addPoint(self.temp, self.temperatureSelect.GetValue(), self.bedTemp, self.bedTemperatureSelect.GetValue())
|
|
||||||
wx.CallAfter(self.UpdateProgress)
|
|
||||||
elif line.strip() != 'ok':
|
|
||||||
wx.CallAfter(self.AddTermLog, line)
|
|
||||||
if self.printIdx != None:
|
|
||||||
if line.startswith("ok"):
|
|
||||||
if len(self.sendList) > 0:
|
|
||||||
self.machineCom.sendCommand(self.sendList.pop(0))
|
|
||||||
elif self.pause:
|
|
||||||
self.sendCnt += 1
|
|
||||||
else:
|
|
||||||
if self.sendLine(self.printIdx):
|
|
||||||
self.printIdx += 1
|
|
||||||
else:
|
|
||||||
if self.cam != None:
|
|
||||||
self.cam.endTimelaps()
|
|
||||||
self.printIdx = None
|
|
||||||
wx.CallAfter(self.UpdateButtonStates)
|
|
||||||
wx.CallAfter(self.UpdateProgress)
|
|
||||||
elif "resend" in line.lower() or "rs" in line:
|
|
||||||
try:
|
|
||||||
lineNr=int(line.replace("N:"," ").replace("N"," ").replace(":"," ").split()[-1])
|
|
||||||
except:
|
|
||||||
if "rs" in line:
|
|
||||||
lineNr=int(line.split()[1])
|
|
||||||
self.printIdx = lineNr
|
|
||||||
#we should actually resend the line here, but we also get an "ok" for each error from Marlin. And thus we'll resend on the OK.
|
|
||||||
|
|
||||||
class temperatureGraph(wx.Panel):
|
class temperatureGraph(wx.Panel):
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
import __init__
|
import __init__
|
||||||
|
|
||||||
import os, glob, sys, time, math, traceback
|
import os, glob, sys, time, math, re, traceback, threading
|
||||||
|
import Queue as queue
|
||||||
|
|
||||||
from serial import Serial
|
from serial import Serial
|
||||||
|
|
||||||
|
@ -84,9 +85,33 @@ class VirtualPrinter():
|
||||||
def close(self):
|
def close(self):
|
||||||
self.readList = None
|
self.readList = None
|
||||||
|
|
||||||
class MachineCom():
|
class MachineComPrintCallback(object):
|
||||||
def __init__(self, port = None, baudrate = None, logCallback = None):
|
def mcLog(self, message):
|
||||||
self._logCallback = logCallback
|
print(message)
|
||||||
|
|
||||||
|
def mcTempUpdate(self, temp, bedTemp):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def mcStateChange(self, state):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def mcMessage(self, message):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def mcProgress(self, lineNr):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class MachineCom(object):
|
||||||
|
STATE_NONE = 0
|
||||||
|
STATE_DETECT_BAUDRATE = 1
|
||||||
|
STATE_CONNECTING = 2
|
||||||
|
STATE_OPERATIONAL = 3
|
||||||
|
STATE_PRINTING = 4
|
||||||
|
STATE_CLOSED = 5
|
||||||
|
STATE_ERROR = 6
|
||||||
|
STATE_CLOSED_WITH_ERROR = 7
|
||||||
|
|
||||||
|
def __init__(self, port = None, baudrate = None, callbackObject = None):
|
||||||
if port == None:
|
if port == None:
|
||||||
port = profile.getPreference('serial_port')
|
port = profile.getPreference('serial_port')
|
||||||
if baudrate == None:
|
if baudrate == None:
|
||||||
|
@ -94,7 +119,21 @@ class MachineCom():
|
||||||
baudrate = 0
|
baudrate = 0
|
||||||
else:
|
else:
|
||||||
baudrate = int(profile.getPreference('serial_baud'))
|
baudrate = int(profile.getPreference('serial_baud'))
|
||||||
|
if callbackObject == None:
|
||||||
|
callbackObject = MachineComPrintCallback()
|
||||||
|
|
||||||
|
self._callback = callbackObject
|
||||||
|
self._state = self.STATE_NONE
|
||||||
self._serial = None
|
self._serial = None
|
||||||
|
self._baudrateDetectList = baudrateList()
|
||||||
|
self._baudrateDetectRetry = 0
|
||||||
|
self._temp = 0
|
||||||
|
self._bedTemp = 0
|
||||||
|
self._gcodeList = None
|
||||||
|
self._gcodePos = 0
|
||||||
|
self._commandQueue = queue.Queue()
|
||||||
|
self._logQueue = queue.Queue(256)
|
||||||
|
|
||||||
if port == 'AUTO':
|
if port == 'AUTO':
|
||||||
programmer = stk500v2.Stk500v2()
|
programmer = stk500v2.Stk500v2()
|
||||||
self._log("Serial port list: %s" % (str(serialList())))
|
self._log("Serial port list: %s" % (str(serialList())))
|
||||||
|
@ -103,83 +142,200 @@ class MachineCom():
|
||||||
self._log("Connecting to: %s" % (port))
|
self._log("Connecting to: %s" % (port))
|
||||||
programmer.connect(port)
|
programmer.connect(port)
|
||||||
self._serial = programmer.leaveISP()
|
self._serial = programmer.leaveISP()
|
||||||
self._configureSerialWithBaudrate(baudrate)
|
|
||||||
break
|
break
|
||||||
except ispBase.IspError as (e):
|
except ispBase.IspError as (e):
|
||||||
self._log("Error while connecting to %s: %s" % (port, str(e)))
|
self._log("Error while connecting to %s: %s" % (port, str(e)))
|
||||||
pass
|
pass
|
||||||
except:
|
except:
|
||||||
self._log("Unexpected error while connecting to serial port: %s %s" % (port, getExceptionString()))
|
self._log("Unexpected error while connecting to serial port: %s %s" % (port, getExceptionString()))
|
||||||
programmer.close()
|
programmer.close()
|
||||||
elif port == 'VIRTUAL':
|
elif port == 'VIRTUAL':
|
||||||
self._serial = VirtualPrinter()
|
self._serial = VirtualPrinter()
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
self._serial = Serial(port, 115200, timeout=2)
|
self._log("Connecting to: %s" % (port))
|
||||||
self._configureSerialWithBaudrate(baudrate)
|
if baudrate == 0:
|
||||||
|
self._serial = Serial(port, 115200, timeout=0.1)
|
||||||
|
else:
|
||||||
|
self._serial = Serial(port, baudrate, timeout=2)
|
||||||
except:
|
except:
|
||||||
self._log("Unexpected error while connecting to serial port: %s %s" % (port, getExceptionString()))
|
self._log("Unexpected error while connecting to serial port: %s %s" % (port, getExceptionString()))
|
||||||
print
|
self._log("Connected to: %s, starting monitor" % (self._serial))
|
||||||
print self._serial
|
if baudrate == 0:
|
||||||
|
self._changeState(self.STATE_DETECT_BAUDRATE)
|
||||||
|
else:
|
||||||
|
self._changeState(self.STATE_CONNECTING)
|
||||||
|
self.thread = threading.Thread(target=self._monitor)
|
||||||
|
self.thread.daemon = True
|
||||||
|
self.thread.start()
|
||||||
|
|
||||||
def _configureSerialWithBaudrate(self, baudrate):
|
def _changeState(self, newState):
|
||||||
if baudrate != 0:
|
if self._state == newState:
|
||||||
self._serial.baudrate = baudrate
|
|
||||||
return
|
return
|
||||||
for baudrate in baudrateList():
|
oldState = self.getStateString()
|
||||||
try:
|
self._state = newState
|
||||||
self._serial.baudrate = baudrate
|
self._log('Changing monitoring state from \'%s\' to \'%s\'' % (oldState, self.getStateString()))
|
||||||
self._log("Trying baudrate: %d" % (baudrate))
|
self._callback.mcStateChange(newState)
|
||||||
except:
|
|
||||||
self._log("Unexpected error while setting baudrate: %d %s" % (baudrate, getExceptionString()))
|
def getState(self):
|
||||||
continue
|
return self._state
|
||||||
time.sleep(0.5)
|
|
||||||
starttime = time.time()
|
def getStateString(self):
|
||||||
self.sendCommand("\nM105")
|
if self._state == self.STATE_NONE:
|
||||||
for line in self._serial:
|
return "Offline"
|
||||||
self._log("Recv: %s" % (unicode(line, 'utf-8', 'replace').rstrip()))
|
if self._state == self.STATE_DETECT_BAUDRATE:
|
||||||
if 'start' in line:
|
return "Detect baudrate"
|
||||||
return
|
if self._state == self.STATE_CONNECTING:
|
||||||
|
return "Connecting"
|
||||||
|
if self._state == self.STATE_OPERATIONAL:
|
||||||
|
return "Operational"
|
||||||
|
if self._state == self.STATE_PRINTING:
|
||||||
|
return "Printing"
|
||||||
|
if self._state == self.STATE_CLOSED:
|
||||||
|
return "Closed"
|
||||||
|
if self._state == self.STATE_ERROR:
|
||||||
|
return "Error: %s" % (self._errorValue)
|
||||||
|
if self._state == self.STATE_CLOSED_WITH_ERROR:
|
||||||
|
return "Error: %s" % (self._errorValue)
|
||||||
|
return "?%d?" % (self._state)
|
||||||
|
|
||||||
|
def isClosedOrError(self):
|
||||||
|
return self._state == self.STATE_ERROR or self._state == self.STATE_CLOSED_WITH_ERROR or self._state == self.STATE_CLOSED
|
||||||
|
|
||||||
|
def isOperational(self):
|
||||||
|
return self._state == self.STATE_OPERATIONAL or self._state == self.STATE_PRINTING
|
||||||
|
|
||||||
|
def isPrinting(self):
|
||||||
|
return self._state == self.STATE_PRINTING
|
||||||
|
|
||||||
|
def getPrintPos(self):
|
||||||
|
return self._gcodePos
|
||||||
|
|
||||||
|
def isPaused(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def getTemp(self):
|
||||||
|
return self._temp
|
||||||
|
|
||||||
|
def getBedTemp(self):
|
||||||
|
return self._bedTemp
|
||||||
|
|
||||||
|
def _monitor(self):
|
||||||
|
self._timeoutTime = time.time() + 5
|
||||||
|
while True:
|
||||||
|
line = self._readline()
|
||||||
|
if line == None:
|
||||||
|
break
|
||||||
|
|
||||||
|
#No matter the state, if we see an error, goto the error state and store the error for reference.
|
||||||
|
if line.startswith('Error: '):
|
||||||
|
#Oh YEAH, consistency.
|
||||||
|
# Marlin reports an MIN/MAX temp error as "Error: x\n: Extruder switched off. MAXTEMP triggered !\n"
|
||||||
|
# But a bed temp error is reported as "Error: Temperature heated bed switched off. MAXTEMP triggered !!"
|
||||||
|
# So we can have an extra newline in the most common case. Awesome work people.
|
||||||
|
if re.match('Error: [0-9]\n', line):
|
||||||
|
line = line.rstrip() + self._readline()
|
||||||
|
self._errorValue = line
|
||||||
|
self._changeState(self.STATE_ERROR)
|
||||||
|
if 'T:' in line:
|
||||||
|
self._temp = float(re.search("[0-9\.]*", line.split('T:')[1]).group(0))
|
||||||
|
if 'B:' in line:
|
||||||
|
self._bedTemp = float(re.search("[0-9\.]*", line.split('B:')[1]).group(0))
|
||||||
|
self._callback.mcTempUpdate(self._temp, self._bedTemp)
|
||||||
|
elif line.strip() != 'ok':
|
||||||
|
self._callback.mcMessage(line)
|
||||||
|
|
||||||
|
if self._state == self.STATE_DETECT_BAUDRATE:
|
||||||
|
if line == '' or time.time() > self._timeoutTime:
|
||||||
|
if len(self._baudrateDetectList) < 1:
|
||||||
|
self._log("No more baudrates to test, and no suitable baudrate found.")
|
||||||
|
self.close()
|
||||||
|
elif self._baudrateDetectRetry > 0:
|
||||||
|
self._baudrateDetectRetry -= 1
|
||||||
|
self._serial.write('\n')
|
||||||
|
self._sendCommand("M105")
|
||||||
|
else:
|
||||||
|
baudrate = self._baudrateDetectList.pop(0)
|
||||||
|
try:
|
||||||
|
self._serial.baudrate = baudrate
|
||||||
|
self._serial.timeout = 0.5
|
||||||
|
self._log("Trying baudrate: %d" % (baudrate))
|
||||||
|
self._baudrateDetectRetry = 5
|
||||||
|
self._timeoutTime = time.time() + 5
|
||||||
|
self._serial.write('\n')
|
||||||
|
self._sendCommand("M105")
|
||||||
|
except:
|
||||||
|
self._log("Unexpected error while setting baudrate: %d %s" % (baudrate, getExceptionString()))
|
||||||
|
elif 'ok' in line:
|
||||||
|
self._serial.timeout = 2
|
||||||
|
self._changeState(self.STATE_OPERATIONAL)
|
||||||
|
elif self._state == self.STATE_CONNECTING:
|
||||||
|
if line == '':
|
||||||
|
self._sendCommand("M105")
|
||||||
|
elif 'ok' in line:
|
||||||
|
self._changeState(self.STATE_OPERATIONAL)
|
||||||
|
if time.time() > self._timeoutTime:
|
||||||
|
self.close()
|
||||||
|
elif self._state == self.STATE_OPERATIONAL:
|
||||||
|
#Request the temperature on comm timeout (every 2 seconds) when we are not printing.
|
||||||
|
if line == '':
|
||||||
|
self._sendCommand("M105")
|
||||||
|
elif self._state == self.STATE_PRINTING:
|
||||||
|
if line == '' and time.time() > self._timeoutTime:
|
||||||
|
self._log("Communication timeout during printing, forcing a line")
|
||||||
|
line = 'ok'
|
||||||
if 'ok' in line:
|
if 'ok' in line:
|
||||||
return
|
self._timeoutTime = time.time() + 5
|
||||||
#Timeout in case we get a lot of crap data from some random device.
|
if not self._commandQueue.empty():
|
||||||
if starttime - time.time() > 5:
|
self._sendCommand(self._commandQueue.get())
|
||||||
break
|
else:
|
||||||
self._serial.close()
|
self._sendNext()
|
||||||
self._serial = None
|
elif "resend" in line.lower() or "rs" in line:
|
||||||
|
try:
|
||||||
|
self._gcodePos = int(line.replace("N:"," ").replace("N"," ").replace(":"," ").split()[-1])
|
||||||
|
except:
|
||||||
|
if "rs" in line:
|
||||||
|
self._gcodePos = int(line.split()[1])
|
||||||
|
self._log("Connection closed, closing down monitor")
|
||||||
|
|
||||||
def _log(self, message):
|
def _log(self, message):
|
||||||
if self._logCallback != None:
|
self._callback.mcLog(message)
|
||||||
self._logCallback(message)
|
try:
|
||||||
else:
|
self._logQueue.put(message, False)
|
||||||
print(message)
|
except:
|
||||||
|
#If the log queue is full, remove the first message and append the new message again
|
||||||
|
self._logQueue.get()
|
||||||
|
self._logQueue.put(message, False)
|
||||||
|
|
||||||
def readline(self):
|
def _readline(self):
|
||||||
if self._serial == None:
|
if self._serial == None:
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
ret = self._serial.readline()
|
ret = self._serial.readline()
|
||||||
except:
|
except:
|
||||||
self._log("Unexpected error while reading serial port: %s" % (getExceptionString()))
|
self._log("Unexpected error while reading serial port: %s" % (getExceptionString()))
|
||||||
|
self._errorValue = getExceptionString()
|
||||||
|
self.close(True)
|
||||||
|
return None
|
||||||
|
if ret == '':
|
||||||
|
#self._log("Recv: TIMEOUT")
|
||||||
return ''
|
return ''
|
||||||
if ret != '':
|
self._log("Recv: %s" % (unicode(ret, 'ascii', 'replace').rstrip()))
|
||||||
self._log("Recv: %s" % (unicode(ret, 'utf-8', 'replace').rstrip()))
|
|
||||||
else:
|
|
||||||
self._log("Recv: TIMEOUT")
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def close(self):
|
def close(self, isError = False):
|
||||||
if self._serial != None:
|
if self._serial != None:
|
||||||
self._serial.close()
|
self._serial.close()
|
||||||
|
if isError:
|
||||||
|
self._changeState(self.STATE_CLOSED_WITH_ERROR)
|
||||||
|
else:
|
||||||
|
self._changeState(self.STATE_CLOSED)
|
||||||
self._serial = None
|
self._serial = None
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def isOpen(self):
|
def _sendCommand(self, cmd):
|
||||||
return self._serial != None
|
|
||||||
|
|
||||||
def sendCommand(self, cmd):
|
|
||||||
if self._serial == None:
|
if self._serial == None:
|
||||||
return
|
return
|
||||||
self._log('Send: %s' % (cmd))
|
self._log('Send: %s' % (cmd))
|
||||||
|
@ -188,6 +344,38 @@ class MachineCom():
|
||||||
self._serial.write('\n')
|
self._serial.write('\n')
|
||||||
except:
|
except:
|
||||||
self._log("Unexpected error while writing serial port: %s" % (getExceptionString()))
|
self._log("Unexpected error while writing serial port: %s" % (getExceptionString()))
|
||||||
|
self._errorValue = getExceptionString()
|
||||||
|
self.close(True)
|
||||||
|
|
||||||
|
def _sendNext(self):
|
||||||
|
if self._gcodePos >= len(self._gcodeList):
|
||||||
|
self._changeState(self.STATE_OPERATIONAL)
|
||||||
|
return
|
||||||
|
line = self._gcodeList[self._gcodePos]
|
||||||
|
checksum = reduce(lambda x,y:x^y, map(ord, "N%d%s" % (self._gcodePos, line)))
|
||||||
|
self._sendCommand("N%d%s*%d" % (self._gcodePos, line, checksum))
|
||||||
|
self._gcodePos += 1
|
||||||
|
self._callback.mcProgress(self._gcodePos)
|
||||||
|
|
||||||
|
def sendCommand(self, cmd):
|
||||||
|
cmd = cmd.encode('ascii', 'replace')
|
||||||
|
if self.isPrinting():
|
||||||
|
self._commandQueue.put(cmd)
|
||||||
|
elif self.isOperational():
|
||||||
|
self._sendCommand(cmd)
|
||||||
|
|
||||||
|
def printGCode(self, gcodeList):
|
||||||
|
if not self.isOperational() or self.isPrinting():
|
||||||
|
return
|
||||||
|
self._gcodeList = gcodeList
|
||||||
|
self._gcodePos = 0
|
||||||
|
self._changeState(self.STATE_PRINTING)
|
||||||
|
for i in xrange(0, 6):
|
||||||
|
self._sendNext()
|
||||||
|
|
||||||
|
def cancelPrint(self):
|
||||||
|
if self.isOperational():
|
||||||
|
self._changeState(self.STATE_OPERATIONAL)
|
||||||
|
|
||||||
def getExceptionString():
|
def getExceptionString():
|
||||||
locationInfo = traceback.extract_tb(sys.exc_info()[2])[0]
|
locationInfo = traceback.extract_tb(sys.exc_info()[2])[0]
|
||||||
|
|
Loading…
Reference in New Issue