Overhauled resend handling to also work with alwaysSendChecksum feature. Also introduced new feature flag resetLineNumbersWithPrefixedN to make M110 commands send the target line number as part of their N prefix (Repetier), not as a separate N parameter (Marlin & co)
parent
5e22b4b096
commit
d6a83d174f
|
@ -211,7 +211,8 @@ class Printer():
|
||||||
self._setProgressData(None, None, None)
|
self._setProgressData(None, None, None)
|
||||||
|
|
||||||
# mark print as failure
|
# mark print as failure
|
||||||
self._gcodeManager.printFailed(self._filename)
|
if self._filename is not None:
|
||||||
|
self._gcodeManager.printFailed(self._filename)
|
||||||
|
|
||||||
#~~ state monitoring
|
#~~ state monitoring
|
||||||
|
|
||||||
|
@ -480,7 +481,7 @@ class GcodeLoader(threading.Thread):
|
||||||
def run(self):
|
def run(self):
|
||||||
#Send an initial M110 to reset the line counter to zero.
|
#Send an initial M110 to reset the line counter to zero.
|
||||||
prevLineType = lineType = "CUSTOM"
|
prevLineType = lineType = "CUSTOM"
|
||||||
gcodeList = ["M110"]
|
gcodeList = ["M110 N0"]
|
||||||
filesize = os.stat(self._filename).st_size
|
filesize = os.stat(self._filename).st_size
|
||||||
with open(self._filename, "r") as file:
|
with open(self._filename, "r") as file:
|
||||||
for line in file:
|
for line in file:
|
||||||
|
|
|
@ -360,7 +360,8 @@ def getSettings():
|
||||||
"feature": {
|
"feature": {
|
||||||
"gcodeViewer": s.getBoolean(["feature", "gCodeVisualizer"]),
|
"gcodeViewer": s.getBoolean(["feature", "gCodeVisualizer"]),
|
||||||
"waitForStart": s.getBoolean(["feature", "waitForStartOnConnect"]),
|
"waitForStart": s.getBoolean(["feature", "waitForStartOnConnect"]),
|
||||||
"alwaysSendChecksum": s.getBoolean(["feature", "alwaysSendChecksum"])
|
"alwaysSendChecksum": s.getBoolean(["feature", "alwaysSendChecksum"]),
|
||||||
|
"resetLineNumbersWithPrefixedN": s.getBoolean(["feature", "resetLineNumbersWithPrefixedN"])
|
||||||
},
|
},
|
||||||
"folder": {
|
"folder": {
|
||||||
"uploads": s.getBaseFolder("uploads"),
|
"uploads": s.getBaseFolder("uploads"),
|
||||||
|
@ -403,6 +404,7 @@ def setSettings():
|
||||||
if "gcodeViewer" in data["feature"].keys(): s.setBoolean(["feature", "gCodeVisualizer"], data["feature"]["gcodeViewer"])
|
if "gcodeViewer" in data["feature"].keys(): s.setBoolean(["feature", "gCodeVisualizer"], data["feature"]["gcodeViewer"])
|
||||||
if "waitForStart" in data["feature"].keys(): s.setBoolean(["feature", "waitForStartOnConnect"], data["feature"]["waitForStart"])
|
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 "alwaysSendChecksum" in data["feature"].keys(): s.setBoolean(["feature", "alwaysSendChecksum"], data["feature"]["alwaysSendChecksum"])
|
||||||
|
if "resetLineNumbersWithPrefixedN" in data["feature"].keys(): s.setBoolean(["feature", "resetLineNumbersWithPrefixedN"], data["feature"]["resetLineNumbersWithPrefixedN"])
|
||||||
|
|
||||||
if "folder" in data.keys():
|
if "folder" in data.keys():
|
||||||
if "uploads" in data["folder"].keys(): s.setBaseFolder("uploads", data["folder"]["uploads"])
|
if "uploads" in data["folder"].keys(): s.setBaseFolder("uploads", data["folder"]["uploads"])
|
||||||
|
@ -521,6 +523,9 @@ class Server():
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"loggers": {
|
"loggers": {
|
||||||
|
#"octoprint.util.comm": {
|
||||||
|
# "level": "DEBUG"
|
||||||
|
#},
|
||||||
"SERIAL": {
|
"SERIAL": {
|
||||||
"level": "DEBUG",
|
"level": "DEBUG",
|
||||||
"handlers": ["serialFile"],
|
"handlers": ["serialFile"],
|
||||||
|
|
|
@ -40,7 +40,8 @@ default_settings = {
|
||||||
"feature": {
|
"feature": {
|
||||||
"gCodeVisualizer": True,
|
"gCodeVisualizer": True,
|
||||||
"waitForStartOnConnect": False,
|
"waitForStartOnConnect": False,
|
||||||
"alwaysSendChecksum": False
|
"alwaysSendChecksum": False,
|
||||||
|
"resetLineNumbersWithPrefixedN": False
|
||||||
},
|
},
|
||||||
"folder": {
|
"folder": {
|
||||||
"uploads": None,
|
"uploads": None,
|
||||||
|
|
|
@ -960,6 +960,7 @@ function SettingsViewModel() {
|
||||||
self.feature_gcodeViewer = ko.observable(undefined);
|
self.feature_gcodeViewer = ko.observable(undefined);
|
||||||
self.feature_waitForStart = ko.observable(undefined);
|
self.feature_waitForStart = ko.observable(undefined);
|
||||||
self.feature_alwaysSendChecksum = ko.observable(undefined);
|
self.feature_alwaysSendChecksum = ko.observable(undefined);
|
||||||
|
self.feature_resetLineNumbersWithPrefixedN = ko.observable(undefined);
|
||||||
|
|
||||||
self.folder_uploads = ko.observable(undefined);
|
self.folder_uploads = ko.observable(undefined);
|
||||||
self.folder_timelapse = ko.observable(undefined);
|
self.folder_timelapse = ko.observable(undefined);
|
||||||
|
@ -1005,6 +1006,7 @@ function SettingsViewModel() {
|
||||||
self.feature_gcodeViewer(response.feature.gcodeViewer);
|
self.feature_gcodeViewer(response.feature.gcodeViewer);
|
||||||
self.feature_waitForStart(response.feature.waitForStart);
|
self.feature_waitForStart(response.feature.waitForStart);
|
||||||
self.feature_alwaysSendChecksum(response.feature.alwaysSendChecksum);
|
self.feature_alwaysSendChecksum(response.feature.alwaysSendChecksum);
|
||||||
|
self.feature_resetLineNumbersWithPrefixedN(response.feature.resetLineNumbersWithPrefixedN);
|
||||||
|
|
||||||
self.folder_uploads(response.folder.uploads);
|
self.folder_uploads(response.folder.uploads);
|
||||||
self.folder_timelapse(response.folder.timelapse);
|
self.folder_timelapse(response.folder.timelapse);
|
||||||
|
@ -1038,7 +1040,8 @@ function SettingsViewModel() {
|
||||||
"feature": {
|
"feature": {
|
||||||
"gcodeViewer": self.feature_gcodeViewer(),
|
"gcodeViewer": self.feature_gcodeViewer(),
|
||||||
"waitForStart": self.feature_waitForStart(),
|
"waitForStart": self.feature_waitForStart(),
|
||||||
"alwaysSendChecksum": self.feature_alwaysSendChecksum()
|
"alwaysSendChecksum": self.feature_alwaysSendChecksum(),
|
||||||
|
"resetLineNumbersWithPrefixedN": self.feature_resetLineNumbersWithPrefixedN()
|
||||||
},
|
},
|
||||||
"folder": {
|
"folder": {
|
||||||
"uploads": self.folder_uploads(),
|
"uploads": self.folder_uploads(),
|
||||||
|
|
|
@ -113,6 +113,13 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="control-group">
|
||||||
|
<div class="controls">
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox" data-bind="checked: feature_resetLineNumbersWithPrefixedN" id="settings-resetLineNumbersWithPrefixedN"> Send M110 commands with target line number as N-prefix
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" id="settings_folder">
|
<div class="tab-pane" id="settings_folder">
|
||||||
|
|
|
@ -64,6 +64,8 @@ class VirtualPrinter():
|
||||||
self.bedTemp = 1.0
|
self.bedTemp = 1.0
|
||||||
self.bedTargetTemp = 1.0
|
self.bedTargetTemp = 1.0
|
||||||
|
|
||||||
|
self.currentLine = 0
|
||||||
|
|
||||||
def write(self, data):
|
def write(self, data):
|
||||||
if self.readList is None:
|
if self.readList is None:
|
||||||
return
|
return
|
||||||
|
@ -79,9 +81,18 @@ class VirtualPrinter():
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
if 'M105' in data:
|
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))
|
self.readList.append("ok T:%.2f /%.2f B:%.2f /%.2f @:64\n" % (self.temp, self.targetTemp, self.bedTemp, self.bedTargetTemp))
|
||||||
|
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
|
||||||
|
self.readList.append("rs %d\n" % (self.currentLine - 5))
|
||||||
elif len(data.strip()) > 0:
|
elif len(data.strip()) > 0:
|
||||||
self.readList.append("ok\n")
|
self.readList.append("ok\n")
|
||||||
|
self.currentLine += 1
|
||||||
|
|
||||||
def readline(self):
|
def readline(self):
|
||||||
if self.readList is None:
|
if self.readList is None:
|
||||||
|
@ -180,7 +191,9 @@ class MachineCom(object):
|
||||||
self._printStartTime100 = None
|
self._printStartTime100 = None
|
||||||
|
|
||||||
self._alwaysSendChecksum = settings().getBoolean(["feature", "alwaysSendChecksum"])
|
self._alwaysSendChecksum = settings().getBoolean(["feature", "alwaysSendChecksum"])
|
||||||
self._currentLine = 0
|
self._currentLine = 1
|
||||||
|
self._resendDelta = None
|
||||||
|
self._lastLines = []
|
||||||
|
|
||||||
self.thread = threading.Thread(target=self._monitor)
|
self.thread = threading.Thread(target=self._monitor)
|
||||||
self.thread.daemon = True
|
self.thread.daemon = True
|
||||||
|
@ -409,22 +422,34 @@ class MachineCom(object):
|
||||||
if line == "" and time.time() > timeout:
|
if line == "" and time.time() > timeout:
|
||||||
self._log("Communication timeout during printing, forcing a line")
|
self._log("Communication timeout during printing, forcing a line")
|
||||||
line = "ok"
|
line = "ok"
|
||||||
#Even when printing request the temperture every 5 seconds.
|
#Even when printing request the temperature every 5 seconds.
|
||||||
if time.time() > tempRequestTimeout:
|
if time.time() > tempRequestTimeout:
|
||||||
self._commandQueue.put("M105")
|
self._commandQueue.put("M105")
|
||||||
tempRequestTimeout = time.time() + 5
|
tempRequestTimeout = time.time() + 5
|
||||||
if "ok" in line:
|
if "ok" in line:
|
||||||
timeout = time.time() + 5
|
timeout = time.time() + 5
|
||||||
if not self._commandQueue.empty():
|
if self._resendDelta is not None:
|
||||||
|
self._resendNextCommand()
|
||||||
|
elif not self._commandQueue.empty():
|
||||||
self._sendCommand(self._commandQueue.get())
|
self._sendCommand(self._commandQueue.get())
|
||||||
else:
|
else:
|
||||||
self._sendNext()
|
self._sendNext()
|
||||||
elif "resend" in line.lower() or "rs" in line:
|
elif "resend" in line.lower() or "rs" in line:
|
||||||
|
lineToResend = None
|
||||||
try:
|
try:
|
||||||
self._gcodePos = int(line.replace("N:"," ").replace("N"," ").replace(":"," ").split()[-1])
|
lineToResend = int(line.replace("N:"," ").replace("N"," ").replace(":"," ").split()[-1])
|
||||||
except:
|
except:
|
||||||
if "rs" in line:
|
if "rs" in line:
|
||||||
self._gcodePos = int(line.split()[1])
|
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()
|
||||||
self._log("Connection closed, closing down monitor")
|
self._log("Connection closed, closing down monitor")
|
||||||
|
|
||||||
def _log(self, message):
|
def _log(self, message):
|
||||||
|
@ -468,6 +493,17 @@ class MachineCom(object):
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
|
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
|
||||||
|
if self._resendDelta < 0:
|
||||||
|
self._resendDelta = None
|
||||||
|
|
||||||
def _sendCommand(self, cmd, sendChecksum=False):
|
def _sendCommand(self, cmd, sendChecksum=False):
|
||||||
cmd = cmd.upper()
|
cmd = cmd.upper()
|
||||||
if self._serial is None:
|
if self._serial is None:
|
||||||
|
@ -485,25 +521,67 @@ class MachineCom(object):
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
commandToSend = cmd
|
|
||||||
if "M110" in cmd:
|
if "M110" in cmd:
|
||||||
pass
|
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
|
||||||
else:
|
else:
|
||||||
self._currentLine += 1
|
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):
|
||||||
if sendChecksum or self._alwaysSendChecksum:
|
if sendChecksum or self._alwaysSendChecksum:
|
||||||
lineNumber = self._gcodePos
|
|
||||||
if self._alwaysSendChecksum:
|
if self._alwaysSendChecksum:
|
||||||
lineNumber = self._currentLine
|
lineNumber = self._currentLine
|
||||||
checksum = reduce(lambda x,y:x^y, map(ord, "N%d%s" % (lineNumber, cmd)))
|
else:
|
||||||
commandToSend = "N%d%s*%d" % (lineNumber, cmd, checksum)
|
lineNumber = self._gcodePos
|
||||||
|
self._doSendWithChecksum(cmd, lineNumber)
|
||||||
|
self._addToLastLines(cmd)
|
||||||
|
self._currentLine += 1
|
||||||
|
else:
|
||||||
|
self._doSendWithoutChecksum(cmd)
|
||||||
|
|
||||||
self._log('Send: %s' % (commandToSend))
|
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)
|
||||||
|
|
||||||
|
def _doSendWithoutChecksum(self, cmd):
|
||||||
|
self._log("Send: %s" % cmd)
|
||||||
try:
|
try:
|
||||||
self._serial.write(commandToSend + '\n')
|
self._serial.write(cmd + '\n')
|
||||||
except serial.SerialTimeoutException:
|
except serial.SerialTimeoutException:
|
||||||
self._log("Serial timeout while writing to serial port, trying again.")
|
self._log("Serial timeout while writing to serial port, trying again.")
|
||||||
try:
|
try:
|
||||||
self._serial.write(commandToSend + '\n')
|
self._serial.write(cmd + '\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._errorValue = getExceptionString()
|
||||||
|
@ -512,15 +590,6 @@ class MachineCom(object):
|
||||||
self._log("Unexpected error while writing serial port: %s" % (getExceptionString()))
|
self._log("Unexpected error while writing serial port: %s" % (getExceptionString()))
|
||||||
self._errorValue = getExceptionString()
|
self._errorValue = getExceptionString()
|
||||||
self.close(True)
|
self.close(True)
|
||||||
finally:
|
|
||||||
if "M110" in cmd:
|
|
||||||
if " N" in cmd:
|
|
||||||
try:
|
|
||||||
self._currentLine = int(re.search("N([0-9]+)", cmd).group(1))
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self._currentLine = 0
|
|
||||||
|
|
||||||
def _sendNext(self):
|
def _sendNext(self):
|
||||||
if self._gcodePos >= len(self._gcodeList):
|
if self._gcodePos >= len(self._gcodeList):
|
||||||
|
@ -561,7 +630,6 @@ class MachineCom(object):
|
||||||
return
|
return
|
||||||
self._gcodeList = gcodeList
|
self._gcodeList = gcodeList
|
||||||
self._gcodePos = 0
|
self._gcodePos = 0
|
||||||
self._currentLine = 0
|
|
||||||
self._printStartTime100 = None
|
self._printStartTime100 = None
|
||||||
self._printSection = 'CUSTOM'
|
self._printSection = 'CUSTOM'
|
||||||
self._changeState(self.STATE_PRINTING)
|
self._changeState(self.STATE_PRINTING)
|
||||||
|
|
Loading…
Reference in New Issue