2013-06-29 16:37:47 +00:00
|
|
|
from __future__ import absolute_import
|
|
|
|
# coding=utf-8
|
|
|
|
__author__ = "Gina Häußge <osd@foosel.net>"
|
|
|
|
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
|
|
|
|
|
|
|
|
|
|
|
|
import time
|
|
|
|
import os
|
|
|
|
import re
|
|
|
|
import threading
|
|
|
|
import math
|
|
|
|
|
|
|
|
from octoprint.settings import settings
|
|
|
|
|
|
|
|
class VirtualPrinter():
|
|
|
|
def __init__(self):
|
|
|
|
self.readList = ['start\n', 'Marlin: Virtual Marlin!\n', '\x80\n', 'SD init fail\n'] # no sd card as default startup scenario
|
|
|
|
self.temp = 0.0
|
|
|
|
self.targetTemp = 0.0
|
|
|
|
self.lastTempAt = time.time()
|
|
|
|
self.bedTemp = 1.0
|
|
|
|
self.bedTargetTemp = 1.0
|
|
|
|
|
|
|
|
self._virtualSd = settings().getBaseFolder("virtualSd")
|
|
|
|
self._sdCardReady = False
|
|
|
|
self._sdPrinter = None
|
|
|
|
self._sdPrintingSemaphore = threading.Event()
|
|
|
|
self._selectedSdFile = None
|
|
|
|
self._selectedSdFileSize = None
|
|
|
|
self._selectedSdFilePos = None
|
|
|
|
self._writingToSd = False
|
|
|
|
self._newSdFilePos = None
|
|
|
|
|
|
|
|
self.currentLine = 0
|
|
|
|
|
|
|
|
waitThread = threading.Thread(target=self._sendWaitAfterTimeout)
|
|
|
|
waitThread.start()
|
|
|
|
|
|
|
|
def write(self, data):
|
|
|
|
if self.readList is None:
|
|
|
|
return
|
|
|
|
|
|
|
|
# strip checksum
|
|
|
|
data = data.strip()
|
|
|
|
if "*" in data:
|
|
|
|
data = data[:data.rfind("*")]
|
|
|
|
self.currentLine += 1
|
|
|
|
data += "\n"
|
|
|
|
|
|
|
|
# shortcut for writing to SD
|
|
|
|
if self._writingToSd and not self._selectedSdFile is None and not "M29" in data:
|
|
|
|
with open(self._selectedSdFile, "a") as f:
|
|
|
|
f.write(data)
|
|
|
|
self.readList.append("ok")
|
|
|
|
return
|
|
|
|
|
|
|
|
#print "Send: %s" % (data.rstrip())
|
|
|
|
if 'M104' in data or 'M109' in data:
|
|
|
|
try:
|
|
|
|
self.targetTemp = float(re.search('S([0-9]+)', data).group(1))
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
if 'M140' in data or 'M190' in data:
|
|
|
|
try:
|
|
|
|
self.bedTargetTemp = float(re.search('S([0-9]+)', data).group(1))
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
|
|
|
if 'M105' in data:
|
|
|
|
# send simulated temperature data
|
|
|
|
self.readList.append("ok T:%.2f /%.2f B:%.2f /%.2f @:64\n" % (self.temp, self.targetTemp, self.bedTemp, self.bedTargetTemp))
|
|
|
|
elif 'M20' in data:
|
|
|
|
if self._sdCardReady:
|
|
|
|
self._listSd()
|
|
|
|
elif 'M21' in data:
|
|
|
|
self._sdCardReady = True
|
|
|
|
self.readList.append("SD card ok")
|
|
|
|
elif 'M22' in data:
|
|
|
|
self._sdCardReady = False
|
|
|
|
elif 'M23' in data:
|
|
|
|
if self._sdCardReady:
|
|
|
|
filename = data.split(None, 1)[1].strip()
|
|
|
|
self._selectSdFile(filename)
|
|
|
|
elif 'M24' in data:
|
|
|
|
if self._sdCardReady:
|
|
|
|
self._startSdPrint()
|
|
|
|
elif 'M25' in data:
|
|
|
|
if self._sdCardReady:
|
|
|
|
self._pauseSdPrint()
|
|
|
|
elif 'M26' in data:
|
|
|
|
if self._sdCardReady:
|
|
|
|
pos = int(re.search("S([0-9]+)", data).group(1))
|
|
|
|
self._setSdPos(pos)
|
|
|
|
elif 'M27' in data:
|
|
|
|
if self._sdCardReady:
|
|
|
|
self._reportSdStatus()
|
|
|
|
elif 'M28' in data:
|
|
|
|
if self._sdCardReady:
|
|
|
|
filename = data.split(None, 1)[1].strip()
|
|
|
|
self._writeSdFile(filename)
|
|
|
|
elif 'M29' in data:
|
|
|
|
if self._sdCardReady:
|
|
|
|
self._finishSdFile()
|
|
|
|
elif 'M30' in data:
|
|
|
|
if self._sdCardReady:
|
|
|
|
filename = data.split(None, 1)[1].strip()
|
|
|
|
self._deleteSdFile(filename)
|
|
|
|
elif "M110" in data:
|
|
|
|
# reset current line
|
|
|
|
self.currentLine = int(re.search('^N([0-9]+)', data).group(1))
|
|
|
|
self.readList.append("reset line to %r\n" % self.currentLine)
|
|
|
|
self.readList.append("ok\n")
|
|
|
|
elif "M114" in data:
|
|
|
|
# send dummy position report
|
|
|
|
self.readList.append("ok C: X:10.00 Y:3.20 Z:5.20 E:1.24")
|
2013-07-08 16:01:10 +00:00
|
|
|
elif "M117" in data:
|
|
|
|
# we'll just use this to echo a message, to allow playing around with pause triggers
|
|
|
|
self.readList.append("ok %s" % re.search("M117\s+(.*)", data).group(1))
|
2013-06-29 16:37:47 +00:00
|
|
|
elif "M999" in data:
|
|
|
|
# mirror Marlin behaviour
|
|
|
|
self.readList.append("Resend: 1")
|
|
|
|
elif self.currentLine == 100:
|
|
|
|
# simulate a resend at line 100 of the last 5 lines
|
|
|
|
self.readList.append("Error: Line Number is not Last Line Number\n")
|
|
|
|
self.readList.append("rs %d\n" % (self.currentLine - 5))
|
|
|
|
elif len(data.strip()) > 0:
|
|
|
|
self.readList.append("ok\n")
|
|
|
|
|
|
|
|
def _listSd(self):
|
|
|
|
self.readList.append("Begin file list")
|
|
|
|
for osFile in os.listdir(self._virtualSd):
|
|
|
|
self.readList.append(osFile.upper())
|
|
|
|
self.readList.append("End file list")
|
|
|
|
self.readList.append("ok")
|
|
|
|
|
|
|
|
def _selectSdFile(self, filename):
|
|
|
|
file = os.path.join(self._virtualSd, filename).lower()
|
|
|
|
if not os.path.exists(file) or not os.path.isfile(file):
|
|
|
|
self.readList.append("open failed, File: %s." % filename)
|
|
|
|
else:
|
|
|
|
self._selectedSdFile = file
|
|
|
|
self._selectedSdFileSize = os.stat(file).st_size
|
|
|
|
self.readList.append("File opened: %s Size: %d" % (filename, self._selectedSdFileSize))
|
|
|
|
self.readList.append("File selected")
|
|
|
|
|
|
|
|
def _startSdPrint(self):
|
|
|
|
if self._selectedSdFile is not None:
|
|
|
|
if self._sdPrinter is None:
|
|
|
|
self._sdPrinter = threading.Thread(target=self._sdPrintingWorker)
|
|
|
|
self._sdPrinter.start()
|
|
|
|
self._sdPrintingSemaphore.set()
|
|
|
|
self.readList.append("ok")
|
|
|
|
|
|
|
|
def _pauseSdPrint(self):
|
|
|
|
self._sdPrintingSemaphore.clear()
|
|
|
|
self.readList.append("ok")
|
|
|
|
|
|
|
|
def _setSdPos(self, pos):
|
|
|
|
self._newSdFilePos = pos
|
|
|
|
|
|
|
|
def _reportSdStatus(self):
|
|
|
|
if self._sdPrinter is not None and self._sdPrintingSemaphore.is_set:
|
|
|
|
self.readList.append("SD printing byte %d/%d" % (self._selectedSdFilePos, self._selectedSdFileSize))
|
|
|
|
else:
|
|
|
|
self.readList.append("Not SD printing")
|
|
|
|
|
|
|
|
def _writeSdFile(self, filename):
|
|
|
|
file = os.path.join(self._virtualSd, filename).lower()
|
|
|
|
if os.path.exists(file):
|
|
|
|
if os.path.isfile(file):
|
|
|
|
os.remove(file)
|
|
|
|
else:
|
|
|
|
self.readList.append("error writing to file")
|
|
|
|
|
|
|
|
self._writingToSd = True
|
|
|
|
self._selectedSdFile = file
|
|
|
|
self.readList.append("Writing to file: %s" % filename)
|
|
|
|
self.readList.append("ok")
|
|
|
|
|
|
|
|
def _finishSdFile(self):
|
|
|
|
self._writingToSd = False
|
|
|
|
self._selectedSdFile = None
|
|
|
|
self.readList.append("ok")
|
|
|
|
|
|
|
|
def _sdPrintingWorker(self):
|
|
|
|
self._selectedSdFilePos = 0
|
|
|
|
with open(self._selectedSdFile, "r") as f:
|
|
|
|
for line in f:
|
|
|
|
# reset position if requested by client
|
|
|
|
if self._newSdFilePos is not None:
|
|
|
|
f.seek(self._newSdFilePos)
|
|
|
|
self._newSdFilePos = None
|
|
|
|
|
|
|
|
# read current file position
|
|
|
|
self._selectedSdFilePos = f.tell()
|
|
|
|
|
|
|
|
# if we are paused, wait for unpausing
|
|
|
|
self._sdPrintingSemaphore.wait()
|
|
|
|
|
|
|
|
# set target temps
|
|
|
|
if 'M104' in line or 'M109' in line:
|
|
|
|
try:
|
|
|
|
self.targetTemp = float(re.search('S([0-9]+)', line).group(1))
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
if 'M140' in line or 'M190' in line:
|
|
|
|
try:
|
|
|
|
self.bedTargetTemp = float(re.search('S([0-9]+)', line).group(1))
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
|
|
|
time.sleep(0.01)
|
|
|
|
|
|
|
|
self._sdPrintingSemaphore.clear()
|
|
|
|
self._selectedSdFilePos = 0
|
|
|
|
self._sdPrinter = None
|
|
|
|
self.readList.append("Done printing file")
|
|
|
|
|
|
|
|
def _deleteSdFile(self, filename):
|
|
|
|
file = os.path.join(self._virtualSd, filename)
|
|
|
|
if os.path.exists(file) and os.path.isfile(file):
|
|
|
|
os.remove(file)
|
|
|
|
self.readList.append("ok")
|
|
|
|
|
|
|
|
def readline(self):
|
|
|
|
if self.readList is None:
|
|
|
|
return ''
|
|
|
|
n = 0
|
|
|
|
timeDiff = self.lastTempAt - time.time()
|
|
|
|
self.lastTempAt = time.time()
|
|
|
|
if abs(self.temp - self.targetTemp) > 1:
|
|
|
|
self.temp += math.copysign(timeDiff * 10, self.targetTemp - self.temp)
|
|
|
|
if self.temp < 0:
|
|
|
|
self.temp = 0
|
|
|
|
if abs(self.bedTemp - self.bedTargetTemp) > 1:
|
|
|
|
self.bedTemp += math.copysign(timeDiff * 10, self.bedTargetTemp - self.bedTemp)
|
|
|
|
if self.bedTemp < 0:
|
|
|
|
self.bedTemp = 0
|
|
|
|
while len(self.readList) < 1:
|
|
|
|
time.sleep(0.1)
|
|
|
|
n += 1
|
|
|
|
if n == 20:
|
|
|
|
return ''
|
|
|
|
if self.readList is None:
|
|
|
|
return ''
|
|
|
|
time.sleep(0.001)
|
|
|
|
return self.readList.pop(0)
|
|
|
|
|
|
|
|
def close(self):
|
|
|
|
self.readList = None
|
|
|
|
|
|
|
|
def _sendWaitAfterTimeout(self, timeout=5):
|
|
|
|
time.sleep(timeout)
|
|
|
|
self.readList.append("wait")
|
|
|
|
|