2012-03-06 16:48:56 +00:00
from __future__ import absolute_import
2012-12-06 13:51:55 +00:00
import os
import glob
import sys
import time
import math
import re
import traceback
import threading
2012-09-06 14:52:05 +00:00
import Queue as queue
2013-03-16 01:24:33 +00:00
import logging
2012-03-06 16:48:56 +00:00
2012-10-04 12:05:09 +00:00
import serial
2012-03-06 16:48:56 +00:00
2013-01-18 22:23:50 +00:00
from octoprint . util . avr_isp import stk500v2
from octoprint . util . avr_isp import ispBase
2012-03-06 16:48:56 +00:00
2013-01-18 22:23:50 +00:00
from octoprint . settings import settings
2012-03-19 10:05:40 +00:00
2012-03-06 16:48:56 +00:00
try :
import _winreg
except :
pass
2012-12-31 12:18:54 +00:00
def isDevVersion ( ) :
gitPath = os . path . abspath ( os . path . join ( os . path . split ( os . path . abspath ( __file__ ) ) [ 0 ] , " ../../.git " ) )
return os . path . exists ( gitPath )
2012-03-06 16:48:56 +00:00
def serialList ( ) :
2012-09-10 13:49:50 +00:00
baselist = [ ]
if os . name == " nt " :
try :
key = _winreg . OpenKey ( _winreg . HKEY_LOCAL_MACHINE , " HARDWARE \\ DEVICEMAP \\ SERIALCOMM " )
i = 0
while ( 1 ) :
baselist + = [ _winreg . EnumValue ( key , i ) [ 1 ] ]
i + = 1
except :
pass
2013-01-01 20:04:00 +00:00
baselist = baselist + glob . glob ( " /dev/ttyUSB* " ) + glob . glob ( " /dev/ttyACM* " ) + glob . glob ( " /dev/tty.usb* " ) + glob . glob ( " /dev/cu.* " ) + glob . glob ( " /dev/rfcomm* " )
2013-02-16 19:28:09 +00:00
prev = settings ( ) . get ( [ " serial " , " port " ] )
2012-09-10 13:49:50 +00:00
if prev in baselist :
baselist . remove ( prev )
baselist . insert ( 0 , prev )
2012-12-31 12:18:54 +00:00
if isDevVersion ( ) :
2013-01-01 20:04:00 +00:00
baselist . append ( " VIRTUAL " )
2012-09-10 13:49:50 +00:00
return baselist
2012-03-06 16:48:56 +00:00
2012-06-30 09:51:43 +00:00
def baudrateList ( ) :
2012-09-10 13:49:50 +00:00
ret = [ 250000 , 230400 , 115200 , 57600 , 38400 , 19200 , 9600 ]
2013-02-16 19:28:09 +00:00
prev = settings ( ) . getInt ( [ " serial " , " baudrate " ] )
2013-01-01 20:04:00 +00:00
if prev in ret :
ret . remove ( prev )
ret . insert ( 0 , prev )
2012-09-10 13:49:50 +00:00
return ret
2012-06-30 09:51:43 +00:00
2012-04-03 09:59:38 +00:00
class VirtualPrinter ( ) :
def __init__ ( self ) :
2012-07-19 12:27:59 +00:00
self . readList = [ ' start \n ' , ' Marlin: Virtual Marlin! \n ' , ' \x80 \n ' ]
2012-04-04 15:02:22 +00:00
self . temp = 0.0
self . targetTemp = 0.0
2012-07-10 15:04:37 +00:00
self . lastTempAt = time . time ( )
2012-06-26 10:05:06 +00:00
self . bedTemp = 1.0
self . bedTargetTemp = 1.0
2013-02-17 16:47:06 +00:00
2013-03-16 17:25:39 +00:00
self . currentLine = 0
2012-04-03 09:59:38 +00:00
def write ( self , data ) :
2012-12-17 13:15:40 +00:00
if self . readList is None :
2012-04-03 09:59:38 +00:00
return
2012-09-02 12:27:24 +00:00
#print "Send: %s" % (data.rstrip())
2012-06-26 10:05:06 +00:00
if ' M104 ' in data or ' M109 ' in data :
2012-04-04 15:02:22 +00:00
try :
2012-12-17 13:15:40 +00:00
self . targetTemp = float ( re . search ( ' S([0-9]+) ' , data ) . group ( 1 ) )
2012-04-04 15:02:22 +00:00
except :
pass
2012-06-26 10:05:06 +00:00
if ' M140 ' in data or ' M190 ' in data :
try :
2012-12-17 13:15:40 +00:00
self . bedTargetTemp = float ( re . search ( ' S([0-9]+) ' , data ) . group ( 1 ) )
2012-06-26 10:05:06 +00:00
except :
pass
2012-04-04 15:02:22 +00:00
if ' M105 ' in data :
2013-03-16 17:25:39 +00:00
# send simulated temperature data
2012-07-06 11:27:06 +00:00
self . readList . append ( " ok T: %.2f / %.2f B: %.2f / %.2f @:64 \n " % ( self . temp , self . targetTemp , self . bedTemp , self . bedTargetTemp ) )
2013-03-16 17:25:39 +00:00
elif " M110 " in data :
# reset current line
self . currentLine = int ( re . search ( ' N([0-9]+) ' , data ) . group ( 1 ) )
self . readList . append ( " ok \n " )
elif self . currentLine == 100 :
# simulate a resend at line 100 of the last 5 lines
2013-03-17 09:58:34 +00:00
self . readList . append ( " Error: Line Number is not Last Line Number \n " )
2013-03-16 17:25:39 +00:00
self . readList . append ( " rs %d \n " % ( self . currentLine - 5 ) )
2012-09-07 12:10:57 +00:00
elif len ( data . strip ( ) ) > 0 :
2012-04-04 15:02:22 +00:00
self . readList . append ( " ok \n " )
2013-03-17 09:58:34 +00:00
if " * " in data :
self . currentLine + = 1
2012-04-03 09:59:38 +00:00
def readline ( self ) :
2012-12-17 13:15:40 +00:00
if self . readList is None :
2012-04-03 09:59:38 +00:00
return ' '
2012-04-04 15:02:22 +00:00
n = 0
2012-07-10 15:04:37 +00:00
timeDiff = self . lastTempAt - time . time ( )
self . lastTempAt = time . time ( )
if abs ( self . temp - self . targetTemp ) > 1 :
2012-12-17 13:15:40 +00:00
self . temp + = math . copysign ( timeDiff * 10 , self . targetTemp - self . temp )
2012-12-25 10:50:38 +00:00
if self . temp < 0 :
self . temp = 0
2012-07-10 15:04:37 +00:00
if abs ( self . bedTemp - self . bedTargetTemp ) > 1 :
2012-12-17 13:15:40 +00:00
self . bedTemp + = math . copysign ( timeDiff * 10 , self . bedTargetTemp - self . bedTemp )
2012-12-25 10:50:38 +00:00
if self . bedTemp < 0 :
self . bedTemp = 0
2012-04-03 09:59:38 +00:00
while len ( self . readList ) < 1 :
time . sleep ( 0.1 )
2012-04-04 15:02:22 +00:00
n + = 1
if n == 20 :
return ' '
2012-12-17 13:15:40 +00:00
if self . readList is None :
2012-04-03 09:59:38 +00:00
return ' '
2012-10-01 12:36:47 +00:00
time . sleep ( 0.001 )
2012-09-02 12:27:24 +00:00
#print "Recv: %s" % (self.readList[0].rstrip())
2012-04-03 09:59:38 +00:00
return self . readList . pop ( 0 )
def close ( self ) :
self . readList = None
2012-09-06 14:52:05 +00:00
class MachineComPrintCallback ( object ) :
def mcLog ( self , message ) :
2012-10-02 14:06:47 +00:00
pass
2012-09-06 14:52:05 +00:00
2012-10-03 13:11:35 +00:00
def mcTempUpdate ( self , temp , bedTemp , targetTemp , bedTargetTemp ) :
2012-09-06 14:52:05 +00:00
pass
def mcStateChange ( self , state ) :
pass
def mcMessage ( self , message ) :
pass
def mcProgress ( self , lineNr ) :
pass
2012-09-07 12:10:57 +00:00
def mcZChange ( self , newZ ) :
pass
2012-09-06 14:52:05 +00:00
class MachineCom ( object ) :
STATE_NONE = 0
2012-10-02 08:58:57 +00:00
STATE_OPEN_SERIAL = 1
STATE_DETECT_SERIAL = 2
STATE_DETECT_BAUDRATE = 3
STATE_CONNECTING = 4
STATE_OPERATIONAL = 5
STATE_PRINTING = 6
STATE_PAUSED = 7
STATE_CLOSED = 8
STATE_ERROR = 9
STATE_CLOSED_WITH_ERROR = 10
2012-09-06 14:52:05 +00:00
def __init__ ( self , port = None , baudrate = None , callbackObject = None ) :
2013-03-16 01:24:33 +00:00
self . _logger = logging . getLogger ( __name__ )
self . _serialLogger = logging . getLogger ( " SERIAL " )
2012-03-19 10:05:40 +00:00
if port == None :
2013-02-16 19:28:09 +00:00
port = settings ( ) . get ( [ " serial " , " port " ] )
2012-03-19 10:05:40 +00:00
if baudrate == None :
2013-02-16 19:28:09 +00:00
settingsBaudrate = settings ( ) . getInt ( [ " serial " , " baudrate " ] )
2013-01-01 20:04:00 +00:00
if settingsBaudrate is None :
2012-06-30 09:51:43 +00:00
baudrate = 0
else :
2013-01-01 20:04:00 +00:00
baudrate = settingsBaudrate
2012-09-06 14:52:05 +00:00
if callbackObject == None :
callbackObject = MachineComPrintCallback ( )
2012-10-02 08:58:57 +00:00
self . _port = port
self . _baudrate = baudrate
2012-09-06 14:52:05 +00:00
self . _callback = callbackObject
self . _state = self . STATE_NONE
2012-09-05 14:10:47 +00:00
self . _serial = None
2012-09-06 14:52:05 +00:00
self . _baudrateDetectList = baudrateList ( )
self . _baudrateDetectRetry = 0
self . _temp = 0
self . _bedTemp = 0
2012-10-03 13:11:35 +00:00
self . _targetTemp = 0
self . _bedTargetTemp = 0
2012-09-06 14:52:05 +00:00
self . _gcodeList = None
self . _gcodePos = 0
self . _commandQueue = queue . Queue ( )
self . _logQueue = queue . Queue ( 256 )
2012-09-07 10:04:55 +00:00
self . _feedRateModifier = { }
2012-09-07 12:10:57 +00:00
self . _currentZ = - 1
2012-10-02 08:58:57 +00:00
self . _heatupWaitStartTime = 0
self . _heatupWaitTimeLost = 0.0
2012-10-04 13:01:54 +00:00
self . _printStartTime100 = None
2013-03-16 00:57:05 +00:00
self . _alwaysSendChecksum = settings ( ) . getBoolean ( [ " feature " , " alwaysSendChecksum " ] )
2013-03-16 17:25:39 +00:00
self . _currentLine = 1
self . _resendDelta = None
self . _lastLines = [ ]
2013-03-16 00:57:05 +00:00
2012-09-06 14:52:05 +00:00
self . thread = threading . Thread ( target = self . _monitor )
self . thread . daemon = True
self . thread . start ( )
2012-06-30 09:51:43 +00:00
2012-09-06 14:52:05 +00:00
def _changeState ( self , newState ) :
if self . _state == newState :
2012-09-05 14:10:47 +00:00
return
2012-09-06 14:52:05 +00:00
oldState = self . getStateString ( )
self . _state = newState
self . _log ( ' Changing monitoring state from \' %s \' to \' %s \' ' % ( oldState , self . getStateString ( ) ) )
self . _callback . mcStateChange ( newState )
def getState ( self ) :
return self . _state
def getStateString ( self ) :
if self . _state == self . STATE_NONE :
return " Offline "
2012-10-02 08:58:57 +00:00
if self . _state == self . STATE_OPEN_SERIAL :
return " Opening serial port "
if self . _state == self . STATE_DETECT_SERIAL :
return " Detecting serial port "
2012-09-06 14:52:05 +00:00
if self . _state == self . STATE_DETECT_BAUDRATE :
2012-10-02 08:58:57 +00:00
return " Detecting baudrate "
2012-09-06 14:52:05 +00:00
if self . _state == self . STATE_CONNECTING :
return " Connecting "
if self . _state == self . STATE_OPERATIONAL :
return " Operational "
if self . _state == self . STATE_PRINTING :
return " Printing "
2012-09-06 15:24:34 +00:00
if self . _state == self . STATE_PAUSED :
return " Paused "
2012-09-06 14:52:05 +00:00
if self . _state == self . STATE_CLOSED :
return " Closed "
if self . _state == self . STATE_ERROR :
2012-10-04 12:05:09 +00:00
return " Error: %s " % ( self . getShortErrorString ( ) )
2012-09-06 14:52:05 +00:00
if self . _state == self . STATE_CLOSED_WITH_ERROR :
2012-10-04 12:05:09 +00:00
return " Error: %s " % ( self . getShortErrorString ( ) )
2012-09-06 14:52:05 +00:00
return " ? %d ? " % ( self . _state )
2012-10-04 12:05:09 +00:00
def getShortErrorString ( self ) :
if len ( self . _errorValue ) < 20 :
return self . _errorValue
return self . _errorValue [ : 20 ] + " ... "
2012-12-04 07:56:15 +00:00
def getErrorString ( self ) :
return self . _errorValue
2012-10-04 12:05:09 +00:00
2012-09-06 14:52:05 +00:00
def isClosedOrError ( self ) :
return self . _state == self . STATE_ERROR or self . _state == self . STATE_CLOSED_WITH_ERROR or self . _state == self . STATE_CLOSED
2012-10-01 11:47:16 +00:00
def isError ( self ) :
return self . _state == self . STATE_ERROR or self . _state == self . STATE_CLOSED_WITH_ERROR
2012-09-06 14:52:05 +00:00
def isOperational ( self ) :
2012-09-06 15:24:34 +00:00
return self . _state == self . STATE_OPERATIONAL or self . _state == self . STATE_PRINTING or self . _state == self . STATE_PAUSED
2012-09-06 14:52:05 +00:00
def isPrinting ( self ) :
return self . _state == self . STATE_PRINTING
2012-10-02 14:06:47 +00:00
def isPaused ( self ) :
return self . _state == self . STATE_PAUSED
2012-09-06 14:52:05 +00:00
def getPrintPos ( self ) :
return self . _gcodePos
2012-10-01 12:51:59 +00:00
def getPrintTime ( self ) :
2013-01-04 10:08:19 +00:00
if self . _printStartTime100 == None :
return 0
else :
return time . time ( ) - self . _printStartTime100
2012-10-04 13:01:54 +00:00
def getPrintTimeRemainingEstimate ( self ) :
if self . _printStartTime100 == None or self . getPrintPos ( ) < 200 :
return None
printTime = ( time . time ( ) - self . _printStartTime100 ) / 60
printTimeTotal = printTime * ( len ( self . _gcodeList ) - 100 ) / ( self . getPrintPos ( ) - 100 )
printTimeLeft = printTimeTotal - printTime
return printTimeLeft
2012-10-01 12:51:59 +00:00
2012-09-06 14:52:05 +00:00
def getTemp ( self ) :
return self . _temp
def getBedTemp ( self ) :
return self . _bedTemp
2012-10-01 11:47:16 +00:00
def getLog ( self ) :
ret = [ ]
while not self . _logQueue . empty ( ) :
ret . append ( self . _logQueue . get ( ) )
for line in ret :
self . _logQueue . put ( line , False )
return ret
2012-09-06 14:52:05 +00:00
def _monitor ( self ) :
2012-10-02 08:58:57 +00:00
#Open the serial port.
if self . _port == ' AUTO ' :
self . _changeState ( self . STATE_DETECT_SERIAL )
programmer = stk500v2 . Stk500v2 ( )
self . _log ( " Serial port list: %s " % ( str ( serialList ( ) ) ) )
for p in serialList ( ) :
try :
self . _log ( " Connecting to: %s " % ( p ) )
programmer . connect ( p )
self . _serial = programmer . leaveISP ( )
break
except ispBase . IspError as ( e ) :
self . _log ( " Error while connecting to %s : %s " % ( p , str ( e ) ) )
pass
except :
self . _log ( " Unexpected error while connecting to serial port: %s %s " % ( p , getExceptionString ( ) ) )
programmer . close ( )
elif self . _port == ' VIRTUAL ' :
self . _changeState ( self . STATE_OPEN_SERIAL )
self . _serial = VirtualPrinter ( )
else :
self . _changeState ( self . STATE_OPEN_SERIAL )
try :
self . _log ( " Connecting to: %s " % ( self . _port ) )
if self . _baudrate == 0 :
2012-12-13 09:56:26 +00:00
self . _serial = serial . Serial ( str ( self . _port ) , 115200 , timeout = 0.1 , writeTimeout = 10000 )
2012-10-02 08:58:57 +00:00
else :
2012-12-13 09:56:26 +00:00
self . _serial = serial . Serial ( str ( self . _port ) , self . _baudrate , timeout = 2 , writeTimeout = 10000 )
2012-10-02 08:58:57 +00:00
except :
self . _log ( " Unexpected error while connecting to serial port: %s %s " % ( self . _port , getExceptionString ( ) ) )
if self . _serial == None :
self . _log ( " Failed to open serial port ( %s ) " % ( self . _port ) )
self . _errorValue = ' Failed to autodetect serial port. '
self . _changeState ( self . STATE_ERROR )
return
self . _log ( " Connected to: %s , starting monitor " % ( self . _serial ) )
if self . _baudrate == 0 :
self . _changeState ( self . STATE_DETECT_BAUDRATE )
else :
self . _changeState ( self . STATE_CONNECTING )
#Start monitoring the serial port.
2012-09-07 12:10:57 +00:00
timeout = time . time ( ) + 5
2012-10-01 12:36:47 +00:00
tempRequestTimeout = timeout
2013-02-17 16:47:06 +00:00
startSeen = not settings ( ) . getBoolean ( [ " feature " , " waitForStartOnConnect " ] )
2012-09-06 14:52:05 +00:00
while True :
line = self . _readline ( )
if line == None :
break
2013-03-17 09:58:34 +00:00
### Error detection
2012-09-06 14:52:05 +00:00
#No matter the state, if we see an error, goto the error state and store the error for reference.
2012-10-03 13:11:35 +00:00
if line . startswith ( ' Error: ' ) :
2012-09-06 14:52:05 +00:00
#Oh YEAH, consistency.
2012-10-03 13:11:35 +00:00
# Marlin reports an MIN/MAX temp error as "Error:x\n: Extruder switched off. MAXTEMP triggered !\n"
2012-09-06 14:52:05 +00:00
# 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.
2012-10-03 13:11:35 +00:00
if re . match ( ' Error:[0-9] \n ' , line ) :
2012-09-06 14:52:05 +00:00
line = line . rstrip ( ) + self . _readline ( )
2012-10-03 13:11:35 +00:00
#Skip the communication errors, as those get corrected.
2013-03-17 09:58:34 +00:00
if ' checksum mismatch ' in line \
or ' Wrong checksum ' in line \
or ' Line Number is not Last Line Number ' in line \
or ' expected line ' in line \
or ' No Line Number with checksum ' in line \
or ' No Checksum with line number ' in line \
or ' Missing checksum ' in line :
2012-10-03 13:11:35 +00:00
pass
2012-10-04 12:05:09 +00:00
elif not self . isError ( ) :
2012-10-03 13:11:35 +00:00
self . _errorValue = line [ 6 : ]
self . _changeState ( self . STATE_ERROR )
2013-03-17 09:58:34 +00:00
### Evaluate temperature status messages
2012-10-02 08:58:57 +00:00
if ' T: ' in line or line . startswith ( ' T: ' ) :
2013-03-17 09:58:34 +00:00
try :
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 , self . _targetTemp , self . _bedTargetTemp )
except ValueError :
# catch conversion issues, we'll rather just not get the temperature update instead of killing the connection
pass
2012-10-02 08:58:57 +00:00
#If we are waiting for an M109 or M190 then measure the time we lost during heatup, so we can remove that time from our printing time estimate.
if not ' ok ' in line and self . _heatupWaitStartTime != 0 :
t = time . time ( )
self . _heatupWaitTimeLost = t - self . _heatupWaitStartTime
self . _heatupWaitStartTime = t
2013-03-17 09:58:34 +00:00
### Forward messages from the firmware
2013-03-16 00:57:05 +00:00
elif line . strip ( ) != ' ' and line . strip ( ) != ' ok ' and not line . startswith ( " wait " ) and not line . startswith ( ' Resend: ' ) and line != ' echo:Unknown command: " " \n ' and self . isOperational ( ) :
2012-09-06 14:52:05 +00:00
self . _callback . mcMessage ( line )
2013-03-17 09:58:34 +00:00
### Baudrate detection
2012-09-06 14:52:05 +00:00
if self . _state == self . STATE_DETECT_BAUDRATE :
2012-09-07 12:10:57 +00:00
if line == ' ' or time . time ( ) > timeout :
2012-09-06 14:52:05 +00:00
if len ( self . _baudrateDetectList ) < 1 :
self . close ( )
2012-10-04 12:05:09 +00:00
self . _errorValue = " No more baudrates to test, and no suitable baudrate found. "
self . _changeState ( self . STATE_ERROR )
2012-09-06 14:52:05 +00:00
elif self . _baudrateDetectRetry > 0 :
self . _baudrateDetectRetry - = 1
self . _serial . write ( ' \n ' )
2012-10-04 12:05:09 +00:00
self . _log ( " Baudrate test retry: %d " % ( self . _baudrateDetectRetry ) )
2012-09-06 14:52:05 +00:00
self . _sendCommand ( " M105 " )
2012-10-04 12:05:09 +00:00
self . _testingBaudrate = True
2012-09-06 14:52:05 +00:00
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
2012-10-04 12:05:09 +00:00
self . _baudrateDetectTestOk = 0
2012-09-07 12:10:57 +00:00
timeout = time . time ( ) + 5
2012-09-06 14:52:05 +00:00
self . _serial . write ( ' \n ' )
self . _sendCommand ( " M105 " )
2012-10-04 12:05:09 +00:00
self . _testingBaudrate = True
2012-09-06 14:52:05 +00:00
except :
self . _log ( " Unexpected error while setting baudrate: %d %s " % ( baudrate , getExceptionString ( ) ) )
2012-10-04 12:05:09 +00:00
elif ' ok ' in line and ' T: ' in line :
self . _baudrateDetectTestOk + = 1
if self . _baudrateDetectTestOk < 10 :
self . _log ( " Baudrate test ok: %d " % ( self . _baudrateDetectTestOk ) )
self . _sendCommand ( " M105 " )
else :
self . _sendCommand ( " M999 " )
self . _serial . timeout = 2
self . _changeState ( self . STATE_OPERATIONAL )
else :
self . _testingBaudrate = False
2013-03-17 09:58:34 +00:00
### Connection attempt
2012-09-06 14:52:05 +00:00
elif self . _state == self . STATE_CONNECTING :
2013-03-16 00:57:05 +00:00
if ( line == " " or " wait " in line ) and startSeen :
2012-09-06 14:52:05 +00:00
self . _sendCommand ( " M105 " )
2013-03-16 00:57:05 +00:00
elif " start " in line :
2013-02-17 16:47:06 +00:00
startSeen = True
2013-03-16 00:57:05 +00:00
elif " ok " in line and startSeen :
2012-09-06 14:52:05 +00:00
self . _changeState ( self . STATE_OPERATIONAL )
2013-01-13 14:45:39 +00:00
elif time . time ( ) > timeout :
2012-09-06 14:52:05 +00:00
self . close ( )
2013-03-17 09:58:34 +00:00
### Operational
2013-03-01 22:19:58 +00:00
elif self . _state == self . STATE_OPERATIONAL or self . _state == self . STATE_PAUSED :
#Request the temperature on comm timeout (every 5 seconds) when we are not printing.
2013-03-16 00:57:05 +00:00
if line == " " or " wait " in line :
2012-09-06 14:52:05 +00:00
self . _sendCommand ( " M105 " )
2012-09-07 12:10:57 +00:00
tempRequestTimeout = time . time ( ) + 5
2013-03-17 09:58:34 +00:00
### Printing
2012-09-06 14:52:05 +00:00
elif self . _state == self . STATE_PRINTING :
2013-03-16 00:57:05 +00:00
if line == " " and time . time ( ) > timeout :
2012-09-06 14:52:05 +00:00
self . _log ( " Communication timeout during printing, forcing a line " )
2013-03-16 00:57:05 +00:00
line = " ok "
2013-03-16 17:25:39 +00:00
#Even when printing request the temperature every 5 seconds.
2012-09-07 12:10:57 +00:00
if time . time ( ) > tempRequestTimeout :
self . _commandQueue . put ( " M105 " )
tempRequestTimeout = time . time ( ) + 5
2013-03-17 09:58:34 +00:00
# ok -> send next command
2013-03-16 00:57:05 +00:00
if " ok " in line :
2012-09-07 12:10:57 +00:00
timeout = time . time ( ) + 5
2013-03-16 17:25:39 +00:00
if self . _resendDelta is not None :
self . _resendNextCommand ( )
elif not self . _commandQueue . empty ( ) :
2012-09-06 14:52:05 +00:00
self . _sendCommand ( self . _commandQueue . get ( ) )
else :
self . _sendNext ( )
2013-03-17 09:58:34 +00:00
# resend -> start resend procedure from requested line
2012-09-06 14:52:05 +00:00
elif " resend " in line . lower ( ) or " rs " in line :
2013-03-16 17:25:39 +00:00
lineToResend = None
2012-09-06 14:52:05 +00:00
try :
2013-03-16 17:25:39 +00:00
lineToResend = int ( line . replace ( " N: " , " " ) . replace ( " N " , " " ) . replace ( " : " , " " ) . split ( ) [ - 1 ] )
2012-09-06 14:52:05 +00:00
except :
if " rs " in line :
2013-03-16 17:25:39 +00:00
lineToResend = int ( line . split ( ) [ 1 ] )
if lineToResend is not None :
self . _resendDelta = self . _currentLine - lineToResend
if self . _resendDelta > len ( self . _lastLines ) :
self . _errorValue = " Printer requested line %d but history is only available up to line %d " % ( lineToResend , self . _currentLine - len ( self . _lastLines ) )
self . _changeState ( self . STATE_ERROR )
self . _logger . warn ( self . _errorValue )
else :
self . _resendNextCommand ( )
2012-09-06 14:52:05 +00:00
self . _log ( " Connection closed, closing down monitor " )
2012-09-05 14:10:47 +00:00
def _log ( self , message ) :
2012-09-06 14:52:05 +00:00
self . _callback . mcLog ( message )
2013-03-16 01:24:33 +00:00
self . _serialLogger . debug ( message )
2012-09-06 14:52:05 +00:00
try :
self . _logQueue . put ( message , False )
except :
#If the log queue is full, remove the first message and append the new message again
self . _logQueue . get ( )
2012-10-10 10:23:53 +00:00
try :
self . _logQueue . put ( message , False )
except :
pass
2012-03-06 16:48:56 +00:00
2012-09-06 14:52:05 +00:00
def _readline ( self ) :
2012-09-05 14:10:47 +00:00
if self . _serial == None :
2012-04-01 17:16:31 +00:00
return None
2012-07-19 12:27:59 +00:00
try :
2012-09-05 14:10:47 +00:00
ret = self . _serial . readline ( )
2012-07-19 12:27:59 +00:00
except :
2012-09-06 09:38:54 +00:00
self . _log ( " Unexpected error while reading serial port: %s " % ( getExceptionString ( ) ) )
2012-09-06 14:52:05 +00:00
self . _errorValue = getExceptionString ( )
self . close ( True )
return None
if ret == ' ' :
#self._log("Recv: TIMEOUT")
2012-09-05 14:10:47 +00:00
return ' '
2012-10-01 13:14:47 +00:00
self . _log ( " Recv: %s " % ( unicode ( ret , ' ascii ' , ' replace ' ) . encode ( ' ascii ' , ' replace ' ) . rstrip ( ) ) )
2012-03-07 21:45:41 +00:00
return ret
2012-03-06 22:11:40 +00:00
2012-09-06 14:52:05 +00:00
def close ( self , isError = False ) :
2012-09-05 14:10:47 +00:00
if self . _serial != None :
self . _serial . close ( )
2012-09-06 14:52:05 +00:00
if isError :
self . _changeState ( self . STATE_CLOSED_WITH_ERROR )
else :
self . _changeState ( self . STATE_CLOSED )
2012-09-05 14:10:47 +00:00
self . _serial = None
2012-03-07 11:17:03 +00:00
2012-05-09 20:47:27 +00:00
def __del__ ( self ) :
self . close ( )
2013-03-16 17:25:39 +00:00
def _resendNextCommand ( self ) :
self . _logger . debug ( " Resending line %d , delta is %d , history log is %s items strong " % ( self . _currentLine - self . _resendDelta , self . _resendDelta , len ( self . _lastLines ) ) )
cmd = self . _lastLines [ - self . _resendDelta ]
lineNumber = self . _currentLine - self . _resendDelta
self . _doSendWithChecksum ( cmd , lineNumber )
self . _resendDelta - = 1
2013-03-17 12:53:38 +00:00
if self . _resendDelta < = 0 :
2013-03-16 17:25:39 +00:00
self . _resendDelta = None
2013-03-16 00:57:05 +00:00
def _sendCommand ( self , cmd , sendChecksum = False ) :
2013-03-11 14:39:03 +00:00
cmd = cmd . upper ( )
2012-12-17 13:15:40 +00:00
if self . _serial is None :
2012-03-07 11:17:03 +00:00
return
2012-10-02 08:58:57 +00:00
if ' M109 ' in cmd or ' M190 ' in cmd :
self . _heatupWaitStartTime = time . time ( )
2012-10-03 13:11:35 +00:00
if ' M104 ' in cmd or ' M109 ' in cmd :
try :
self . _targetTemp = float ( re . search ( ' S([0-9]+) ' , cmd ) . group ( 1 ) )
except :
pass
if ' M140 ' in cmd or ' M190 ' in cmd :
try :
2013-01-07 17:54:49 +00:00
self . _bedTargetTemp = float ( re . search ( ' S([0-9]+) ' , cmd ) . group ( 1 ) )
2012-10-03 13:11:35 +00:00
except :
pass
2013-03-16 00:57:05 +00:00
2013-03-16 08:30:02 +00:00
if " M110 " in cmd :
2013-03-16 17:25:39 +00:00
newLineNumber = None
if " N " in cmd :
try :
newLineNumber = int ( re . search ( " N([0-9]+) " , cmd ) . group ( 1 ) )
except :
pass
else :
newLineNumber = 0
if settings ( ) . getBoolean ( [ " feature " , " resetLineNumbersWithPrefixedN " ] ) and newLineNumber is not None :
# let's rewrite the M110 command to fit repetier syntax
self . _doSendWithChecksum ( " M110 " , newLineNumber )
self . _addToLastLines ( cmd )
self . _currentLine = newLineNumber + 1
else :
self . _doSend ( cmd , sendChecksum )
if newLineNumber is not None :
self . _currentLine = newLineNumber + 1
# after a reset of the line number we have no way to determine what line exactly the printer now wants
self . _lastLines = [ ]
self . _resendDelta = None
2013-03-16 08:30:02 +00:00
else :
2013-03-16 17:25:39 +00:00
self . _doSend ( cmd , sendChecksum )
def _addToLastLines ( self , cmd ) :
self . _lastLines . append ( cmd )
if len ( self . _lastLines ) > 50 :
self . _lastLines = self . _lastLines [ - 50 : ] # only keep the last 50 lines in memory
self . _logger . debug ( " Got %d lines of history in memory " % len ( self . _lastLines ) )
def _doSend ( self , cmd , sendChecksum = False ) :
2013-03-16 00:57:05 +00:00
if sendChecksum or self . _alwaysSendChecksum :
if self . _alwaysSendChecksum :
lineNumber = self . _currentLine
2013-03-16 17:25:39 +00:00
else :
lineNumber = self . _gcodePos
self . _doSendWithChecksum ( cmd , lineNumber )
self . _addToLastLines ( cmd )
self . _currentLine + = 1
else :
self . _doSendWithoutChecksum ( cmd )
def _doSendWithChecksum ( self , cmd , lineNumber = None ) :
self . _logger . debug ( " Sending cmd ' %s ' with lineNumber %r " % ( cmd , lineNumber ) )
if lineNumber is None :
lineNumber = self . _currentLine
checksum = reduce ( lambda x , y : x ^ y , map ( ord , " N %d %s " % ( lineNumber , cmd ) ) )
commandToSend = " N %d %s * %d " % ( lineNumber , cmd , checksum )
self . _doSendWithoutChecksum ( commandToSend )
2013-03-16 00:57:05 +00:00
2013-03-16 17:25:39 +00:00
def _doSendWithoutChecksum ( self , cmd ) :
self . _log ( " Send: %s " % cmd )
2012-09-05 14:10:47 +00:00
try :
2013-03-16 17:25:39 +00:00
self . _serial . write ( cmd + ' \n ' )
2012-10-04 12:05:09 +00:00
except serial . SerialTimeoutException :
2012-10-03 13:11:35 +00:00
self . _log ( " Serial timeout while writing to serial port, trying again. " )
try :
2013-03-16 17:25:39 +00:00
self . _serial . write ( cmd + ' \n ' )
2012-10-03 13:11:35 +00:00
except :
self . _log ( " Unexpected error while writing serial port: %s " % ( getExceptionString ( ) ) )
self . _errorValue = getExceptionString ( )
self . close ( True )
2012-09-05 14:10:47 +00:00
except :
2012-09-06 09:38:54 +00:00
self . _log ( " Unexpected error while writing serial port: %s " % ( getExceptionString ( ) ) )
2012-09-06 14:52:05 +00:00
self . _errorValue = getExceptionString ( )
self . close ( True )
2013-03-16 00:57:05 +00:00
2012-09-06 14:52:05 +00:00
def _sendNext ( self ) :
if self . _gcodePos > = len ( self . _gcodeList ) :
self . _changeState ( self . STATE_OPERATIONAL )
return
2012-10-04 13:01:54 +00:00
if self . _gcodePos == 100 :
self . _printStartTime100 = time . time ( )
2012-09-06 14:52:05 +00:00
line = self . _gcodeList [ self . _gcodePos ]
2012-09-07 10:04:55 +00:00
if type ( line ) is tuple :
self . _printSection = line [ 1 ]
line = line [ 0 ]
try :
if line == ' M0 ' or line == ' M1 ' :
self . setPause ( True )
line = ' M105 ' #Don't send the M0 or M1 to the machine, as M0 and M1 are handled as an LCD menu pause.
if self . _printSection in self . _feedRateModifier :
line = re . sub ( ' F([0-9]*) ' , lambda m : ' F ' + str ( int ( int ( m . group ( 1 ) ) * self . _feedRateModifier [ self . _printSection ] ) ) , line )
2012-09-07 12:10:57 +00:00
if ( ' G0 ' in line or ' G1 ' in line ) and ' Z ' in line :
z = float ( re . search ( ' Z([0-9 \ .]*) ' , line ) . group ( 1 ) )
if self . _currentZ != z :
self . _currentZ = z
self . _callback . mcZChange ( z )
2012-09-07 10:04:55 +00:00
except :
self . _log ( " Unexpected error: %s " % ( getExceptionString ( ) ) )
2013-03-16 00:57:05 +00:00
self . _sendCommand ( line , True )
2012-09-06 14:52:05 +00:00
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
2012-10-04 13:01:54 +00:00
self . _printStartTime100 = None
2012-09-07 10:04:55 +00:00
self . _printSection = ' CUSTOM '
2012-09-06 14:52:05 +00:00
self . _changeState ( self . STATE_PRINTING )
2012-10-01 12:51:59 +00:00
self . _printStartTime = time . time ( )
2012-09-06 14:52:05 +00:00
for i in xrange ( 0 , 6 ) :
self . _sendNext ( )
def cancelPrint ( self ) :
if self . isOperational ( ) :
self . _changeState ( self . STATE_OPERATIONAL )
2012-09-06 15:24:34 +00:00
def setPause ( self , pause ) :
if not pause and self . isPaused ( ) :
self . _changeState ( self . STATE_PRINTING )
for i in xrange ( 0 , 6 ) :
self . _sendNext ( )
if pause and self . isPrinting ( ) :
self . _changeState ( self . STATE_PAUSED )
2012-09-07 10:04:55 +00:00
def setFeedrateModifier ( self , type , value ) :
self . _feedRateModifier [ type ] = value
2012-09-06 09:38:54 +00:00
2012-12-29 14:41:23 +00:00
def getFeedrateModifiers ( self ) :
result = { }
result . update ( self . _feedRateModifier )
return result
2012-09-06 09:38:54 +00:00
def getExceptionString ( ) :
locationInfo = traceback . extract_tb ( sys . exc_info ( ) [ 2 ] ) [ 0 ]
return " %s : ' %s ' @ %s : %s : %d " % ( str ( sys . exc_info ( ) [ 0 ] . __name__ ) , str ( sys . exc_info ( ) [ 1 ] ) , os . path . basename ( locationInfo [ 0 ] ) , locationInfo [ 2 ] , locationInfo [ 1 ] )