added new comm_acc2 file and switched to it

This commit is contained in:
make-ing 2015-10-16 18:48:42 +02:00
parent b97995323a
commit 9776bba6bc
3 changed files with 803 additions and 2 deletions

View file

@ -23,7 +23,7 @@ __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms
import re
import octoprint.util.comm_acc as comm
import octoprint.util.comm_acc2 as comm
import octoprint.util as util
from octoprint.settings import settings

View file

@ -22,7 +22,7 @@ from octoprint.plugin import plugin_manager, ProgressPlugin
from octoprint.printer import PrinterInterface, PrinterCallback, UnknownScript
from octoprint.printer.estimation import TimeEstimationHelper
from octoprint.settings import settings
from octoprint.util import comm_acc as comm
from octoprint.util import comm_acc2 as comm
from octoprint.util import InvariantContainer

View file

@ -0,0 +1,801 @@
# coding=utf-8
from __future__ import absolute_import
__author__ = "Florian Becker <florian@mr-beam.org> based on work by Gina Häußge and David Braam"
__license__ = "GNU Affero General Public License http://www.gnu.org/licenses/agpl.html"
__copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
import os
import threading
import logging
import glob
import time
import serial
import re
from yaml import load as yamlload
from yaml import dump as yamldump
from subprocess import call as subprocesscall
import octoprint.plugin
from octoprint.events import eventManager, Events
from octoprint.settings import settings, default_settings
from octoprint.util import get_exception_string, RepeatedTimer, CountedEvent, sanitize_ascii
### MachineCom #########################################################################################################
class MachineCom(object):
STATE_NONE = 0
STATE_OPEN_SERIAL = 1
STATE_DETECT_SERIAL = 2
STATE_CONNECTING = 3
STATE_OPERATIONAL = 4
STATE_PRINTING = 5
STATE_PAUSED = 6
STATE_CLOSED = 7
STATE_ERROR = 8
STATE_CLOSED_WITH_ERROR = 9
STATE_LOCKED = 10
STATE_HOMING = 11
STATE_FLASHING = 12
def __init__(self, port=None, baudrate=None, callbackObject=None, printerProfileManager=None):
self._logger = logging.getLogger(__name__)
self._serialLogger = logging.getLogger("SERIAL")
if port is None:
port = settings().get(["serial", "port"])
if baudrate is None:
settingsBaudrate = settings().getInt(["serial", "baudrate"])
if settingsBaudrate is None:
baudrate = 0
else:
baudrate = settingsBaudrate
if callbackObject is None:
callbackObject = MachineComPrintCallback()
self._port = port
self._baudrate = baudrate
self._callback = callbackObject
self._printerProfileManager = printerProfileManager
self._state = self.STATE_NONE
self._errorValue = "Unknown Error"
self._serial = None
self._currentFile = None
self._clear_to_send = CountedEvent(max=10, name="comm.clear_to_send")
self._long_running_command = False
self._status_timer = None
# hooks
self._pluginManager = octoprint.plugin.plugin_manager()
self._serial_factory_hooks = self._pluginManager.get_hooks("octoprint.comm.transport.serial.factory")
self._state_parse_dict = {self.STATE_NONE:self._state_none_handle,
self.STATE_CONNECTING:self._state_connecting_handle,
self.STATE_LOCKED:self._state_locked_handle,
self.STATE_HOMING:self._state_homing_handle,
self.STATE_OPERATIONAL:self._state_operational_handle}
# monitoring thread
self._monitoring_active = True
self.monitoring_thread = threading.Thread(target=self._monitor_loop, name="comm._monitoring_thread")
self.monitoring_thread.daemon = True
self.monitoring_thread.start()
# sending thread
self._sending_active = True
self.sending_thread = threading.Thread(target=self._send_loop, name="comm.sending_thread")
self.sending_thread.daemon = True
self.sending_thread.start()
def _monitor_loop(self):
pause_triggers = convert_pause_triggers(settings().get(["printerParameters", "pauseTriggers"]))
#Open the serial port.
if not self._openSerial():
return
self._log("Connected to: %s, starting monitor" % self._serial)
self._changeState(self.STATE_CONNECTING)
self._timeout = get_new_timeout("communication")
supportWait = settings().getBoolean(["feature", "supportWait"])
while self._monitoring_active:
try:
line = self._readline()
if line is None:
break
if line.strip() is not "":
self._timeout = get_new_timeout("communication")
# parse line depending on state
self._state_parse_dict[self._state](self, line)
return
# # GRBL Position update
# if self._grbl :
# if(self._state == self.STATE_HOMING and 'ok' in line):
# self._changeState(self.STATE_OPERATIONAL)
# self._onHomingDone();
#
# # TODO check if "Alarm" is enough
# if("Alarm lock" in line):
# self._changeState(self.STATE_LOCKED)
# if("['$H'|'$X' to unlock]" in line):
# self._changeState(self.STATE_LOCKED)
#
# # TODO maybe better in _gcode_X_sent ...
# if("Idle" in line and (self._state == self.STATE_LOCKED) ):
# self._changeState(self.STATE_OPERATIONAL)
#
# # TODO highly experimental. needs testing.
# if("Hold" in line and self._state == self.STATE_PRINTING):
# self._changeState(self.STATE_PAUSED)
# #if("Run" in line and self._state == self.STATE_PAUSED):
# # self._changeState(self.STATE_PRINTING)
#
# if 'MPos:' in line:
# self._update_grbl_pos(line)
#
# if("ALARM: Hard/soft limit" in line):
# errorMsg = "Machine Limit Hit. Please reset the machine and do a homing cycle"
# self._log(errorMsg)
# self._errorValue = errorMsg
# eventManager().fire(Events.ERROR, {"error": self.getErrorString()})
# eventManager().fire(Events.LIMITS_HIT, {"error": self.getErrorString()})
# self._openSerial()
# self._changeState(self.STATE_CONNECTING)
#
# if("Invalid gcode" in line and self._state == self.STATE_PRINTING):
# # TODO Pause machine instead of resetting it.
# errorMsg = line
# self._log(errorMsg)
# self._errorValue = errorMsg
# # self._changeState(self.STATE_ERROR)
# eventManager().fire(Events.ERROR, {"error": self.getErrorString()})
# self._openSerial()
# self._changeState(self.STATE_CONNECTING)
#
# if("Grbl" in line and self._state == self.STATE_PRINTING):
# errorMsg = "Machine reset."
# self._log(errorMsg)
# self._errorValue = errorMsg
# self._changeState(self.STATE_LOCKED)
# eventManager().fire(Events.ERROR, {"error": self.getErrorString()})
#
# if("Grbl" in line):
# versionMatch = re.search("Grbl (?P<grbl>.+?)(_(?P<git>[0-9a-f]{7})(?P<dirty>-dirty)?)? \[.+\]", line)
# if(versionMatch):
# versionDict = versionMatch.groupdict()
# self._writeGrblVersionToFile(versionDict)
# if self._compareGrblVersion(versionDict) is False:
# self._flashGrbl()
#
# if("error:" in line):
# self.handle_grbl_error(line)
#
# # ##~~ SD file list
# # # if we are currently receiving an sd file list, each line is just a filename, so just read it and abort processing
# # if self._sdFileList and not "End file list" in line:
# # preprocessed_line = line.strip().lower()
# # fileinfo = preprocessed_line.rsplit(None, 1)
# # if len(fileinfo) > 1:
# # # we might have extended file information here, so let's split filename and size and try to make them a bit nicer
# # filename, size = fileinfo
# # try:
# # size = int(size)
# # except ValueError:
# # # whatever that was, it was not an integer, so we'll just use the whole line as filename and set size to None
# # filename = preprocessed_line
# # size = None
# # else:
# # # no extended file information, so only the filename is there and we set size to None
# # filename = preprocessed_line
# # size = None
# #
# # if valid_file_type(filename, "machinecode"):
# # if filter_non_ascii(filename):
# # self._logger.warn("Got a file from printer's SD that has a non-ascii filename (%s), that shouldn't happen according to the protocol" % filename)
# # else:
# # if not filename.startswith("/"):
# # # file from the root of the sd -- we'll prepend a /
# # filename = "/" + filename
# # self._sdFiles.append((filename, size))
# # continue
#
# ##~~ process oks
# if line.strip().startswith("ok") or (self.isPrinting() and supportWait and line.strip().startswith("wait")):
# self._clear_to_send.set()
# self._long_running_command = False
#
# # ##~~ Temperature processing
# # if ' T:' in line or line.startswith('T:') or ' T0:' in line or line.startswith('T0:') or ' B:' in line or line.startswith('B:'):
# # if not disable_external_heatup_detection and not line.strip().startswith("ok") and not self._heating:
# # self._logger.debug("Externally triggered heatup detected")
# # self._heating = True
# # self._heatupWaitStartTime = time.time()
# # self._processTemperatures(line)
# # self._callback.on_comm_temperature_update(self._temp, self._bedTemp)
# #
# # elif supportRepetierTargetTemp and ('TargetExtr' in line or 'TargetBed' in line):
# # matchExtr = self._regex_repetierTempExtr.match(line)
# # matchBed = self._regex_repetierTempBed.match(line)
# #
# # if matchExtr is not None:
# # toolNum = int(matchExtr.group(1))
# # try:
# # target = float(matchExtr.group(2))
# # if toolNum in self._temp.keys() and self._temp[toolNum] is not None and isinstance(self._temp[toolNum], tuple):
# # (actual, oldTarget) = self._temp[toolNum]
# # self._temp[toolNum] = (actual, target)
# # else:
# # self._temp[toolNum] = (None, target)
# # self._callback.on_comm_temperature_update(self._temp, self._bedTemp)
# # except ValueError:
# # pass
# # elif matchBed is not None:
# # try:
# # target = float(matchBed.group(1))
# # if self._bedTemp is not None and isinstance(self._bedTemp, tuple):
# # (actual, oldTarget) = self._bedTemp
# # self._bedTemp = (actual, target)
# # else:
# # self._bedTemp = (None, target)
# # self._callback.on_comm_temperature_update(self._temp, self._bedTemp)
# # except ValueError:
# # pass
#
# # #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 'ok' in line and self._heatupWaitStartTime:
# # self._heatupWaitTimeLost = self._heatupWaitTimeLost + (time.time() - self._heatupWaitStartTime)
# # self._heatupWaitStartTime = None
# # self._heating = False
#
# # ##~~ SD Card handling
# # elif 'SD init fail' in line or 'volume.init failed' in line or 'openRoot failed' in line:
# # self._sdAvailable = False
# # self._sdFiles = []
# # self._callback.on_comm_sd_state_change(self._sdAvailable)
# # elif 'Not SD printing' in line:
# # if self.isSdFileSelected() and self.isPrinting():
# # # something went wrong, printer is reporting that we actually are not printing right now...
# # self._sdFilePos = 0
# # self._changeState(self.STATE_OPERATIONAL)
# # elif 'SD card ok' in line and not self._sdAvailable:
# # self._sdAvailable = True
# # self.refreshSdFiles()
# # self._callback.on_comm_sd_state_change(self._sdAvailable)
# # elif 'Begin file list' in line:
# # self._sdFiles = []
# # self._sdFileList = True
# # elif 'End file list' in line:
# # self._sdFileList = False
# # self._callback.on_comm_sd_files(self._sdFiles)
# # elif 'SD printing byte' in line and self.isSdPrinting():
# # # answer to M27, at least on Marlin, Repetier and Sprinter: "SD printing byte %d/%d"
# # match = self._regex_sdPrintingByte.search(line)
# # self._currentFile.setFilepos(int(match.group(1)))
# # self._callback.on_comm_progress()
# # elif 'File opened' in line and not self._ignore_select:
# # # answer to M23, at least on Marlin, Repetier and Sprinter: "File opened:%s Size:%d"
# # match = self._regex_sdFileOpened.search(line)
# # if self._sdFileToSelect:
# # name = self._sdFileToSelect
# # self._sdFileToSelect = None
# # else:
# # name = match.group(1)
# # self._currentFile = PrintingSdFileInformation(name, int(match.group(2)))
# # elif 'File selected' in line:
# # if self._ignore_select:
# # self._ignore_select = False
# # elif self._currentFile is not None:
# # # final answer to M23, at least on Marlin, Repetier and Sprinter: "File selected"
# # self._callback.on_comm_file_selected(self._currentFile.getFilename(), self._currentFile.getFilesize(), True)
# # eventManager().fire(Events.FILE_SELECTED, {
# # "file": self._currentFile.getFilename(),
# # "origin": self._currentFile.getFileLocation()
# # })
# # elif 'Writing to file' in line:
# # # anwer to M28, at least on Marlin, Repetier and Sprinter: "Writing to file: %s"
# # self._changeState(self.STATE_PRINTING)
# # self._clear_to_send.set()
# # line = "ok"
# # elif 'Done printing file' in line and self.isSdPrinting():
# # # printer is reporting file finished printing
# # self._sdFilePos = 0
# # self._callback.on_comm_print_job_done()
# # self._changeState(self.STATE_OPERATIONAL)
# # eventManager().fire(Events.PRINT_DONE, {
# # "file": self._currentFile.getFilename(),
# # "filename": os.path.basename(self._currentFile.getFilename()),
# # "origin": self._currentFile.getFileLocation(),
# # "time": self.getPrintTime()
# # })
# # if self._sd_status_timer is not None:
# # try:
# # self._sd_status_timer.cancel()
# # except:
# # pass
# # elif 'Done saving file' in line:
# # self.refreshSdFiles()
# # elif 'File deleted' in line and line.strip().endswith("ok"):
# # # buggy Marlin version that doesn't send a proper \r after the "File deleted" statement, fixed in
# # # current versions
# # self._clear_to_send.set()
#
# ##~~ Message handling
# elif line.strip() != '' \
# and line.strip() != 'ok' and not line.startswith("wait") \
# and not line.startswith('Resend:') \
# and line != 'echo:Unknown command:""\n' \
# and line != "Unsupported statement" \
# and self.isOperational():
# self._callback.on_comm_message(line)
#
# ##~~ Parsing for feedback commands
# if feedback_controls and feedback_matcher and not "_all" in feedback_errors:
# try:
# self._process_registered_message(line, feedback_matcher, feedback_controls, feedback_errors)
# except:
# # something went wrong while feedback matching
# self._logger.exception("Error while trying to apply feedback control matching, disabling it")
# feedback_errors.append("_all")
#
# ##~~ Parsing for pause triggers
#
# if pause_triggers and not self.isStreaming():
# if "enable" in pause_triggers.keys() and pause_triggers["enable"].search(line) is not None:
# self.setPause(True)
# elif "disable" in pause_triggers.keys() and pause_triggers["disable"].search(line) is not None:
# self.setPause(False)
# elif "toggle" in pause_triggers.keys() and pause_triggers["toggle"].search(line) is not None:
# self.setPause(not self.isPaused())
#
#
# ### Operational
# elif self._state == self.STATE_OPERATIONAL or self._state == self.STATE_PAUSED:
# if "ok" in line:
# # if we still have commands to process, process them
# if self._resendSwallowNextOk:
# self._resendSwallowNextOk = False
# elif self._resendDelta is not None:
# self._resendNextCommand()
# elif self._sendFromQueue():
# pass
#
# # resend -> start resend procedure from requested line
# elif line.lower().startswith("resend") or line.lower().startswith("rs"):
# self._handleResendRequest(line)
#
# ### Printing
# elif self._state == self.STATE_PRINTING:
# if line == "" and time.time() > self._timeout:
# if not self._long_running_command:
# self._log("Communication timeout during printing, forcing a line")
# self._sendCommand("?")
# self._clear_to_send.set()
# else:
# self._logger.debug("Ran into a communication timeout, but a command known to be a long runner is currently active")
#
# if "ok" in line or (supportWait and "wait" in line):
# # a wait while printing means our printer's buffer ran out, probably due to some ok getting
# # swallowed, so we treat it the same as an ok here teo take up communication again
# if self._resendSwallowNextOk:
# self._resendSwallowNextOk = False
#
# elif self._resendDelta is not None:
# self._resendNextCommand()
#
# else:
# if self._sendFromQueue():
# pass
# elif not self.isSdPrinting():
# self._sendNext()
#
# elif line.lower().startswith("resend") or line.lower().startswith("rs"):
# self._handleResendRequest(line)
except:
self._logger.exception("Something crashed inside the serial connection loop, please report this in OctoPrint's bug tracker:")
errorMsg = "See octoprint.log for details"
self._log(errorMsg)
self._errorValue = errorMsg
self._changeState(self.STATE_ERROR)
eventManager().fire(Events.ERROR, {"error": self.getErrorString()})
self._log("Connection closed, closing down monitor")
def _send_loop(self):
pass
def _openSerial(self):
def default(_, port, baudrate, read_timeout):
if port is None or port == 'AUTO':
# no known port, try auto detection
self._changeState(self.STATE_DETECT_SERIAL)
ser = self._detectPort(True)
if ser is None:
self._errorValue = 'Failed to autodetect serial port, please set it manually.'
self._changeState(self.STATE_ERROR)
eventManager().fire(Events.ERROR, {"error": self.getErrorString()})
self._log("Failed to autodetect serial port, please set it manually.")
return None
port = ser.port
# connect to regular serial port
self._log("Connecting to: %s" % port)
if baudrate == 0:
baudrates = baudrateList()
ser = serial.Serial(str(port), 115200 if 115200 in baudrates else baudrates[0], timeout=read_timeout, writeTimeout=10000, parity=serial.PARITY_ODD)
else:
ser = serial.Serial(str(port), baudrate, timeout=read_timeout, writeTimeout=10000, parity=serial.PARITY_ODD)
ser.close()
ser.parity = serial.PARITY_NONE
ser.open()
return ser
serial_factories = self._serial_factory_hooks.items() + [("default", default)]
for name, factory in serial_factories:
try:
serial_obj = factory(self, self._port, self._baudrate, settings().getFloat(["serial", "timeout", "connection"]))
except (OSError, serial.SerialException):
exception_string = get_exception_string()
self._errorValue = "Connection error, see Terminal tab"
self._changeState(self.STATE_ERROR)
eventManager().fire(Events.ERROR, {"error": self.getErrorString()})
self._log("Unexpected error while connecting to serial port: %s %s (hook %s)" % (self._port, exception_string, name))
if "failed to set custom baud rate" in exception_string.lower():
self._log("Your installation does not support custom baudrates (e.g. 250000) for connecting to your printer. This is a problem of the pyserial library that OctoPrint depends on. Please update to a pyserial version that supports your baudrate or switch your printer's firmware to a standard baudrate (e.g. 115200). See https://github.com/foosel/OctoPrint/wiki/OctoPrint-support-for-250000-baud-rate-on-Raspbian")
return False
if serial_obj is not None:
# first hook to succeed wins, but any can pass on to the next
self._changeState(self.STATE_OPEN_SERIAL)
self._serial = serial_obj
self._clear_to_send.clear()
return True
return False
def _readline(self):
if self._serial is None:
return None
try:
ret = self._serial.readline()
if('ok' in ret or 'error' in ret):
if(len(self.acc_line_lengths) > 0):
#print('buffer',sum(self.acc_line_lengths), 'deleting after ok', self.acc_line_lengths[0])
del self.acc_line_lengths[0] # Delete the commands character count corresponding to the last 'ok'
except serial.SerialException:
self._log("Unexpected error while reading serial port: %s" % (get_exception_string()))
self._errorValue = get_exception_string()
self.close(True)
return None
if ret == '': return ''
try:
self._log("Recv: %s" % sanitize_ascii(ret))
except ValueError as e:
self._log("WARN: While reading last line: %s" % e)
self._log("Recv: %r" % ret)
return ret
def _state_none_handle(self, line):
pass
def _state_connecting_handle(self, line):
if line.startswith("Grbl"):
versionMatch = re.search("Grbl (?P<grbl>.+?)(_(?P<git>[0-9a-f]{7})(?P<dirty>-dirty)?)? \[.+\]", line)
if(versionMatch):
versionDict = versionMatch.groupdict()
self._writeGrblVersionToFile(versionDict)
if self._compareGrblVersion(versionDict) is False:
self._flashGrbl()
self._onConnected(self.STATE_LOCKED)
def _state_locked_handle(self, line):
pass
def _state_homing_handle(self, line):
if line.startswith("ok"):
self._changeState(self.STATE_OPERATIONAL)
def _state_operational_handle(self, line):
pass
# internal state management
def _changeState(self, newState):
if self._state == newState:
return
if newState == self.STATE_PRINTING:
if self._status_timer is not None:
self._status_timer.cancel()
elif newState == self.STATE_OPERATIONAL:
if self._status_timer is not None:
self._status_timer.cancel()
self._status_timer = RepeatedTimer(1, self._poll_status, run_first=True)
self._status_timer.start()
if newState == self.STATE_CLOSED or newState == self.STATE_CLOSED_WITH_ERROR:
if self._currentFile is not None:
self._currentFile.close()
self._log("entered state closed / closed with error. reseting character counter.")
self.acc_line_lengths = []
oldState = self.getStateString()
self._state = newState
self._log('Changing monitoring state from \'%s\' to \'%s\'' % (oldState, self.getStateString()))
self._callback.on_comm_state_change(newState)
def _onConnected(self, nextState):
self._serial.timeout = settings().getFloat(["serial", "timeout", "communication"])
if(nextState is None):
self._changeState(self.STATE_LOCKED)
else:
self._changeState(nextState)
payload = dict(port=self._port, baudrate=self._baudrate)
eventManager().fire(Events.CONNECTED, payload)
def _detectPort(self, close):
self._log("Serial port list: %s" % (str(serialList())))
for p in serialList():
try:
self._log("Connecting to: %s" % (p))
serial_obj = serial.Serial(p)
if close:
serial_obj.close()
return serial_obj
except (OSError, serial.SerialException) as e:
self._log("Error while connecting to %s: %s" % (p, str(e)))
return None
def _poll_status(self):
if self.isOperational() and not self._long_running_command:
self.sendCommand("?", cmd_type="status_poll")
def _log(self, message):
self._callback.on_comm_log(message)
self._serialLogger.debug(message)
def _compareGrblVersion(self, versionDict):
cwd = os.path.dirname(__file__)
with open(cwd + "/../grbl/grblVersionRequirement.yml", 'r') as infile:
grblReqDict = yamlload(infile)
requiredGrblVer = str(grblReqDict['grbl']) + '_' + str(grblReqDict['git'])
if grblReqDict['dirty'] is True:
requiredGrblVer += '-dirty'
actualGrblVer = str(versionDict['grbl']) + '_' + str(versionDict['git'])
if versionDict['dirty'] is not(None):
actualGrblVer += '-dirty'
# compare actual and required grbl version
self._requiredGrblVer = requiredGrblVer
self._actualGrblVer = actualGrblVer
print repr(requiredGrblVer)
print repr(actualGrblVer)
if requiredGrblVer != actualGrblVer:
self._log("unsupported grbl version detected...")
self._log("required: " + requiredGrblVer)
self._log("detected: " + actualGrblVer)
return False
else:
return True
def _flashGrbl(self):
self._changeState(self.STATE_FLASHING)
self._serial.close()
cwd = os.path.dirname(__file__)
pathToGrblHex = cwd + "/../grbl/grbl.hex"
# TODO check if avrdude is installed.
# TODO log in logfile as well, not only to the serial monitor (use self._logger.info()... )
params = ["avrdude", "-patmega328p", "-carduino", "-b" + str(self._baudrate), "-P" + str(self._port), "-D", "-Uflash:w:" + pathToGrblHex]
rc = subprocesscall(params)
if rc is False:
self._log("successfully flashed new grbl version")
self._openSerial()
self._changeState(self.STATE_CONNECTING)
else:
self._log("error during flashing of new grbl version")
self._errorValue = "avrdude returncode: %s" % rc
self._changeState(self.STATE_CLOSED_WITH_ERROR)
@staticmethod
def _writeGrblVersionToFile(versionDict):
if versionDict['dirty'] == '-dirty':
versionDict['dirty'] = True
versionDict['lastConnect'] = time.time()
versionFile = os.path.join(settings().getBaseFolder("logs"), 'grbl_Version.yml')
with open(versionFile, 'w') as outfile:
outfile.write(yamldump(versionDict, default_flow_style=True))
def sendCommand(self, cmd, cmd_type=None):
pass
def getStateString(self):
if self._state == self.STATE_NONE:
return "Offline"
if self._state == self.STATE_OPEN_SERIAL:
return "Opening serial port"
if self._state == self.STATE_DETECT_SERIAL:
return "Detecting serial port"
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_PAUSED:
return "Paused"
if self._state == self.STATE_CLOSED:
return "Closed"
if self._state == self.STATE_ERROR:
return "Error: %s" % (self.getErrorString())
if self._state == self.STATE_CLOSED_WITH_ERROR:
return "Error: %s" % (self.getErrorString())
if self._state == self.STATE_LOCKED:
return "Locked"
if self._state == self.STATE_HOMING:
return "Homing"
if self._state == self.STATE_FLASHING:
return "Flashing"
return "?%d?" % (self._state)
def isOperational(self):
return self._state == self.STATE_OPERATIONAL or self._state == self.STATE_PRINTING or self._state == self.STATE_PAUSED
def isPrinting(self):
return self._state == self.STATE_PRINTING
def isPaused(self):
return self._state == self.STATE_PAUSED
def getErrorString(self):
return self._errorValue
def close(self, isError = False):
if self._status_timer is not None:
try:
self._status_timer.cancel()
self._status_timer = None
except AttributeError:
pass
self._monitoring_active = False
self._sending_active = False
printing = self.isPrinting() or self.isPaused()
if self._serial is not None:
if isError:
self._changeState(self.STATE_CLOSED_WITH_ERROR)
else:
self._changeState(self.STATE_CLOSED)
self._serial.close()
self._serial = None
if printing:
payload = None
if self._currentFile is not None:
payload = {
"file": self._currentFile.getFilename(),
"filename": os.path.basename(self._currentFile.getFilename()),
"origin": self._currentFile.getFileLocation()
}
eventManager().fire(Events.PRINT_FAILED, payload)
eventManager().fire(Events.DISCONNECTED)
### MachineCom callback ################################################################################################
class MachineComPrintCallback(object):
def on_comm_log(self, message):
pass
def on_comm_temperature_update(self, temp, bedTemp):
pass
def on_comm_state_change(self, state):
pass
def on_comm_message(self, message):
pass
def on_comm_progress(self):
pass
def on_comm_print_job_done(self):
pass
def on_comm_z_change(self, newZ):
pass
def on_comm_file_selected(self, filename, filesize, sd):
pass
def on_comm_sd_state_change(self, sdReady):
pass
def on_comm_sd_files(self, files):
pass
def on_comm_file_transfer_started(self, filename, filesize):
pass
def on_comm_file_transfer_done(self, filename):
pass
def on_comm_force_disconnect(self):
pass
def on_comm_pos_update(self, MPos, WPos):
pass
def convert_pause_triggers(configured_triggers):
triggers = {
"enable": [],
"disable": [],
"toggle": []
}
for trigger in configured_triggers:
if not "regex" in trigger or not "type" in trigger:
continue
try:
regex = trigger["regex"]
t = trigger["type"]
if t in triggers:
# make sure regex is valid
re.compile(regex)
# add to type list
triggers[t].append(regex)
except re.error:
# invalid regex or something like this, we'll just skip this entry
pass
result = dict()
for t in triggers.keys():
if len(triggers[t]) > 0:
result[t] = re.compile("|".join(map(lambda pattern: "({pattern})".format(pattern=pattern), triggers[t])))
return result
def get_new_timeout(t):
now = time.time()
return now + get_interval(t)
def get_interval(t):
if t not in default_settings["serial"]["timeout"]:
return 0
else:
return settings().getFloat(["serial", "timeout", type])
def serialList():
baselist = [glob.glob("/dev/ttyUSB*"),
+ glob.glob("/dev/ttyACM*"),
+ glob.glob("/dev/tty.usb*"),
+ glob.glob("/dev/cu.*"),
+ glob.glob("/dev/cuaU*"),
+ glob.glob("/dev/rfcomm*")]
additionalPorts = settings().get(["serial", "additionalPorts"])
for additional in additionalPorts:
baselist += glob.glob(additional)
prev = settings().get(["serial", "port"])
if prev in baselist:
baselist.remove(prev)
baselist.insert(0, prev)
if settings().getBoolean(["devel", "virtualPrinter", "enabled"]):
baselist.append("VIRTUAL")
return baselist
def baudrateList():
ret = [250000, 230400, 115200, 57600, 38400, 19200, 9600]
prev = settings().getInt(["serial", "baudrate"])
if prev in ret:
ret.remove(prev)
ret.insert(0, prev)
return ret