diff --git a/octoprint/server.py b/octoprint/server.py
index adfaadb2..047d1f38 100644
--- a/octoprint/server.py
+++ b/octoprint/server.py
@@ -590,7 +590,8 @@ def getSettings():
"temperatureGraph": s.getBoolean(["feature", "temperatureGraph"]),
"waitForStart": s.getBoolean(["feature", "waitForStartOnConnect"]),
"alwaysSendChecksum": s.getBoolean(["feature", "alwaysSendChecksum"]),
- "sdSupport": s.getBoolean(["feature", "sdSupport"])
+ "sdSupport": s.getBoolean(["feature", "sdSupport"]),
+ "swallowOkAfterResend": s.getBoolean(["feature", "swallowOkAfterResend"])
},
"serial": {
"port": connectionOptions["portPreference"],
@@ -656,6 +657,7 @@ def setSettings():
if "waitForStart" in data["feature"].keys(): s.setBoolean(["feature", "waitForStartOnConnect"], data["feature"]["waitForStart"])
if "alwaysSendChecksum" in data["feature"].keys(): s.setBoolean(["feature", "alwaysSendChecksum"], data["feature"]["alwaysSendChecksum"])
if "sdSupport" in data["feature"].keys(): s.setBoolean(["feature", "sdSupport"], data["feature"]["sdSupport"])
+ if "swallowOkAfterResend" in data["feature"].keys(): s.setBoolean(["feature", "swallowOkAfterResend"], data["feature"]["swallowOkAfterResend"])
if "serial" in data.keys():
if "autoconnect" in data["serial"].keys(): s.setBoolean(["serial", "autoconnect"], data["serial"]["autoconnect"])
diff --git a/octoprint/settings.py b/octoprint/settings.py
index 4cc80222..f95ed104 100644
--- a/octoprint/settings.py
+++ b/octoprint/settings.py
@@ -53,7 +53,8 @@ default_settings = {
"temperatureGraph": True,
"waitForStartOnConnect": False,
"alwaysSendChecksum": False,
- "sdSupport": True
+ "sdSupport": True,
+ "swallowOkAfterResend": False
},
"folder": {
"uploads": None,
@@ -109,7 +110,15 @@ default_settings = {
"terminalFilters": [
{ "name": "Suppress M105 requests/responses", "regex": "(Send: M105)|(Recv: ok T:)" },
{ "name": "Suppress M27 requests/responses", "regex": "(Send: M27)|(Recv: SD printing byte)" }
- ]
+ ],
+ "devel": {
+ "virtualPrinter": {
+ "enabled": False,
+ "okAfterResend": False,
+ "forceChecksum": False,
+ "okWithLinenumber": False
+ }
+ }
}
valid_boolean_trues = ["true", "yes", "y", "1"]
diff --git a/octoprint/static/js/app/viewmodels/settings.js b/octoprint/static/js/app/viewmodels/settings.js
index 2790e40d..c8cdd214 100644
--- a/octoprint/static/js/app/viewmodels/settings.js
+++ b/octoprint/static/js/app/viewmodels/settings.js
@@ -31,6 +31,7 @@ function SettingsViewModel(loginStateViewModel, usersViewModel) {
self.feature_waitForStart = ko.observable(undefined);
self.feature_alwaysSendChecksum = ko.observable(undefined);
self.feature_sdSupport = ko.observable(undefined);
+ self.feature_swallowOkAfterResend = ko.observable(undefined);
self.serial_port = ko.observable();
self.serial_baudrate = ko.observable();
@@ -103,6 +104,7 @@ function SettingsViewModel(loginStateViewModel, usersViewModel) {
self.feature_waitForStart(response.feature.waitForStart);
self.feature_alwaysSendChecksum(response.feature.alwaysSendChecksum);
self.feature_sdSupport(response.feature.sdSupport);
+ self.feature_swallowOkAfterResend(response.feature.swallowOkAfterResend);
self.serial_port(response.serial.port);
self.serial_baudrate(response.serial.baudrate);
@@ -156,7 +158,8 @@ function SettingsViewModel(loginStateViewModel, usersViewModel) {
"temperatureGraph": self.feature_temperatureGraph(),
"waitForStart": self.feature_waitForStart(),
"alwaysSendChecksum": self.feature_alwaysSendChecksum(),
- "sdSupport": self.feature_sdSupport()
+ "sdSupport": self.feature_sdSupport(),
+ "swallowOkAfterResend": self.feature_swallowOkAfterResend()
},
"serial": {
"port": self.serial_port(),
diff --git a/octoprint/templates/settings.jinja2 b/octoprint/templates/settings.jinja2
index a015f5bc..f543793d 100644
--- a/octoprint/templates/settings.jinja2
+++ b/octoprint/templates/settings.jinja2
@@ -203,6 +203,13 @@
+
+
+
+
+
diff --git a/octoprint/util/comm.py b/octoprint/util/comm.py
index 65d53690..45882d98 100644
--- a/octoprint/util/comm.py
+++ b/octoprint/util/comm.py
@@ -49,7 +49,7 @@ def serialList():
if prev in baselist:
baselist.remove(prev)
baselist.insert(0, prev)
- if isDevVersion():
+ if settings().getBoolean(["devel", "virtualPrinter", "enabled"]):
baselist.append("VIRTUAL")
return baselist
@@ -121,7 +121,7 @@ class MachineCom(object):
self._heatupWaitTimeLost = 0.0
self._alwaysSendChecksum = settings().getBoolean(["feature", "alwaysSendChecksum"])
- self._currentLine = 0
+ self._currentLine = 1
self._resendDelta = None
self._lastLines = deque([], 50)
@@ -499,6 +499,7 @@ class MachineCom(object):
sdStatusRequestTimeout = timeout
startSeen = not settings().getBoolean(["feature", "waitForStartOnConnect"])
heatingUp = False
+ swallowOk = False
while True:
try:
line = self._readline()
@@ -704,6 +705,8 @@ class MachineCom(object):
tempRequestTimeout = getNewTimeout("communication")
# resend -> start resend procedure from requested line
elif line.lower().startswith("resend") or line.lower().startswith("rs"):
+ if settings().get(["feature", "swallowOkAfterResend"]):
+ swallowOk = True
self._handleResendRequest(line)
### Printing
@@ -729,7 +732,9 @@ class MachineCom(object):
self._commandQueue.put("M105")
tempRequestTimeout = getNewTimeout("communication")
- if 'ok' in line:
+ if "ok" in line and swallowOk:
+ swallowOk = False
+ elif "ok" in line:
timeout = getNewTimeout("communication")
if self._resendDelta is not None:
self._resendNextCommand()
@@ -738,6 +743,8 @@ class MachineCom(object):
else:
self._sendNext()
elif line.lower().startswith("resend") or line.lower().startswith("rs"):
+ if settings().get(["feature", "swallowOkAfterResend"]):
+ swallowOk = True
self._handleResendRequest(line)
except:
self._logger.exception("Something crashed inside the serial connection loop, please report this in OctoPrint's bug tracker:")
@@ -799,7 +806,7 @@ class MachineCom(object):
if lineToResend is not None:
self._resendDelta = self._currentLine - lineToResend
- if self._resendDelta > len(self._lastLines) or len(self._lastLines) == 0:
+ if self._resendDelta > len(self._lastLines) or len(self._lastLines) == 0 or self._resendDelta <= 0:
self._errorValue = "Printer requested line %d but no sufficient history is available, can't resend" % lineToResend
self._logger.warn(self._errorValue)
if self.isPrinting():
diff --git a/octoprint/util/virtual.py b/octoprint/util/virtual.py
index cf699f6c..6a445593 100644
--- a/octoprint/util/virtual.py
+++ b/octoprint/util/virtual.py
@@ -32,6 +32,7 @@ class VirtualPrinter():
self._newSdFilePos = None
self.currentLine = 0
+ self.lastN = 0
waitThread = threading.Thread(target=self._sendWaitAfterTimeout)
waitThread.start()
@@ -40,18 +41,49 @@ class VirtualPrinter():
if self.readList is None:
return
- # strip checksum
data = data.strip()
+
+ # strip checksum
if "*" in data:
data = data[:data.rfind("*")]
self.currentLine += 1
+ elif settings().getBoolean(["devel", "virtualPrinter", "forceChecksum"]):
+ self.readList.append("Error: Missing checksum")
+ return
+
+ # track N = N + 1
+ if data.startswith("N") and "M110" in data:
+ linenumber = int(re.search("N([0-9]+)", data).group(1))
+ self.lastN = linenumber
+ self.currentLine = linenumber
+ return
+ elif data.startswith("N"):
+ linenumber = int(re.search("N([0-9]+)", data).group(1))
+ expected = self.lastN + 1
+ if linenumber != expected:
+ self.readList.append("Error: expected line %d got %d" % (expected, linenumber))
+ self.readList.append("Resend:%d" % expected)
+ if settings().getBoolean(["devel", "virtualPrinter", "okAfterResend"]):
+ self.readList.append("ok")
+ return
+ elif self.currentLine == 100:
+ # simulate a resend at line 100 of the last 5 lines
+ self.lastN = 94
+ self.readList.append("Error: Line Number is not Last Line Number\n")
+ self.readList.append("rs %d\n" % (self.currentLine - 5))
+ if settings().getBoolean(["devel", "virtualPrinter", "okAfterResend"]):
+ self.readList.append("ok")
+ return
+ else:
+ self.lastN = linenumber
+
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")
+ self._sendOk()
return
#print "Send: %s" % (data.rstrip())
@@ -105,11 +137,6 @@ class VirtualPrinter():
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")
@@ -119,19 +146,15 @@ class VirtualPrinter():
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")
+ self._sendOk()
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")
+ self._sendOk()
def _selectSdFile(self, filename):
file = os.path.join(self._virtualSd, filename).lower()
@@ -149,11 +172,11 @@ class VirtualPrinter():
self._sdPrinter = threading.Thread(target=self._sdPrintingWorker)
self._sdPrinter.start()
self._sdPrintingSemaphore.set()
- self.readList.append("ok")
+ self._sendOk()
def _pauseSdPrint(self):
self._sdPrintingSemaphore.clear()
- self.readList.append("ok")
+ self._sendOk()
def _setSdPos(self, pos):
self._newSdFilePos = pos
@@ -175,12 +198,12 @@ class VirtualPrinter():
self._writingToSd = True
self._selectedSdFile = file
self.readList.append("Writing to file: %s" % filename)
- self.readList.append("ok")
+ self._sendOk()
def _finishSdFile(self):
self._writingToSd = False
self._selectedSdFile = None
- self.readList.append("ok")
+ self._sendOk()
def _sdPrintingWorker(self):
self._selectedSdFilePos = 0
@@ -220,7 +243,7 @@ class VirtualPrinter():
file = os.path.join(self._virtualSd, filename)
if os.path.exists(file) and os.path.isfile(file):
os.remove(file)
- self.readList.append("ok")
+ self._sendOk()
def readline(self):
if self.readList is None:
@@ -249,7 +272,14 @@ class VirtualPrinter():
def close(self):
self.readList = None
+ def _sendOk(self):
+ if settings().getBoolean(["devel", "virtualPrinter", "okWithLinenumber"]):
+ self.readList.append("ok %d" % self.lastN)
+ else:
+ self.readList.append("ok")
+
def _sendWaitAfterTimeout(self, timeout=5):
time.sleep(timeout)
- self.readList.append("wait")
+ if self.readList is not None:
+ self.readList.append("wait")