Changed to GCODE streaming instead of loading it all into memory -- no more loading times. Also better file handling.
parent
8d53f313fe
commit
56cb1c294c
|
@ -57,18 +57,17 @@ class Printer():
|
||||||
self._printTime = None
|
self._printTime = None
|
||||||
self._printTimeLeft = None
|
self._printTimeLeft = None
|
||||||
|
|
||||||
# gcode handling
|
self._printAfterSelect = False
|
||||||
self._gcodeList = None
|
|
||||||
self._filename = None
|
|
||||||
self._gcodeLoader = None
|
|
||||||
|
|
||||||
# sd handling
|
# sd handling
|
||||||
self._sdPrinting = False
|
self._sdPrinting = False
|
||||||
|
self._sdStreaming = False
|
||||||
|
|
||||||
|
# TODO Still needed?
|
||||||
self._sdFile = None
|
self._sdFile = None
|
||||||
self._sdStreamer = None
|
self._sdStreamer = None
|
||||||
|
|
||||||
# feedrate
|
self._selectedFile = None
|
||||||
self._feedrateModifierMapping = {"outerWall": "WALL-OUTER", "innerWall": "WALL_INNER", "fill": "FILL", "support": "SUPPORT"}
|
|
||||||
|
|
||||||
# timelapse
|
# timelapse
|
||||||
self._timelapse = None
|
self._timelapse = None
|
||||||
|
@ -89,10 +88,8 @@ class Printer():
|
||||||
)
|
)
|
||||||
self._stateMonitor.reset(
|
self._stateMonitor.reset(
|
||||||
state={"state": None, "stateString": self.getStateString(), "flags": self._getStateFlags()},
|
state={"state": None, "stateString": self.getStateString(), "flags": self._getStateFlags()},
|
||||||
jobData={"filename": None, "lines": None, "estimatedPrintTime": None, "filament": None},
|
jobData={"filename": None, "filesize": None, "estimatedPrintTime": None, "filament": None},
|
||||||
gcodeData={"filename": None, "progress": None},
|
progress={"progress": None, "filepos": None, "printTime": None, "printTimeLeft": None},
|
||||||
sdUploadData={"filename": None, "progress": None},
|
|
||||||
progress={"progress": None, "printTime": None, "printTimeLeft": None},
|
|
||||||
currentZ=None
|
currentZ=None
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -163,52 +160,25 @@ class Printer():
|
||||||
for command in commands:
|
for command in commands:
|
||||||
self._comm.sendCommand(command)
|
self._comm.sendCommand(command)
|
||||||
|
|
||||||
def setFeedrateModifier(self, structure, percentage):
|
def selectFile(self, filename, sd, printAfterSelect=False):
|
||||||
if (not self._feedrateModifierMapping.has_key(structure)) or percentage < 0:
|
if self._comm is not None and (self._comm.isBusy() or self._comm.isStreaming()):
|
||||||
return
|
return
|
||||||
|
|
||||||
self._comm.setFeedrateModifier(self._feedrateModifierMapping[structure], percentage / 100.0)
|
self._printAfterSelect = printAfterSelect
|
||||||
|
self._comm.selectFile(filename, sd)
|
||||||
def loadGcode(self, file, printAfterLoading=False):
|
|
||||||
"""
|
|
||||||
Loads the gcode from the given file as the new print job.
|
|
||||||
Aborts if the printer is currently printing or another gcode file is currently being loaded.
|
|
||||||
"""
|
|
||||||
if (self._comm is not None and self._comm.isPrinting()) or (self._gcodeLoader is not None):
|
|
||||||
return
|
|
||||||
|
|
||||||
self._sdFile = None
|
|
||||||
self._setJobData(None, None)
|
|
||||||
|
|
||||||
onGcodeLoadedCallback = self._onGcodeLoaded
|
|
||||||
if printAfterLoading:
|
|
||||||
onGcodeLoadedCallback = self._onGcodeLoadedToPrint
|
|
||||||
|
|
||||||
self._gcodeLoader = GcodeLoader(file, self._onGcodeLoadingProgress, onGcodeLoadedCallback)
|
|
||||||
self._gcodeLoader.start()
|
|
||||||
|
|
||||||
self._stateMonitor.setState({"state": self._state, "stateString": self.getStateString(), "flags": self._getStateFlags()})
|
|
||||||
|
|
||||||
def startPrint(self):
|
def startPrint(self):
|
||||||
"""
|
"""
|
||||||
Starts the currently loaded print job.
|
Starts the currently loaded print job.
|
||||||
Only starts if the printer is connected and operational, not currently printing and a printjob is loaded
|
Only starts if the printer is connected and operational, not currently printing and a printjob is loaded
|
||||||
"""
|
"""
|
||||||
if self._comm is None or not self._comm.isOperational():
|
if self._comm is None or not self._comm.isOperational() or self._comm.isPrinting():
|
||||||
return
|
return
|
||||||
if self._gcodeList is None and self._sdFile is None:
|
if self._selectedFile is None:
|
||||||
return
|
|
||||||
if self._comm.isPrinting():
|
|
||||||
return
|
return
|
||||||
|
|
||||||
self._setCurrentZ(-1)
|
self._setCurrentZ(-1)
|
||||||
if self._sdFile is not None:
|
self._comm.startPrint()
|
||||||
# we are working in sd mode
|
|
||||||
self._sdPrinting = True
|
|
||||||
self._comm.printSdFile()
|
|
||||||
else:
|
|
||||||
# we are working in local mode
|
|
||||||
self._comm.printGCode(self._gcodeList)
|
|
||||||
|
|
||||||
def togglePausePrint(self):
|
def togglePausePrint(self):
|
||||||
"""
|
"""
|
||||||
|
@ -216,6 +186,7 @@ class Printer():
|
||||||
"""
|
"""
|
||||||
if self._comm is None:
|
if self._comm is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
self._comm.setPause(not self._comm.isPaused())
|
self._comm.setPause(not self._comm.isPaused())
|
||||||
|
|
||||||
def cancelPrint(self, disableMotorsAndHeater=True):
|
def cancelPrint(self, disableMotorsAndHeater=True):
|
||||||
|
@ -225,22 +196,18 @@ class Printer():
|
||||||
if self._comm is None:
|
if self._comm is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self._sdPrinting:
|
|
||||||
self._sdPrinting = False
|
|
||||||
self._comm.cancelPrint()
|
self._comm.cancelPrint()
|
||||||
|
|
||||||
if disableMotorsAndHeater:
|
if disableMotorsAndHeater:
|
||||||
self.commands(["M84", "M104 S0", "M140 S0", "M106 S0"]) # disable motors, switch off heaters and fan
|
self.commands(["M84", "M104 S0", "M140 S0", "M106 S0"]) # disable motors, switch off heaters and fan
|
||||||
|
|
||||||
# reset line, height, print time
|
# reset progress, height, print time
|
||||||
self._setCurrentZ(None)
|
self._setCurrentZ(None)
|
||||||
self._setProgressData(None, None, None, None)
|
self._setProgressData(None, None, None, None)
|
||||||
|
|
||||||
# mark print as failure
|
# mark print as failure
|
||||||
if self._filename is not None:
|
if self._selectedFile is not None:
|
||||||
self._gcodeManager.printFailed(self._filename)
|
self._gcodeManager.printFailed(self._selectedFile["filename"])
|
||||||
|
|
||||||
#~~ state monitoring
|
|
||||||
|
|
||||||
def setTimelapse(self, timelapse):
|
def setTimelapse(self, timelapse):
|
||||||
if self._timelapse is not None and self.isPrinting():
|
if self._timelapse is not None and self.isPrinting():
|
||||||
|
@ -251,6 +218,8 @@ class Printer():
|
||||||
def getTimelapse(self):
|
def getTimelapse(self):
|
||||||
return self._timelapse
|
return self._timelapse
|
||||||
|
|
||||||
|
#~~ state monitoring
|
||||||
|
|
||||||
def _setCurrentZ(self, currentZ):
|
def _setCurrentZ(self, currentZ):
|
||||||
self._currentZ = currentZ
|
self._currentZ = currentZ
|
||||||
|
|
||||||
|
@ -273,7 +242,7 @@ class Printer():
|
||||||
self._messages = self._messages[-300:]
|
self._messages = self._messages[-300:]
|
||||||
self._stateMonitor.addMessage(message)
|
self._stateMonitor.addMessage(message)
|
||||||
|
|
||||||
def _setProgressData(self, progress, currentLine, printTime, printTimeLeft):
|
def _setProgressData(self, progress, filepos, printTime, printTimeLeft):
|
||||||
self._progress = progress
|
self._progress = progress
|
||||||
self._printTime = printTime
|
self._printTime = printTime
|
||||||
self._printTimeLeft = printTimeLeft
|
self._printTimeLeft = printTimeLeft
|
||||||
|
@ -286,7 +255,11 @@ class Printer():
|
||||||
if (self._printTimeLeft):
|
if (self._printTimeLeft):
|
||||||
formattedPrintTimeLeft = util.getFormattedTimeDelta(datetime.timedelta(minutes=self._printTimeLeft))
|
formattedPrintTimeLeft = util.getFormattedTimeDelta(datetime.timedelta(minutes=self._printTimeLeft))
|
||||||
|
|
||||||
self._stateMonitor.setProgress({"progress": self._progress, "currentLine": currentLine, "printTime": formattedPrintTime, "printTimeLeft": formattedPrintTimeLeft})
|
formattedFilePos = None
|
||||||
|
if (filepos):
|
||||||
|
formattedFilePos = util.getFormattedSize(filepos)
|
||||||
|
|
||||||
|
self._stateMonitor.setProgress({"progress": self._progress, "filepos": formattedFilePos, "printTime": formattedPrintTime, "printTimeLeft": formattedPrintTimeLeft})
|
||||||
|
|
||||||
def _addTemperatureData(self, temp, bedTemp, targetTemp, bedTargetTemp):
|
def _addTemperatureData(self, temp, bedTemp, targetTemp, bedTargetTemp):
|
||||||
currentTimeUtc = int(time.time() * 1000)
|
currentTimeUtc = int(time.time() * 1000)
|
||||||
|
@ -310,19 +283,25 @@ class Printer():
|
||||||
|
|
||||||
self._stateMonitor.addTemperature({"currentTime": currentTimeUtc, "temp": self._temp, "bedTemp": self._bedTemp, "targetTemp": self._targetTemp, "targetBedTemp": self._targetBedTemp})
|
self._stateMonitor.addTemperature({"currentTime": currentTimeUtc, "temp": self._temp, "bedTemp": self._bedTemp, "targetTemp": self._targetTemp, "targetBedTemp": self._targetBedTemp})
|
||||||
|
|
||||||
def _setJobData(self, filename, gcodeList):
|
def _setJobData(self, filename, filesize, sd):
|
||||||
self._filename = filename
|
if filename is not None:
|
||||||
self._gcodeList = gcodeList
|
self._selectedFile = {
|
||||||
|
"filename": filename,
|
||||||
lines = None
|
"filesize": filesize,
|
||||||
if self._gcodeList:
|
"sd": sd
|
||||||
lines = len(self._gcodeList)
|
}
|
||||||
|
else:
|
||||||
|
self._selectedFile = None
|
||||||
|
|
||||||
formattedFilename = None
|
formattedFilename = None
|
||||||
|
formattedFilesize = None
|
||||||
estimatedPrintTime = None
|
estimatedPrintTime = None
|
||||||
filament = None
|
filament = None
|
||||||
if self._filename:
|
if filename:
|
||||||
formattedFilename = os.path.basename(self._filename)
|
formattedFilename = os.path.basename(filename)
|
||||||
|
|
||||||
|
if filesize:
|
||||||
|
formattedFilesize = util.getFormattedSize(filesize)
|
||||||
|
|
||||||
fileData = self._gcodeManager.getFileData(filename)
|
fileData = self._gcodeManager.getFileData(filename)
|
||||||
if fileData is not None and "gcodeAnalysis" in fileData.keys():
|
if fileData is not None and "gcodeAnalysis" in fileData.keys():
|
||||||
|
@ -331,7 +310,7 @@ class Printer():
|
||||||
if "filament" in fileData["gcodeAnalysis"].keys():
|
if "filament" in fileData["gcodeAnalysis"].keys():
|
||||||
filament = fileData["gcodeAnalysis"]["filament"]
|
filament = fileData["gcodeAnalysis"]["filament"]
|
||||||
|
|
||||||
self._stateMonitor.setJobData({"filename": formattedFilename, "lines": lines, "estimatedPrintTime": estimatedPrintTime, "filament": filament})
|
self._stateMonitor.setJobData({"filename": formattedFilename, "filesize": formattedFilesize, "estimatedPrintTime": estimatedPrintTime, "filament": filament, "sd": sd})
|
||||||
|
|
||||||
def _sendInitialStateUpdate(self, callback):
|
def _sendInitialStateUpdate(self, callback):
|
||||||
try:
|
try:
|
||||||
|
@ -358,7 +337,6 @@ class Printer():
|
||||||
"printing": self.isPrinting(),
|
"printing": self.isPrinting(),
|
||||||
"closedOrError": self.isClosedOrError(),
|
"closedOrError": self.isClosedOrError(),
|
||||||
"error": self.isError(),
|
"error": self.isError(),
|
||||||
"loading": self.isLoading(),
|
|
||||||
"paused": self.isPaused(),
|
"paused": self.isPaused(),
|
||||||
"ready": self.isReady(),
|
"ready": self.isReady(),
|
||||||
"sdReady": sdReady
|
"sdReady": sdReady
|
||||||
|
@ -386,14 +364,15 @@ class Printer():
|
||||||
if oldState == self._comm.STATE_PRINTING and state != self._comm.STATE_PAUSED:
|
if oldState == self._comm.STATE_PRINTING and state != self._comm.STATE_PAUSED:
|
||||||
self._timelapse.onPrintjobStopped()
|
self._timelapse.onPrintjobStopped()
|
||||||
elif state == self._comm.STATE_PRINTING and oldState != self._comm.STATE_PAUSED:
|
elif state == self._comm.STATE_PRINTING and oldState != self._comm.STATE_PAUSED:
|
||||||
self._timelapse.onPrintjobStarted(self._filename)
|
self._timelapse.onPrintjobStarted(self._selectedFile["filename"])
|
||||||
|
|
||||||
# forward relevant state changes to gcode manager
|
# forward relevant state changes to gcode manager
|
||||||
if self._comm is not None and oldState == self._comm.STATE_PRINTING:
|
if self._comm is not None and oldState == self._comm.STATE_PRINTING:
|
||||||
|
if self._selectedFile is not None:
|
||||||
if state == self._comm.STATE_OPERATIONAL:
|
if state == self._comm.STATE_OPERATIONAL:
|
||||||
self._gcodeManager.printSucceeded(self._filename)
|
self._gcodeManager.printSucceeded(self._selectedFile["filename"])
|
||||||
elif state == self._comm.STATE_CLOSED or state == self._comm.STATE_ERROR or state == self._comm.STATE_CLOSED_WITH_ERROR:
|
elif state == self._comm.STATE_CLOSED or state == self._comm.STATE_ERROR or state == self._comm.STATE_CLOSED_WITH_ERROR:
|
||||||
self._gcodeManager.printFailed(self._filename)
|
self._gcodeManager.printFailed(self._selectedFile["filename"])
|
||||||
self._gcodeManager.resumeAnalysis() # printing done, put those cpu cycles to good use
|
self._gcodeManager.resumeAnalysis() # printing done, put those cpu cycles to good use
|
||||||
elif self._comm is not None and state == self._comm.STATE_PRINTING:
|
elif self._comm is not None and state == self._comm.STATE_PRINTING:
|
||||||
self._gcodeManager.pauseAnalysis() # do not analyse gcode while printing
|
self._gcodeManager.pauseAnalysis() # do not analyse gcode while printing
|
||||||
|
@ -410,25 +389,10 @@ class Printer():
|
||||||
def mcProgress(self):
|
def mcProgress(self):
|
||||||
"""
|
"""
|
||||||
Callback method for the comm object, called upon any change in progress of the printjob.
|
Callback method for the comm object, called upon any change in progress of the printjob.
|
||||||
Triggers storage of new values for printTime, printTimeLeft and the current line.
|
Triggers storage of new values for printTime, printTimeLeft and the current progress.
|
||||||
"""
|
"""
|
||||||
oldProgress = self._progress
|
|
||||||
|
|
||||||
if self._sdPrinting:
|
self._setProgressData(self._comm.getPrintProgress(), self._comm.getPrintFilepos(), self._comm.getPrintTime(), self._comm.getPrintTimeRemainingEstimate())
|
||||||
newLine = None
|
|
||||||
(filePos, fileSize) = self._comm.getSdProgress()
|
|
||||||
if fileSize > 0:
|
|
||||||
newProgress = float(filePos) / float(fileSize)
|
|
||||||
else:
|
|
||||||
newProgress = 0.0
|
|
||||||
else:
|
|
||||||
newLine = self._comm.getPrintPos()
|
|
||||||
if self._gcodeList is not None:
|
|
||||||
newProgress = float(newLine) / float(len(self._gcodeList))
|
|
||||||
else:
|
|
||||||
newProgress = 0.0
|
|
||||||
|
|
||||||
self._setProgressData(newProgress, newLine, self._comm.getPrintTime(), self._comm.getPrintTimeRemainingEstimate())
|
|
||||||
|
|
||||||
def mcZChange(self, newZ):
|
def mcZChange(self, newZ):
|
||||||
"""
|
"""
|
||||||
|
@ -446,33 +410,49 @@ class Printer():
|
||||||
def mcSdFiles(self, files):
|
def mcSdFiles(self, files):
|
||||||
self._sendTriggerUpdateCallbacks("gcodeFiles")
|
self._sendTriggerUpdateCallbacks("gcodeFiles")
|
||||||
|
|
||||||
def mcSdSelected(self, filename, filesize):
|
def mcFileSelected(self, filename, filesize, sd):
|
||||||
self._sdFile = filename
|
self._setJobData(filename, filesize, sd)
|
||||||
|
|
||||||
self._setJobData(filename, None)
|
|
||||||
self._stateMonitor.setState({"state": self._state, "stateString": self.getStateString(), "flags": self._getStateFlags()})
|
self._stateMonitor.setState({"state": self._state, "stateString": self.getStateString(), "flags": self._getStateFlags()})
|
||||||
|
|
||||||
if self._sdPrintAfterSelect:
|
if self._printAfterSelect:
|
||||||
self.startPrint()
|
self.startPrint()
|
||||||
|
|
||||||
def mcSdPrintingDone(self):
|
def mcPrintjobDone(self):
|
||||||
self._sdPrinting = False
|
self._setProgressData(1.0, self._selectedFile["filesize"], self._comm.getPrintTime(), 0)
|
||||||
self._setProgressData(1.0, None, self._comm.getPrintTime(), self._comm.getPrintTimeRemainingEstimate())
|
|
||||||
self._stateMonitor.setState({"state": self._state, "stateString": self.getStateString(), "flags": self._getStateFlags()})
|
self._stateMonitor.setState({"state": self._state, "stateString": self.getStateString(), "flags": self._getStateFlags()})
|
||||||
|
|
||||||
#~~ sd file handling
|
def mcFileTransferStarted(self, filename, filesize):
|
||||||
|
self._sdStreaming = True
|
||||||
|
self._selectedFile = {
|
||||||
|
"filename": filename,
|
||||||
|
"filesize": filesize,
|
||||||
|
"sd": True
|
||||||
|
}
|
||||||
|
|
||||||
|
self._setJobData(filename, filesize, True)
|
||||||
|
self._setProgressData(0.0, 0, 0, None)
|
||||||
|
self._stateMonitor.setState({"state": self._state, "stateString": self.getStateString(), "flags": self._getStateFlags()})
|
||||||
|
|
||||||
|
def mcFileTransferDone(self):
|
||||||
|
self._sdStreaming = False
|
||||||
|
self._selectedFile = None
|
||||||
|
|
||||||
|
self._setCurrentZ(None)
|
||||||
|
self._setJobData(None, None, None)
|
||||||
|
self._setProgressData(None, None, None, None)
|
||||||
|
self._stateMonitor.setState({"state": self._state, "stateString": self.getStateString(), "flags": self._getStateFlags()})
|
||||||
|
|
||||||
|
#~~ sd file handling
|
||||||
|
|
||||||
def getSdFiles(self):
|
def getSdFiles(self):
|
||||||
if self._comm is None:
|
if self._comm is None:
|
||||||
return
|
return
|
||||||
return self._comm.getSdFiles()
|
return self._comm.getSdFiles()
|
||||||
|
|
||||||
def addSdFile(self, filename, file):
|
def addSdFile(self, filename, path):
|
||||||
if not self._comm:
|
if not self._comm or self._comm.isBusy():
|
||||||
return
|
return
|
||||||
|
self._comm.startFileTransfer(path, filename[:8].lower() + ".gco")
|
||||||
self._sdStreamer = SdFileStreamer(self._comm, filename, file, self._onSdFileStreamProgress, self._onSdFileStreamFinish)
|
|
||||||
self._sdStreamer.start()
|
|
||||||
|
|
||||||
def deleteSdFile(self, filename):
|
def deleteSdFile(self, filename):
|
||||||
if not self._comm:
|
if not self._comm:
|
||||||
|
@ -482,13 +462,6 @@ class Printer():
|
||||||
self._sdFile = None
|
self._sdFile = None
|
||||||
self._comm.deleteSdFile(filename)
|
self._comm.deleteSdFile(filename)
|
||||||
|
|
||||||
def selectSdFile(self, filename, printAfterSelect):
|
|
||||||
if not self._comm:
|
|
||||||
return
|
|
||||||
|
|
||||||
self._sdPrintAfterSelect = printAfterSelect
|
|
||||||
self._comm.selectSdFile(filename)
|
|
||||||
|
|
||||||
def initSdCard(self):
|
def initSdCard(self):
|
||||||
if not self._comm:
|
if not self._comm:
|
||||||
return
|
return
|
||||||
|
@ -504,56 +477,8 @@ class Printer():
|
||||||
return
|
return
|
||||||
self._comm.refreshSdFiles()
|
self._comm.refreshSdFiles()
|
||||||
|
|
||||||
#~~ callbacks triggered by sdFileStreamer
|
|
||||||
|
|
||||||
def _onSdFileStreamProgress(self, filename, progress):
|
|
||||||
self._stateMonitor.setSdUploadData({"filename": filename, "progress": progress})
|
|
||||||
|
|
||||||
def _onSdFileStreamFinish(self, filename):
|
|
||||||
self._setCurrentZ(None)
|
|
||||||
self._setProgressData(None, None, None, None)
|
|
||||||
self._sdStreamer = None
|
|
||||||
|
|
||||||
self._stateMonitor.setSdUploadData({"filename": None, "progress": None})
|
|
||||||
self._stateMonitor.setState({"state": self._state, "stateString": self.getStateString(), "flags": self._getStateFlags()})
|
|
||||||
|
|
||||||
#~~ callbacks triggered by gcodeLoader
|
|
||||||
|
|
||||||
def _onGcodeLoadingProgress(self, filename, progress, mode):
|
|
||||||
formattedFilename = None
|
|
||||||
if filename is not None:
|
|
||||||
formattedFilename = os.path.basename(filename)
|
|
||||||
|
|
||||||
self._stateMonitor.setGcodeData({"filename": formattedFilename, "progress": progress, "mode": mode})
|
|
||||||
|
|
||||||
def _onGcodeLoaded(self, filename, gcodeList):
|
|
||||||
self._setJobData(filename, gcodeList)
|
|
||||||
self._setCurrentZ(None)
|
|
||||||
self._setProgressData(None, None, None, None)
|
|
||||||
self._gcodeLoader = None
|
|
||||||
|
|
||||||
self._stateMonitor.setGcodeData({"filename": None, "progress": None})
|
|
||||||
self._stateMonitor.setState({"state": self._state, "stateString": self.getStateString(), "flags": self._getStateFlags()})
|
|
||||||
|
|
||||||
def _onGcodeLoadedToPrint(self, filename, gcodeList):
|
|
||||||
self._onGcodeLoaded(filename, gcodeList)
|
|
||||||
self.startPrint()
|
|
||||||
|
|
||||||
#~~ state reports
|
#~~ state reports
|
||||||
|
|
||||||
def feedrateState(self):
|
|
||||||
if self._comm is not None:
|
|
||||||
feedrateModifiers = self._comm.getFeedrateModifiers()
|
|
||||||
result = {}
|
|
||||||
for structure in self._feedrateModifierMapping.keys():
|
|
||||||
if (feedrateModifiers.has_key(self._feedrateModifierMapping[structure])):
|
|
||||||
result[structure] = int(round(feedrateModifiers[self._feedrateModifierMapping[structure]] * 100))
|
|
||||||
else:
|
|
||||||
result[structure] = 100
|
|
||||||
return result
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def getStateString(self):
|
def getStateString(self):
|
||||||
"""
|
"""
|
||||||
Returns a human readable string corresponding to the current communication state.
|
Returns a human readable string corresponding to the current communication state.
|
||||||
|
@ -579,55 +504,7 @@ class Printer():
|
||||||
return self._comm is not None and self._comm.isError()
|
return self._comm is not None and self._comm.isError()
|
||||||
|
|
||||||
def isReady(self):
|
def isReady(self):
|
||||||
return self._gcodeLoader is None and self._sdStreamer is None and ((self._gcodeList and len(self._gcodeList) > 0) or self._sdFile)
|
return self.isOperational() and not self._comm.isStreaming()
|
||||||
|
|
||||||
def isLoading(self):
|
|
||||||
return self._gcodeLoader is not None or self._sdStreamer is not None
|
|
||||||
|
|
||||||
class GcodeLoader(threading.Thread):
|
|
||||||
"""
|
|
||||||
The GcodeLoader takes care of loading a gcode-File from disk and parsing it into a gcode object in a separate
|
|
||||||
thread while constantly notifying interested listeners about the current progress.
|
|
||||||
The progress is returned as a float value between 0 and 1 which is to be interpreted as the percentage of completion.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, filename, progressCallback, loadedCallback):
|
|
||||||
threading.Thread.__init__(self)
|
|
||||||
|
|
||||||
self._progressCallback = progressCallback
|
|
||||||
self._loadedCallback = loadedCallback
|
|
||||||
|
|
||||||
self._filename = filename
|
|
||||||
self._gcodeList = None
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
#Send an initial M110 to reset the line counter to zero.
|
|
||||||
prevLineType = lineType = "CUSTOM"
|
|
||||||
gcodeList = ["M110 N0"]
|
|
||||||
filesize = os.stat(self._filename).st_size
|
|
||||||
with open(self._filename, "r") as file:
|
|
||||||
for line in file:
|
|
||||||
if line.startswith(";TYPE:"):
|
|
||||||
lineType = line[6:].strip()
|
|
||||||
if ";" in line:
|
|
||||||
line = line[0:line.find(";")]
|
|
||||||
line = line.strip()
|
|
||||||
if len(line) > 0:
|
|
||||||
if prevLineType != lineType:
|
|
||||||
gcodeList.append((line, lineType, ))
|
|
||||||
else:
|
|
||||||
gcodeList.append(line)
|
|
||||||
prevLineType = lineType
|
|
||||||
self._onLoadingProgress(float(file.tell()) / float(filesize))
|
|
||||||
|
|
||||||
self._gcodeList = gcodeList
|
|
||||||
self._loadedCallback(self._filename, self._gcodeList)
|
|
||||||
|
|
||||||
def _onLoadingProgress(self, progress):
|
|
||||||
self._progressCallback(self._filename, progress, "loading")
|
|
||||||
|
|
||||||
def _onParsingProgress(self, progress):
|
|
||||||
self._progressCallback(self._filename, progress, "parsing")
|
|
||||||
|
|
||||||
class SdFileStreamer(threading.Thread):
|
class SdFileStreamer(threading.Thread):
|
||||||
def __init__(self, comm, filename, file, progressCallback, finishCallback):
|
def __init__(self, comm, filename, file, progressCallback, finishCallback):
|
||||||
|
@ -644,7 +521,7 @@ class SdFileStreamer(threading.Thread):
|
||||||
return
|
return
|
||||||
|
|
||||||
name = self._filename[:self._filename.rfind(".")]
|
name = self._filename[:self._filename.rfind(".")]
|
||||||
sdFilename = name[:8] + ".GCO"
|
sdFilename = name[:8].lower() + ".gco"
|
||||||
try:
|
try:
|
||||||
size = os.stat(self._file).st_size
|
size = os.stat(self._file).st_size
|
||||||
with open(self._file, "r") as f:
|
with open(self._file, "r") as f:
|
||||||
|
@ -683,11 +560,9 @@ class StateMonitor(object):
|
||||||
self._worker.daemon = True
|
self._worker.daemon = True
|
||||||
self._worker.start()
|
self._worker.start()
|
||||||
|
|
||||||
def reset(self, state=None, jobData=None, gcodeData=None, sdUploadData=None, progress=None, currentZ=None):
|
def reset(self, state=None, jobData=None, progress=None, currentZ=None):
|
||||||
self.setState(state)
|
self.setState(state)
|
||||||
self.setJobData(jobData)
|
self.setJobData(jobData)
|
||||||
self.setGcodeData(gcodeData)
|
|
||||||
self.setSdUploadData(sdUploadData)
|
|
||||||
self.setProgress(progress)
|
self.setProgress(progress)
|
||||||
self.setCurrentZ(currentZ)
|
self.setCurrentZ(currentZ)
|
||||||
|
|
||||||
|
@ -715,14 +590,6 @@ class StateMonitor(object):
|
||||||
self._jobData = jobData
|
self._jobData = jobData
|
||||||
self._changeEvent.set()
|
self._changeEvent.set()
|
||||||
|
|
||||||
def setGcodeData(self, gcodeData):
|
|
||||||
self._gcodeData = gcodeData
|
|
||||||
self._changeEvent.set()
|
|
||||||
|
|
||||||
def setSdUploadData(self, uploadData):
|
|
||||||
self._sdUploadData = uploadData
|
|
||||||
self._changeEvent.set()
|
|
||||||
|
|
||||||
def setProgress(self, progress):
|
def setProgress(self, progress):
|
||||||
self._progress = progress
|
self._progress = progress
|
||||||
self._changeEvent.set()
|
self._changeEvent.set()
|
||||||
|
@ -746,8 +613,6 @@ class StateMonitor(object):
|
||||||
return {
|
return {
|
||||||
"state": self._state,
|
"state": self._state,
|
||||||
"job": self._jobData,
|
"job": self._jobData,
|
||||||
"gcode": self._gcodeData,
|
|
||||||
"sdUpload": self._sdUploadData,
|
|
||||||
"currentZ": self._currentZ,
|
"currentZ": self._currentZ,
|
||||||
"progress": self._progress
|
"progress": self._progress
|
||||||
}
|
}
|
||||||
|
|
|
@ -232,23 +232,6 @@ def jog():
|
||||||
|
|
||||||
return jsonify(SUCCESS)
|
return jsonify(SUCCESS)
|
||||||
|
|
||||||
@app.route(BASEURL + "control/speed", methods=["GET"])
|
|
||||||
def getSpeedValues():
|
|
||||||
return jsonify(feedrate=printer.feedrateState())
|
|
||||||
|
|
||||||
@app.route(BASEURL + "control/speed", methods=["POST"])
|
|
||||||
@login_required
|
|
||||||
def speed():
|
|
||||||
if not printer.isOperational():
|
|
||||||
return jsonify(SUCCESS)
|
|
||||||
|
|
||||||
for key in ["outerWall", "innerWall", "fill", "support"]:
|
|
||||||
if key in request.values.keys():
|
|
||||||
value = int(request.values[key])
|
|
||||||
printer.setFeedrateModifier(key, value)
|
|
||||||
|
|
||||||
return getSpeedValues()
|
|
||||||
|
|
||||||
@app.route(BASEURL + "control/custom", methods=["GET"])
|
@app.route(BASEURL + "control/custom", methods=["GET"])
|
||||||
def getCustomControls():
|
def getCustomControls():
|
||||||
customControls = settings().get(["controls"])
|
customControls = settings().get(["controls"])
|
||||||
|
@ -312,13 +295,14 @@ def loadGcodeFile():
|
||||||
if "print" in request.values.keys() and request.values["print"] in valid_boolean_trues:
|
if "print" in request.values.keys() and request.values["print"] in valid_boolean_trues:
|
||||||
printAfterLoading = True
|
printAfterLoading = True
|
||||||
|
|
||||||
|
sd = False
|
||||||
|
filename = None
|
||||||
if "target" in request.values.keys() and request.values["target"] == "sd":
|
if "target" in request.values.keys() and request.values["target"] == "sd":
|
||||||
filename = request.values["filename"]
|
filename = request.values["filename"]
|
||||||
printer.selectSdFile(filename, printAfterLoading)
|
sd = True
|
||||||
else:
|
else:
|
||||||
filename = gcodeManager.getAbsolutePath(request.values["filename"])
|
filename = gcodeManager.getAbsolutePath(request.values["filename"])
|
||||||
if filename is not None:
|
printer.selectFile(filename, sd, printAfterLoading)
|
||||||
printer.loadGcode(filename, printAfterLoading)
|
|
||||||
return jsonify(SUCCESS)
|
return jsonify(SUCCESS)
|
||||||
|
|
||||||
@app.route(BASEURL + "gcodefiles/delete", methods=["POST"])
|
@app.route(BASEURL + "gcodefiles/delete", methods=["POST"])
|
||||||
|
|
|
@ -526,3 +526,9 @@ ul.dropdown-menu li a {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-sd-black-14 {
|
||||||
|
background: url("../img/icon-sd-black-14.png") 0 3px no-repeat;
|
||||||
|
width: 11px;
|
||||||
|
height: 17px;
|
||||||
|
}
|
|
@ -216,21 +216,25 @@ function PrinterStateViewModel(loginStateViewModel) {
|
||||||
self.isSdReady = ko.observable(undefined);
|
self.isSdReady = ko.observable(undefined);
|
||||||
|
|
||||||
self.filename = ko.observable(undefined);
|
self.filename = ko.observable(undefined);
|
||||||
self.filament = ko.observable(undefined);
|
self.progress = ko.observable(undefined);
|
||||||
self.estimatedPrintTime = ko.observable(undefined);
|
self.filesize = ko.observable(undefined);
|
||||||
|
self.filepos = ko.observable(undefined);
|
||||||
self.printTime = ko.observable(undefined);
|
self.printTime = ko.observable(undefined);
|
||||||
self.printTimeLeft = ko.observable(undefined);
|
self.printTimeLeft = ko.observable(undefined);
|
||||||
self.progress = ko.observable(undefined);
|
self.sd = ko.observable(undefined);
|
||||||
self.currentLine = ko.observable(undefined);
|
|
||||||
self.totalLines = ko.observable(undefined);
|
self.filament = ko.observable(undefined);
|
||||||
|
self.estimatedPrintTime = ko.observable(undefined);
|
||||||
|
|
||||||
self.currentHeight = ko.observable(undefined);
|
self.currentHeight = ko.observable(undefined);
|
||||||
|
|
||||||
self.lineString = ko.computed(function() {
|
self.byteString = ko.computed(function() {
|
||||||
if (!self.totalLines())
|
if (!self.filesize())
|
||||||
return "-";
|
return "-";
|
||||||
var currentLine = self.currentLine() ? self.currentLine() : "-";
|
var filepos = self.filepos() ? self.filepos() : "-";
|
||||||
return currentLine + " / " + self.totalLines();
|
return filepos + " / " + self.filesize();
|
||||||
});
|
});
|
||||||
|
|
||||||
self.progressString = ko.computed(function() {
|
self.progressString = ko.computed(function() {
|
||||||
if (!self.progress())
|
if (!self.progress())
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -254,8 +258,6 @@ function PrinterStateViewModel(loginStateViewModel) {
|
||||||
self._fromData = function(data) {
|
self._fromData = function(data) {
|
||||||
self._processStateData(data.state)
|
self._processStateData(data.state)
|
||||||
self._processJobData(data.job);
|
self._processJobData(data.job);
|
||||||
self._processGcodeData(data.gcode);
|
|
||||||
self._processSdUploadData(data.sdUpload);
|
|
||||||
self._processProgressData(data.progress);
|
self._processProgressData(data.progress);
|
||||||
self._processZData(data.currentZ);
|
self._processZData(data.currentZ);
|
||||||
}
|
}
|
||||||
|
@ -268,33 +270,15 @@ function PrinterStateViewModel(loginStateViewModel) {
|
||||||
self.isPrinting(data.flags.printing);
|
self.isPrinting(data.flags.printing);
|
||||||
self.isError(data.flags.error);
|
self.isError(data.flags.error);
|
||||||
self.isReady(data.flags.ready);
|
self.isReady(data.flags.ready);
|
||||||
self.isLoading(data.flags.loading);
|
|
||||||
self.isSdReady(data.flags.sdReady);
|
self.isSdReady(data.flags.sdReady);
|
||||||
}
|
}
|
||||||
|
|
||||||
self._processJobData = function(data) {
|
self._processJobData = function(data) {
|
||||||
self.filename(data.filename);
|
self.filename(data.filename);
|
||||||
self.totalLines(data.lines);
|
self.filesize(data.filesize);
|
||||||
self.estimatedPrintTime(data.estimatedPrintTime);
|
self.estimatedPrintTime(data.estimatedPrintTime);
|
||||||
self.filament(data.filament);
|
self.filament(data.filament);
|
||||||
}
|
self.sd(data.sd);
|
||||||
|
|
||||||
self._processGcodeData = function(data) {
|
|
||||||
if (self.isLoading()) {
|
|
||||||
var progress = Math.round(data.progress * 100);
|
|
||||||
if (data.mode == "loading") {
|
|
||||||
self.filename("Loading... (" + progress + "%)");
|
|
||||||
} else if (data.mode == "parsing") {
|
|
||||||
self.filename("Parsing... (" + progress + "%)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self._processSdUploadData = function(data) {
|
|
||||||
if (self.isLoading()) {
|
|
||||||
var progress = Math.round(data.progress * 100);
|
|
||||||
self.filename("Streaming... (" + progress + "%)");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self._processProgressData = function(data) {
|
self._processProgressData = function(data) {
|
||||||
|
@ -303,7 +287,7 @@ function PrinterStateViewModel(loginStateViewModel) {
|
||||||
} else {
|
} else {
|
||||||
self.progress(undefined);
|
self.progress(undefined);
|
||||||
}
|
}
|
||||||
self.currentLine(data.currentLine);
|
self.filepos(data.filepos);
|
||||||
self.printTime(data.printTime);
|
self.printTime(data.printTime);
|
||||||
self.printTimeLeft(data.printTimeLeft);
|
self.printTimeLeft(data.printTimeLeft);
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,13 +108,13 @@
|
||||||
<div class="accordion-body collapse in" id="state">
|
<div class="accordion-body collapse in" id="state">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
Machine State: <strong data-bind="text: stateString"></strong><br>
|
Machine State: <strong data-bind="text: stateString"></strong><br>
|
||||||
File: <strong data-bind="text: filename"></strong><br>
|
File: <strong data-bind="text: filename"></strong> <strong data-bind="visible: sd">(SD)</strong><br>
|
||||||
Filament: <strong data-bind="text: filament"></strong><br>
|
Filament: <strong data-bind="text: filament"></strong><br>
|
||||||
Estimated Print Time: <strong data-bind="text: estimatedPrintTime"></strong><br>
|
Estimated Print Time: <strong data-bind="text: estimatedPrintTime"></strong><br>
|
||||||
Line: <strong data-bind="text: lineString"></strong><br>
|
|
||||||
Height: <strong data-bind="text: currentHeight"></strong><br>
|
Height: <strong data-bind="text: currentHeight"></strong><br>
|
||||||
Print Time: <strong data-bind="text: printTime"></strong><br>
|
Print Time: <strong data-bind="text: printTime"></strong><br>
|
||||||
Print Time Left: <strong data-bind="text: printTimeLeft"></strong><br>
|
Print Time Left: <strong data-bind="text: printTimeLeft"></strong><br>
|
||||||
|
Printed: <strong data-bind="text: byteString"></strong><br>
|
||||||
|
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div class="bar" id="job_progressBar" data-bind="style: { width: progressString() + '%' }"></div>
|
<div class="bar" id="job_progressBar" data-bind="style: { width: progressString() + '%' }"></div>
|
||||||
|
@ -153,7 +153,7 @@
|
||||||
{% if enableSdSupport %}
|
{% if enableSdSupport %}
|
||||||
<div class="sd-trigger accordion-heading-button btn-group">
|
<div class="sd-trigger accordion-heading-button btn-group">
|
||||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
<span class="icon-hdd"></span>
|
<span class="icon-sd-black-14"></span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li data-bind="visible: !isSdReady()"><a href="#" data-bind="click: function() { $root.initSdCard(); }"><i class="icon-flag"></i> Initialize SD card</a></li>
|
<li data-bind="visible: !isSdReady()"><a href="#" data-bind="click: function() { $root.initSdCard(); }"><i class="icon-flag"></i> Initialize SD card</a></li>
|
||||||
|
|
|
@ -87,6 +87,7 @@ class VirtualPrinter():
|
||||||
if self._writingToSd and not self._selectedSdFile is None and not "M29" in data:
|
if self._writingToSd and not self._selectedSdFile is None and not "M29" in data:
|
||||||
with open(self._selectedSdFile, "a") as f:
|
with open(self._selectedSdFile, "a") as f:
|
||||||
f.write(data)
|
f.write(data)
|
||||||
|
self.readList.append("ok")
|
||||||
return
|
return
|
||||||
|
|
||||||
#print "Send: %s" % (data.rstrip())
|
#print "Send: %s" % (data.rstrip())
|
||||||
|
@ -202,6 +203,7 @@ class VirtualPrinter():
|
||||||
|
|
||||||
self._writingToSd = True
|
self._writingToSd = True
|
||||||
self._selectedSdFile = file
|
self._selectedSdFile = file
|
||||||
|
self.readList.append("Writing to file: %s" % filename)
|
||||||
self.readList.append("ok")
|
self.readList.append("ok")
|
||||||
|
|
||||||
def _finishSdFile(self):
|
def _finishSdFile(self):
|
||||||
|
@ -299,16 +301,19 @@ class MachineComPrintCallback(object):
|
||||||
def mcZChange(self, newZ):
|
def mcZChange(self, newZ):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def mcFileSelected(self, filename, filesize, sd):
|
||||||
|
pass
|
||||||
|
|
||||||
def mcSdStateChange(self, sdReady):
|
def mcSdStateChange(self, sdReady):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def mcSdFiles(self, files):
|
def mcSdFiles(self, files):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def mcSdSelected(self, filename, size):
|
def mcSdPrintingDone(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def mcSdPrintingDone(self):
|
def mcFileTransferStarted(self, filename, filesize):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class MachineCom(object):
|
class MachineCom(object):
|
||||||
|
@ -351,15 +356,11 @@ class MachineCom(object):
|
||||||
self._bedTemp = 0
|
self._bedTemp = 0
|
||||||
self._targetTemp = 0
|
self._targetTemp = 0
|
||||||
self._bedTargetTemp = 0
|
self._bedTargetTemp = 0
|
||||||
self._gcodeList = None
|
|
||||||
self._gcodePos = 0
|
|
||||||
self._commandQueue = queue.Queue()
|
self._commandQueue = queue.Queue()
|
||||||
self._logQueue = queue.Queue(256)
|
self._logQueue = queue.Queue(256)
|
||||||
self._feedRateModifier = {}
|
|
||||||
self._currentZ = -1
|
self._currentZ = -1
|
||||||
self._heatupWaitStartTime = 0
|
self._heatupWaitStartTime = 0
|
||||||
self._heatupWaitTimeLost = 0.0
|
self._heatupWaitTimeLost = 0.0
|
||||||
self._printStartTime = None
|
|
||||||
|
|
||||||
self._alwaysSendChecksum = settings().getBoolean(["feature", "alwaysSendChecksum"])
|
self._alwaysSendChecksum = settings().getBoolean(["feature", "alwaysSendChecksum"])
|
||||||
self._currentLine = 1
|
self._currentLine = 1
|
||||||
|
@ -373,25 +374,21 @@ class MachineCom(object):
|
||||||
self.thread.daemon = True
|
self.thread.daemon = True
|
||||||
self.thread.start()
|
self.thread.start()
|
||||||
|
|
||||||
|
# SD status data
|
||||||
self._sdAvailable = False
|
self._sdAvailable = False
|
||||||
self._sdPrinting = False
|
|
||||||
self._sdFileList = False
|
self._sdFileList = False
|
||||||
self._sdFile = None
|
|
||||||
self._sdFilePos = None
|
|
||||||
self._sdFileSize = None
|
|
||||||
self._sdFiles = []
|
self._sdFiles = []
|
||||||
|
|
||||||
|
# print job
|
||||||
|
self._currentFile = None
|
||||||
|
|
||||||
def _changeState(self, newState):
|
def _changeState(self, newState):
|
||||||
if self._state == newState:
|
if self._state == newState:
|
||||||
return
|
return
|
||||||
|
|
||||||
if newState == self.STATE_CLOSED or newState == self.STATE_CLOSED_WITH_ERROR:
|
if newState == self.STATE_CLOSED or newState == self.STATE_CLOSED_WITH_ERROR:
|
||||||
if settings().get(["feature", "sdSupport"]):
|
if settings().get(["feature", "sdSupport"]):
|
||||||
self._sdPrinting = False
|
|
||||||
self._sdFileList = False
|
self._sdFileList = False
|
||||||
self._sdFile = None
|
|
||||||
self._sdFilePos = None
|
|
||||||
self._sdFileSize = None
|
|
||||||
self._sdFiles = []
|
self._sdFiles = []
|
||||||
self._callback.mcSdFiles([])
|
self._callback.mcSdFiles([])
|
||||||
|
|
||||||
|
@ -417,8 +414,10 @@ class MachineCom(object):
|
||||||
if self._state == self.STATE_OPERATIONAL:
|
if self._state == self.STATE_OPERATIONAL:
|
||||||
return "Operational"
|
return "Operational"
|
||||||
if self._state == self.STATE_PRINTING:
|
if self._state == self.STATE_PRINTING:
|
||||||
if self._sdPrinting:
|
if self.isSdFileSelected():
|
||||||
return "Printing from SD"
|
return "Printing from SD"
|
||||||
|
elif self.isStreaming():
|
||||||
|
return "Sending file to SD"
|
||||||
else:
|
else:
|
||||||
return "Printing"
|
return "Printing"
|
||||||
if self._state == self.STATE_PAUSED:
|
if self._state == self.STATE_PAUSED:
|
||||||
|
@ -454,53 +453,51 @@ class MachineCom(object):
|
||||||
return self._state == self.STATE_PRINTING
|
return self._state == self.STATE_PRINTING
|
||||||
|
|
||||||
def isSdPrinting(self):
|
def isSdPrinting(self):
|
||||||
return self._sdPrinting
|
return self.isSdFileSelected() and self.isPrinting()
|
||||||
|
|
||||||
|
def isSdFileSelected(self):
|
||||||
|
return self._currentFile is not None and isinstance(self._currentFile, PrintingSdFileInformation)
|
||||||
|
|
||||||
|
def isStreaming(self):
|
||||||
|
return self._currentFile is not None and isinstance(self._currentFile, StreamingGcodeFileInformation)
|
||||||
|
|
||||||
def isPaused(self):
|
def isPaused(self):
|
||||||
return self._state == self.STATE_PAUSED
|
return self._state == self.STATE_PAUSED
|
||||||
|
|
||||||
def isBusy(self):
|
def isBusy(self):
|
||||||
return self.isPrinting() or self._state == self.STATE_RECEIVING_FILE
|
return self.isPrinting() or self.isPaused()
|
||||||
|
|
||||||
def isSdReady(self):
|
def isSdReady(self):
|
||||||
return self._sdAvailable
|
return self._sdAvailable
|
||||||
|
|
||||||
def getPrintPos(self):
|
def getPrintProgress(self):
|
||||||
if self._sdPrinting:
|
if self._currentFile is None:
|
||||||
return self._sdFilePos
|
return None
|
||||||
else:
|
return self._currentFile.getProgress()
|
||||||
return self._gcodePos
|
|
||||||
|
def getPrintFilepos(self):
|
||||||
|
if self._currentFile is None:
|
||||||
|
return None
|
||||||
|
return self._currentFile.getFilepos()
|
||||||
|
|
||||||
def getPrintTime(self):
|
def getPrintTime(self):
|
||||||
if self._printStartTime == None:
|
if self._currentFile is None or self._currentFile.getStartTime() is None:
|
||||||
return 0
|
return None
|
||||||
else:
|
else:
|
||||||
return time.time() - self._printStartTime
|
return time.time() - self._currentFile.getStartTime()
|
||||||
|
|
||||||
def getPrintTimeRemainingEstimate(self):
|
def getPrintTimeRemainingEstimate(self):
|
||||||
if self._printStartTime == None:
|
printTime = self.getPrintTime()
|
||||||
|
if printTime is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if self._sdPrinting:
|
printTime /= 60
|
||||||
printTime = (time.time() - self._printStartTime) / 60
|
progress = self._currentFile.getProgress()
|
||||||
if self._sdFilePos > 0:
|
if progress:
|
||||||
printTimeTotal = printTime * self._sdFileSize / self._sdFilePos
|
printTimeTotal = printTime / progress
|
||||||
|
return printTimeTotal - printTime
|
||||||
else:
|
else:
|
||||||
printTimeTotal = printTime * self._sdFileSize
|
|
||||||
printTimeLeft = printTimeTotal - printTime
|
|
||||||
return printTimeLeft
|
|
||||||
else:
|
|
||||||
# for host printing we only start counting the print time at gcode line 100, so we need to calculate stuff
|
|
||||||
# a bit different here
|
|
||||||
if self.getPrintPos() < 200:
|
|
||||||
return None
|
return None
|
||||||
printTime = (time.time() - self._printStartTime) / 60
|
|
||||||
printTimeTotal = printTime * (len(self._gcodeList) - 100) / (self.getPrintPos() - 100)
|
|
||||||
printTimeLeft = printTimeTotal - printTime
|
|
||||||
return printTimeLeft
|
|
||||||
|
|
||||||
def getSdProgress(self):
|
|
||||||
return (self._sdFilePos, self._sdFileSize)
|
|
||||||
|
|
||||||
def getTemp(self):
|
def getTemp(self):
|
||||||
return self._temp
|
return self._temp
|
||||||
|
@ -593,7 +590,7 @@ class MachineCom(object):
|
||||||
##~~ SD file list
|
##~~ 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 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:
|
if self._sdFileList and not 'End file list' in line:
|
||||||
self._sdFiles.append(line)
|
self._sdFiles.append(line.lower())
|
||||||
continue
|
continue
|
||||||
|
|
||||||
##~~ Temperature processing
|
##~~ Temperature processing
|
||||||
|
@ -619,6 +616,11 @@ class MachineCom(object):
|
||||||
self._sdAvailable = False
|
self._sdAvailable = False
|
||||||
self._sdFiles = []
|
self._sdFiles = []
|
||||||
self._callback.mcSdStateChange(self._sdAvailable)
|
self._callback.mcSdStateChange(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:
|
elif 'SD card ok' in line:
|
||||||
self._sdAvailable = True
|
self._sdAvailable = True
|
||||||
self.refreshSdFiles()
|
self.refreshSdFiles()
|
||||||
|
@ -632,23 +634,24 @@ class MachineCom(object):
|
||||||
elif 'SD printing byte' in line:
|
elif 'SD printing byte' in line:
|
||||||
# answer to M27, at least on Marlin, Repetier and Sprinter: "SD printing byte %d/%d"
|
# answer to M27, at least on Marlin, Repetier and Sprinter: "SD printing byte %d/%d"
|
||||||
match = re.search("([0-9]*)/([0-9]*)", line)
|
match = re.search("([0-9]*)/([0-9]*)", line)
|
||||||
self._sdFilePos = int(match.group(1))
|
self._currentFile.setFilepos(int(match.group(1)))
|
||||||
self._sdFileSize = int(match.group(2))
|
|
||||||
self._callback.mcProgress()
|
self._callback.mcProgress()
|
||||||
elif 'File opened' in line:
|
elif 'File opened' in line:
|
||||||
# answer to M23, at least on Marlin, Repetier and Sprinter: "File opened:%s Size:%d"
|
# answer to M23, at least on Marlin, Repetier and Sprinter: "File opened:%s Size:%d"
|
||||||
match = re.search("File opened:\s*(.*?)\s+Size:\s*([0-9]*)", line)
|
match = re.search("File opened:\s*(.*?)\s+Size:\s*([0-9]*)", line)
|
||||||
self._sdFile = match.group(1)
|
self._currentFile = PrintingSdFileInformation(match.group(1), int(match.group(2)))
|
||||||
self._sdFileSize = int(match.group(2))
|
|
||||||
elif 'File selected' in line:
|
elif 'File selected' in line:
|
||||||
# final answer to M23, at least on Marlin, Repetier and Sprinter: "File selected"
|
# final answer to M23, at least on Marlin, Repetier and Sprinter: "File selected"
|
||||||
self._callback.mcSdSelected(self._sdFile, self._sdFileSize)
|
self._callback.mcFileSelected(self._currentFile.getFilename(), self._currentFile.getFilesize(), True)
|
||||||
|
elif 'Writing to file' in line:
|
||||||
|
# anwer to M28, at least on Marlin, Repetier and Sprinter: "Writing to file: %s"
|
||||||
|
self._printSection = "CUSTOM"
|
||||||
|
self._changeState(self.STATE_PRINTING)
|
||||||
elif 'Done printing file' in line:
|
elif 'Done printing file' in line:
|
||||||
# printer is reporting file finished printing
|
# printer is reporting file finished printing
|
||||||
self._sdPrinting = False
|
|
||||||
self._sdFilePos = 0
|
self._sdFilePos = 0
|
||||||
self._changeState(self.STATE_OPERATIONAL)
|
self._changeState(self.STATE_OPERATIONAL)
|
||||||
self._callback.mcSdPrintingDone()
|
self._callback.mcPrintjobDone()
|
||||||
|
|
||||||
##~~ Message handling
|
##~~ 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 self.isOperational():
|
elif line.strip() != '' and line.strip() != 'ok' and not line.startswith("wait") and not line.startswith('Resend:') and line != 'echo:Unknown command:""\n' and self.isOperational():
|
||||||
|
@ -725,7 +728,7 @@ class MachineCom(object):
|
||||||
self._log("Communication timeout during printing, forcing a line")
|
self._log("Communication timeout during printing, forcing a line")
|
||||||
line = 'ok'
|
line = 'ok'
|
||||||
|
|
||||||
if self._sdPrinting:
|
if self.isSdPrinting():
|
||||||
if time.time() > tempRequestTimeout:
|
if time.time() > tempRequestTimeout:
|
||||||
self._sendCommand("M105")
|
self._sendCommand("M105")
|
||||||
tempRequestTimeout = time.time() + 5
|
tempRequestTimeout = time.time() + 5
|
||||||
|
@ -738,7 +741,7 @@ class MachineCom(object):
|
||||||
timeout = time.time() + 5
|
timeout = time.time() + 5
|
||||||
else:
|
else:
|
||||||
# Even when printing request the temperature every 5 seconds.
|
# Even when printing request the temperature every 5 seconds.
|
||||||
if time.time() > tempRequestTimeout:
|
if time.time() > tempRequestTimeout and not self.isStreaming():
|
||||||
self._commandQueue.put("M105")
|
self._commandQueue.put("M105")
|
||||||
tempRequestTimeout = time.time() + 5
|
tempRequestTimeout = time.time() + 5
|
||||||
|
|
||||||
|
@ -746,7 +749,7 @@ class MachineCom(object):
|
||||||
timeout = time.time() + 5
|
timeout = time.time() + 5
|
||||||
if self._resendDelta is not None:
|
if self._resendDelta is not None:
|
||||||
self._resendNextCommand()
|
self._resendNextCommand()
|
||||||
elif not self._commandQueue.empty():
|
elif not self._commandQueue.empty() and not self.isStreaming():
|
||||||
self._sendCommand(self._commandQueue.get())
|
self._sendCommand(self._commandQueue.get())
|
||||||
else:
|
else:
|
||||||
self._sendNext()
|
self._sendNext()
|
||||||
|
@ -883,7 +886,7 @@ class MachineCom(object):
|
||||||
if self._alwaysSendChecksum:
|
if self._alwaysSendChecksum:
|
||||||
lineNumber = self._currentLine
|
lineNumber = self._currentLine
|
||||||
else:
|
else:
|
||||||
lineNumber = self._gcodePos
|
lineNumber = self._currentFile.getLineCount()
|
||||||
self._addToLastLines(cmd)
|
self._addToLastLines(cmd)
|
||||||
self._currentLine += 1
|
self._currentLine += 1
|
||||||
self._doSendWithChecksum(cmd, lineNumber)
|
self._doSendWithChecksum(cmd, lineNumber)
|
||||||
|
@ -916,21 +919,26 @@ class MachineCom(object):
|
||||||
|
|
||||||
def _sendNext(self):
|
def _sendNext(self):
|
||||||
with self._sendNextLock:
|
with self._sendNextLock:
|
||||||
if self._gcodePos >= len(self._gcodeList):
|
line = self._currentFile.getNext()
|
||||||
|
if line is None:
|
||||||
|
if self.isStreaming():
|
||||||
|
self._sendCommand("M29")
|
||||||
|
self._currentFile = None
|
||||||
|
self._callback.mcFileTransferDone()
|
||||||
|
else:
|
||||||
|
self._callback.mcPrintjobDone()
|
||||||
self._changeState(self.STATE_OPERATIONAL)
|
self._changeState(self.STATE_OPERATIONAL)
|
||||||
return
|
return
|
||||||
if self._gcodePos == 100:
|
|
||||||
self._printStartTime100 = time.time()
|
|
||||||
line = self._gcodeList[self._gcodePos]
|
|
||||||
if type(line) is tuple:
|
if type(line) is tuple:
|
||||||
self._printSection = line[1]
|
self._printSection = line[1]
|
||||||
line = line[0]
|
line = line[0]
|
||||||
|
|
||||||
|
if not self.isStreaming():
|
||||||
try:
|
try:
|
||||||
if line == 'M0' or line == 'M1':
|
if line == 'M0' or line == 'M1':
|
||||||
self.setPause(True)
|
self.setPause(True)
|
||||||
line = 'M105' #Don't send the M0 or M1 to the machine, as M0 and M1 are handled as an LCD menu pause.
|
line = 'M105' #Don't send the M0 or M1 to the machine, as M0 and M1 are handled as an LCD menu pause.
|
||||||
if self._printSection in self._feedRateModifier:
|
|
||||||
line = re.sub('F([0-9]*)', lambda m: 'F' + str(int(int(m.group(1)) * self._feedRateModifier[self._printSection])), line)
|
|
||||||
if ('G0' in line or 'G1' in line) and 'Z' in line:
|
if ('G0' in line or 'G1' in line) and 'Z' in line:
|
||||||
z = float(re.search('Z([0-9\.]*)', line).group(1))
|
z = float(re.search('Z([0-9\.]*)', line).group(1))
|
||||||
if self._currentZ != z:
|
if self._currentZ != z:
|
||||||
|
@ -939,100 +947,107 @@ class MachineCom(object):
|
||||||
except:
|
except:
|
||||||
self._log("Unexpected error: %s" % (getExceptionString()))
|
self._log("Unexpected error: %s" % (getExceptionString()))
|
||||||
self._sendCommand(line, True)
|
self._sendCommand(line, True)
|
||||||
self._gcodePos += 1
|
|
||||||
self._callback.mcProgress()
|
self._callback.mcProgress()
|
||||||
|
|
||||||
def sendCommand(self, cmd):
|
def sendCommand(self, cmd):
|
||||||
cmd = cmd.encode('ascii', 'replace')
|
cmd = cmd.encode('ascii', 'replace')
|
||||||
if self.isPrinting():
|
if self.isPrinting() and not self.isSdFileSelected():
|
||||||
self._commandQueue.put(cmd)
|
self._commandQueue.put(cmd)
|
||||||
elif self.isOperational():
|
elif self.isOperational():
|
||||||
self._sendCommand(cmd)
|
self._sendCommand(cmd)
|
||||||
|
|
||||||
def printGCode(self, gcodeList):
|
def startPrint(self):
|
||||||
if not self.isOperational() or self.isPrinting():
|
if not self.isOperational() or self.isPrinting():
|
||||||
return
|
return
|
||||||
if self._sdPrinting:
|
|
||||||
self._sdPrinting = False
|
if self._currentFile is None:
|
||||||
self._gcodeList = gcodeList
|
raise ValueError("No file selected for printing")
|
||||||
self._gcodePos = 0
|
|
||||||
self._printSection = 'CUSTOM'
|
self._printSection = "CUSTOM"
|
||||||
self._changeState(self.STATE_PRINTING)
|
self._changeState(self.STATE_PRINTING)
|
||||||
self._printStartTime = time.time()
|
|
||||||
self._sendNext()
|
|
||||||
|
|
||||||
def printSdFile(self):
|
|
||||||
if not self.isOperational() or self.isPrinting():
|
|
||||||
return
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._currentFile.start()
|
||||||
|
if self.isSdFileSelected():
|
||||||
if self.isPaused():
|
if self.isPaused():
|
||||||
self.sendCommand("M26 S0") # reset position in file to byte 0
|
self.sendCommand("M26 S0")
|
||||||
|
self._currentFile.setFilepos(0)
|
||||||
self.sendCommand("M24")
|
self.sendCommand("M24")
|
||||||
|
else:
|
||||||
|
self._sendNext()
|
||||||
|
except:
|
||||||
|
self._changeState(self.STATE_ERROR)
|
||||||
|
self._errorValue = getExceptionString()
|
||||||
|
|
||||||
self._printSection = 'CUSTOM'
|
def startFileTransfer(self, filename, remoteFilename):
|
||||||
self._sdPrinting = True
|
if not self.isOperational() or self.isBusy():
|
||||||
self._changeState(self.STATE_PRINTING)
|
return
|
||||||
self._printStartTime = time.time()
|
|
||||||
|
self._currentFile = StreamingGcodeFileInformation(filename)
|
||||||
|
self._currentFile.start()
|
||||||
|
|
||||||
|
self.sendCommand("M28 %s" % remoteFilename)
|
||||||
|
self._callback.mcFileTransferStarted(remoteFilename, self._currentFile.getFilesize())
|
||||||
|
|
||||||
|
def selectFile(self, filename, sd):
|
||||||
|
if self.isBusy():
|
||||||
|
return
|
||||||
|
|
||||||
|
if sd:
|
||||||
|
if not self.isOperational():
|
||||||
|
# printer is not connected, can't use SD
|
||||||
|
return
|
||||||
|
self.sendCommand("M23 %s" % filename)
|
||||||
|
else:
|
||||||
|
self._currentFile = PrintingGcodeFileInformation(filename)
|
||||||
|
self._callback.mcFileSelected(filename, self._currentFile.getFilesize(), False)
|
||||||
|
|
||||||
def cancelPrint(self):
|
def cancelPrint(self):
|
||||||
if self.isOperational():
|
if not self.isOperational() or self.isStreaming():
|
||||||
|
return
|
||||||
|
|
||||||
self._changeState(self.STATE_OPERATIONAL)
|
self._changeState(self.STATE_OPERATIONAL)
|
||||||
if self._sdPrinting:
|
|
||||||
self._sdPrinting = False
|
if self.isSdFileSelected():
|
||||||
self.sendCommand("M25") # pause print
|
self.sendCommand("M25") # pause print
|
||||||
self.sendCommand("M26 S0") # reset position in file to byte 0
|
self.sendCommand("M26 S0") # reset position in file to byte 0
|
||||||
|
|
||||||
def setPause(self, pause):
|
def setPause(self, pause):
|
||||||
|
if self.isStreaming():
|
||||||
|
return
|
||||||
|
|
||||||
if not pause and self.isPaused():
|
if not pause and self.isPaused():
|
||||||
self._changeState(self.STATE_PRINTING)
|
self._changeState(self.STATE_PRINTING)
|
||||||
if self._sdPrinting:
|
if self.isSdFileSelected():
|
||||||
self.sendCommand("M24")
|
self.sendCommand("M24")
|
||||||
else:
|
else:
|
||||||
for i in xrange(0, 6):
|
|
||||||
self._sendNext()
|
self._sendNext()
|
||||||
if pause and self.isPrinting():
|
if pause and self.isPrinting():
|
||||||
self._changeState(self.STATE_PAUSED)
|
self._changeState(self.STATE_PAUSED)
|
||||||
if self._sdPrinting:
|
if self.isSdFileSelected():
|
||||||
self.sendCommand("M25") # pause print
|
self.sendCommand("M25") # pause print
|
||||||
|
|
||||||
def setFeedrateModifier(self, type, value):
|
##~~ SD card handling
|
||||||
self._feedRateModifier[type] = value
|
|
||||||
|
|
||||||
def getFeedrateModifiers(self):
|
|
||||||
result = {}
|
|
||||||
result.update(self._feedRateModifier)
|
|
||||||
return result
|
|
||||||
|
|
||||||
##~~ SD card
|
|
||||||
def getSdFiles(self):
|
def getSdFiles(self):
|
||||||
return self._sdFiles
|
return self._sdFiles
|
||||||
|
|
||||||
def startSdFileTransfer(self, filename):
|
def startSdFileTransfer(self, filename):
|
||||||
if not self.isOperational() or self.isPrinting() or self.isPaused():
|
if not self.isOperational() or self.isBusy():
|
||||||
return
|
return
|
||||||
|
|
||||||
self._changeState(self.STATE_RECEIVING_FILE)
|
self._changeState(self.STATE_RECEIVING_FILE)
|
||||||
self.sendCommand("M28 %s" % filename.lower())
|
self.sendCommand("M28 %s" % filename.lower())
|
||||||
|
|
||||||
def endSdFileTransfer(self, filename):
|
def endSdFileTransfer(self, filename):
|
||||||
if not self.isOperational() or self.isPrinting() or self.isPaused():
|
if not self.isOperational() or self.isBusy():
|
||||||
return
|
return
|
||||||
|
|
||||||
self.sendCommand("M29 %s" % filename.lower())
|
self.sendCommand("M29 %s" % filename.lower())
|
||||||
self._changeState(self.STATE_OPERATIONAL)
|
self._changeState(self.STATE_OPERATIONAL)
|
||||||
self.refreshSdFiles()
|
self.refreshSdFiles()
|
||||||
|
|
||||||
def selectSdFile(self, filename):
|
|
||||||
if not self.isOperational() or self.isPrinting() or self.isPaused():
|
|
||||||
return
|
|
||||||
|
|
||||||
self._sdFile = None
|
|
||||||
self._sdFilePos = 0
|
|
||||||
|
|
||||||
self.sendCommand("M23 %s" % filename.lower())
|
|
||||||
|
|
||||||
def deleteSdFile(self, filename):
|
def deleteSdFile(self, filename):
|
||||||
if not self.isOperational() or ((self.isPrinting() or self.isPaused()) and self._sdFile == filename.lower()):
|
if not self.isOperational() or (self.isBusy() and self._sdFile == filename.lower()):
|
||||||
# do not delete a file from sd we are currently printing from
|
# do not delete a file from sd we are currently printing from
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -1040,7 +1055,7 @@ class MachineCom(object):
|
||||||
self.refreshSdFiles()
|
self.refreshSdFiles()
|
||||||
|
|
||||||
def refreshSdFiles(self):
|
def refreshSdFiles(self):
|
||||||
if not self.isOperational() or self.isPrinting() or self.isPaused():
|
if not self.isOperational() or self.isBusy():
|
||||||
return
|
return
|
||||||
self.sendCommand("M20")
|
self.sendCommand("M20")
|
||||||
|
|
||||||
|
@ -1050,7 +1065,7 @@ class MachineCom(object):
|
||||||
self.sendCommand("M21")
|
self.sendCommand("M21")
|
||||||
|
|
||||||
def releaseSdCard(self):
|
def releaseSdCard(self):
|
||||||
if not self.isOperational() or ((self.isPrinting() or self.isPaused()) and self._sdPrinting):
|
if not self.isOperational() or (self.isBusy() and self.isSdFileSelected()):
|
||||||
# do not release the sd card if we are currently printing from it
|
# do not release the sd card if we are currently printing from it
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -1064,3 +1079,145 @@ class MachineCom(object):
|
||||||
def getExceptionString():
|
def getExceptionString():
|
||||||
locationInfo = traceback.extract_tb(sys.exc_info()[2])[0]
|
locationInfo = traceback.extract_tb(sys.exc_info()[2])[0]
|
||||||
return "%s: '%s' @ %s:%s:%d" % (str(sys.exc_info()[0].__name__), str(sys.exc_info()[1]), os.path.basename(locationInfo[0]), locationInfo[2], locationInfo[1])
|
return "%s: '%s' @ %s:%s:%d" % (str(sys.exc_info()[0].__name__), str(sys.exc_info()[1]), os.path.basename(locationInfo[0]), locationInfo[2], locationInfo[1])
|
||||||
|
|
||||||
|
class PrintingFileInformation(object):
|
||||||
|
"""
|
||||||
|
Encapsulates information regarding the current file being printed: file name, current position, total size and
|
||||||
|
time the print started.
|
||||||
|
Allows to reset the current file position to 0 and to calculate the current progress as a floating point
|
||||||
|
value between 0 and 1.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, filename):
|
||||||
|
self._filename = filename
|
||||||
|
self._filepos = 0
|
||||||
|
self._filesize = None
|
||||||
|
self._startTime = None
|
||||||
|
|
||||||
|
def getStartTime(self):
|
||||||
|
return self._startTime
|
||||||
|
|
||||||
|
def getFilename(self):
|
||||||
|
return self._filename
|
||||||
|
|
||||||
|
def getFilesize(self):
|
||||||
|
return self._filesize
|
||||||
|
|
||||||
|
def getFilepos(self):
|
||||||
|
return self._filepos
|
||||||
|
|
||||||
|
def getProgress(self):
|
||||||
|
"""
|
||||||
|
The current progress of the file, calculated as relation between file position and absolute size. Returns -1
|
||||||
|
if file size is None or < 1.
|
||||||
|
"""
|
||||||
|
if self._filesize is None or not self._filesize > 0:
|
||||||
|
return -1
|
||||||
|
return float(self._filepos) / float(self._filesize)
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
"""
|
||||||
|
Resets the current file position to 0.
|
||||||
|
"""
|
||||||
|
self._filepos = 0
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""
|
||||||
|
Marks the print job as started and remembers the start time.
|
||||||
|
"""
|
||||||
|
self._startTime = time.time()
|
||||||
|
|
||||||
|
class PrintingSdFileInformation(PrintingFileInformation):
|
||||||
|
"""
|
||||||
|
Encapsulates information regarding an ongoing print from SD.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, filename, filesize):
|
||||||
|
PrintingFileInformation.__init__(self, filename)
|
||||||
|
self._filesize = filesize
|
||||||
|
|
||||||
|
def setFilepos(self, filepos):
|
||||||
|
"""
|
||||||
|
Sets the current file position.
|
||||||
|
"""
|
||||||
|
self._filepos = filepos
|
||||||
|
|
||||||
|
class PrintingGcodeFileInformation(PrintingFileInformation):
|
||||||
|
"""
|
||||||
|
Encapsulates information regarding an ongoing direct print. Takes care of the needed file handle and ensures
|
||||||
|
that the file is closed in case of an error.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, filename):
|
||||||
|
PrintingFileInformation.__init__(self, filename)
|
||||||
|
self._filehandle = None
|
||||||
|
self._lineCount = 0
|
||||||
|
self._firstLine = None
|
||||||
|
self._prevLineType = None
|
||||||
|
|
||||||
|
if not os.path.exists(self._filename) or not os.path.isfile(self._filename):
|
||||||
|
raise IOError("File %s does not exist" % self._filename)
|
||||||
|
self._filesize = os.stat(self._filename).st_size
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""
|
||||||
|
Opens the file for reading and determines the file size. Start time won't be recorded until 100 lines in
|
||||||
|
"""
|
||||||
|
self._filehandle = open(self._filename, "r")
|
||||||
|
self._lineCount = 0
|
||||||
|
self._prevLineType = "CUSTOM"
|
||||||
|
|
||||||
|
def getNext(self):
|
||||||
|
"""
|
||||||
|
Retrieves the next line for printing.
|
||||||
|
"""
|
||||||
|
if self._filehandle is None:
|
||||||
|
raise ValueError("File %s is not open for reading" % self._filename)
|
||||||
|
|
||||||
|
if self._lineCount == 0:
|
||||||
|
self._lineCount += 1
|
||||||
|
return "M110 N0"
|
||||||
|
|
||||||
|
try:
|
||||||
|
processedLine = None
|
||||||
|
while processedLine is None:
|
||||||
|
if self._filehandle is None:
|
||||||
|
# file got closed just now
|
||||||
|
return None
|
||||||
|
line = self._filehandle.readline()
|
||||||
|
self._lineCount += 1
|
||||||
|
if not line:
|
||||||
|
self._filehandle.close()
|
||||||
|
self._filehandle = None
|
||||||
|
processedLine = self._processLine(line)
|
||||||
|
self._filepos = self._filehandle.tell()
|
||||||
|
|
||||||
|
if self._lineCount >= 100 and self._startTime is None:
|
||||||
|
self._startTime = time.time()
|
||||||
|
|
||||||
|
return processedLine
|
||||||
|
except Exception as (e):
|
||||||
|
if self._filehandle is not None:
|
||||||
|
self._filehandle.close()
|
||||||
|
raise e
|
||||||
|
|
||||||
|
def getLineCount(self):
|
||||||
|
return self._lineCount
|
||||||
|
|
||||||
|
def _processLine(self, line):
|
||||||
|
lineType = self._prevLineType
|
||||||
|
if line.startswith(";TYPE:"):
|
||||||
|
lineType = line[6:].strip()
|
||||||
|
if ";" in line:
|
||||||
|
line = line[0:line.find(";")]
|
||||||
|
line = line.strip()
|
||||||
|
if len(line) > 0:
|
||||||
|
if self._prevLineType != lineType:
|
||||||
|
return line, lineType
|
||||||
|
else:
|
||||||
|
return line
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
class StreamingGcodeFileInformation(PrintingGcodeFileInformation):
|
||||||
|
pass
|
Loading…
Reference in New Issue