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)

master
Gina Häußge 2013-03-16 18:25:39 +01:00
parent 5e22b4b096
commit d6a83d174f
6 changed files with 115 additions and 30 deletions

View File

@ -211,7 +211,8 @@ class Printer():
self._setProgressData(None, None, None)
# mark print as failure
self._gcodeManager.printFailed(self._filename)
if self._filename is not None:
self._gcodeManager.printFailed(self._filename)
#~~ state monitoring
@ -480,7 +481,7 @@ class GcodeLoader(threading.Thread):
def run(self):
#Send an initial M110 to reset the line counter to zero.
prevLineType = lineType = "CUSTOM"
gcodeList = ["M110"]
gcodeList = ["M110 N0"]
filesize = os.stat(self._filename).st_size
with open(self._filename, "r") as file:
for line in file:

View File

@ -360,7 +360,8 @@ def getSettings():
"feature": {
"gcodeViewer": s.getBoolean(["feature", "gCodeVisualizer"]),
"waitForStart": s.getBoolean(["feature", "waitForStartOnConnect"]),
"alwaysSendChecksum": s.getBoolean(["feature", "alwaysSendChecksum"])
"alwaysSendChecksum": s.getBoolean(["feature", "alwaysSendChecksum"]),
"resetLineNumbersWithPrefixedN": s.getBoolean(["feature", "resetLineNumbersWithPrefixedN"])
},
"folder": {
"uploads": s.getBaseFolder("uploads"),
@ -403,6 +404,7 @@ def setSettings():
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 "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 "uploads" in data["folder"].keys(): s.setBaseFolder("uploads", data["folder"]["uploads"])
@ -521,6 +523,9 @@ class Server():
}
},
"loggers": {
#"octoprint.util.comm": {
# "level": "DEBUG"
#},
"SERIAL": {
"level": "DEBUG",
"handlers": ["serialFile"],

View File

@ -40,7 +40,8 @@ default_settings = {
"feature": {
"gCodeVisualizer": True,
"waitForStartOnConnect": False,
"alwaysSendChecksum": False
"alwaysSendChecksum": False,
"resetLineNumbersWithPrefixedN": False
},
"folder": {
"uploads": None,

View File

@ -960,6 +960,7 @@ function SettingsViewModel() {
self.feature_gcodeViewer = ko.observable(undefined);
self.feature_waitForStart = ko.observable(undefined);
self.feature_alwaysSendChecksum = ko.observable(undefined);
self.feature_resetLineNumbersWithPrefixedN = ko.observable(undefined);
self.folder_uploads = ko.observable(undefined);
self.folder_timelapse = ko.observable(undefined);
@ -1005,6 +1006,7 @@ function SettingsViewModel() {
self.feature_gcodeViewer(response.feature.gcodeViewer);
self.feature_waitForStart(response.feature.waitForStart);
self.feature_alwaysSendChecksum(response.feature.alwaysSendChecksum);
self.feature_resetLineNumbersWithPrefixedN(response.feature.resetLineNumbersWithPrefixedN);
self.folder_uploads(response.folder.uploads);
self.folder_timelapse(response.folder.timelapse);
@ -1038,7 +1040,8 @@ function SettingsViewModel() {
"feature": {
"gcodeViewer": self.feature_gcodeViewer(),
"waitForStart": self.feature_waitForStart(),
"alwaysSendChecksum": self.feature_alwaysSendChecksum()
"alwaysSendChecksum": self.feature_alwaysSendChecksum(),
"resetLineNumbersWithPrefixedN": self.feature_resetLineNumbersWithPrefixedN()
},
"folder": {
"uploads": self.folder_uploads(),

View File

@ -113,6 +113,13 @@
</label>
</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>
</div>
<div class="tab-pane" id="settings_folder">

View File

@ -64,6 +64,8 @@ class VirtualPrinter():
self.bedTemp = 1.0
self.bedTargetTemp = 1.0
self.currentLine = 0
def write(self, data):
if self.readList is None:
return
@ -79,9 +81,18 @@ class VirtualPrinter():
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 "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:
self.readList.append("ok\n")
self.currentLine += 1
def readline(self):
if self.readList is None:
@ -180,7 +191,9 @@ class MachineCom(object):
self._printStartTime100 = None
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.daemon = True
@ -409,22 +422,34 @@ class MachineCom(object):
if line == "" and time.time() > timeout:
self._log("Communication timeout during printing, forcing a line")
line = "ok"
#Even when printing request the temperture every 5 seconds.
#Even when printing request the temperature every 5 seconds.
if time.time() > tempRequestTimeout:
self._commandQueue.put("M105")
tempRequestTimeout = time.time() + 5
if "ok" in line:
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())
else:
self._sendNext()
elif "resend" in line.lower() or "rs" in line:
lineToResend = None
try:
self._gcodePos = int(line.replace("N:"," ").replace("N"," ").replace(":"," ").split()[-1])
lineToResend = int(line.replace("N:"," ").replace("N"," ").replace(":"," ").split()[-1])
except:
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")
def _log(self, message):
@ -467,7 +492,18 @@ class MachineCom(object):
def __del__(self):
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):
cmd = cmd.upper()
if self._serial is None:
@ -485,25 +521,67 @@ class MachineCom(object):
except:
pass
commandToSend = 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:
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:
lineNumber = self._gcodePos
if self._alwaysSendChecksum:
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)
else:
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:
self._serial.write(commandToSend + '\n')
self._serial.write(cmd + '\n')
except serial.SerialTimeoutException:
self._log("Serial timeout while writing to serial port, trying again.")
try:
self._serial.write(commandToSend + '\n')
self._serial.write(cmd + '\n')
except:
self._log("Unexpected error while writing serial port: %s" % (getExceptionString()))
self._errorValue = getExceptionString()
@ -512,15 +590,6 @@ class MachineCom(object):
self._log("Unexpected error while writing serial port: %s" % (getExceptionString()))
self._errorValue = getExceptionString()
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):
if self._gcodePos >= len(self._gcodeList):
@ -561,7 +630,6 @@ class MachineCom(object):
return
self._gcodeList = gcodeList
self._gcodePos = 0
self._currentLine = 0
self._printStartTime100 = None
self._printSection = 'CUSTOM'
self._changeState(self.STATE_PRINTING)