diff --git a/docs/api/connection.rst b/docs/api/connection.rst index 27feb765..1caefe99 100644 --- a/docs/api/connection.rst +++ b/docs/api/connection.rst @@ -20,7 +20,7 @@ Get connection settings .. sourcecode:: http - GET /api/control/connection HTTP/1.1 + GET /api/connection HTTP/1.1 Host: example.com Content-Type: application/json X-Api-Key: abcdef... @@ -77,7 +77,7 @@ Issue a connection command .. sourcecode:: http - POST /api/control/connection HTTP/1.1 + POST /api/connection HTTP/1.1 Host: example.com Content-Type: application/json X-Api-Key: abcdef... @@ -94,7 +94,7 @@ Issue a connection command .. sourcecode:: http - POST /api/control/connection HTTP/1.1 + POST /api/connection HTTP/1.1 Host: example.com Content-Type: application/json X-Api-Key: abcdef... @@ -114,6 +114,6 @@ Issue a connection command Defaults to ``false`` if not set. :json boolean autoconnect: ``connect`` command: Whether to attempt to automatically connect to the printer on server startup. If not set no changes will be made to the current setting. - :statuscode 200: No error + :statuscode 204: No error :statuscode 400: If the selected `port` or `baudrate` for a ``connect`` command are not part of the available options. \ No newline at end of file diff --git a/src/octoprint/gcodefiles.py b/src/octoprint/gcodefiles.py index 34860bad..a6cf1fe4 100644 --- a/src/octoprint/gcodefiles.py +++ b/src/octoprint/gcodefiles.py @@ -560,8 +560,6 @@ class MetadataAnalyzer: def _work(self): aborted = None while True: - self._active.wait() - if aborted is not None: filename = aborted aborted = None @@ -570,6 +568,8 @@ class MetadataAnalyzer: (priority, filename) = self._queue.get() self._logger.debug("Processing file %s from queue (priority %d)" % (filename, priority)) + self._active.wait() + try: self._analyzeGcode(filename) self._queue.task_done() diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index 8eb1f9e3..aa6af8de 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -191,6 +191,8 @@ class Server(): printer.connect(port, baudrate) try: IOLoop.instance().start() + except KeyboardInterrupt: + logger.info("Goodbye!") except: logger.fatal("Now that is embarrassing... Something really really went wrong here. Please report this including the stacktrace below in OctoPrint's bugtracker. Thanks!") logger.exception("Stacktrace follows:") diff --git a/src/octoprint/static/js/app/main.js b/src/octoprint/static/js/app/main.js index 68a47449..c649279d 100644 --- a/src/octoprint/static/js/app/main.js +++ b/src/octoprint/static/js/app/main.js @@ -1,5 +1,4 @@ $(function() { - //~~ Initialize view models var loginStateViewModel = new LoginStateViewModel(); var usersViewModel = new UsersViewModel(loginStateViewModel); diff --git a/src/octoprint/util/__init__.py b/src/octoprint/util/__init__.py index 437af118..0c5e66d2 100644 --- a/src/octoprint/util/__init__.py +++ b/src/octoprint/util/__init__.py @@ -191,6 +191,24 @@ def silentRemove(file): pass +def sanitizeAscii(line): + return unicode(line, 'ascii', 'replace').encode('ascii', 'replace').rstrip() + + +def filterNonAscii(line): + """ + Returns True if the line contains non-ascii characters, false otherwise + + @param line the line to test + """ + + try: + unicode(line, 'ascii').encode('ascii') + return False + except ValueError: + return True + + def getJsonCommandFromRequest(request, valid_commands): if not "application/json" in request.headers["Content-Type"]: return None, None, make_response("Expected content-type JSON", 400) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 1a970495..091bbddf 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -23,7 +23,7 @@ from octoprint.settings import settings from octoprint.events import eventManager, Events from octoprint.filemanager.destinations import FileDestinations from octoprint.gcodefiles import isGcodeFileName -from octoprint.util import getExceptionString, getNewTimeout +from octoprint.util import getExceptionString, getNewTimeout, sanitizeAscii, filterNonAscii from octoprint.util.virtual import VirtualPrinter try: @@ -341,6 +341,7 @@ class MachineCom(object): if self._currentFile is not None: payload = { "file": self._currentFile.getFilename(), + "filename": os.path.basename(self._currentFile.getFilename()), "origin": self._currentFile.getFileLocation() } eventManager().fire(Events.PRINT_FAILED, payload) @@ -372,6 +373,7 @@ class MachineCom(object): self._changeState(self.STATE_PRINTING) eventManager().fire(Events.PRINT_STARTED, { "file": self._currentFile.getFilename(), + "filename": os.path.basename(self._currentFile.getFilename()), "origin": self._currentFile.getFileLocation() }) @@ -438,6 +440,7 @@ class MachineCom(object): eventManager().fire(Events.PRINT_CANCELLED, { "file": self._currentFile.getFilename(), + "filename": os.path.basename(self._currentFile.getFilename()), "origin": self._currentFile.getFileLocation() }) @@ -454,6 +457,7 @@ class MachineCom(object): eventManager().fire(Events.PRINT_RESUMED, { "file": self._currentFile.getFilename(), + "filename": os.path.basename(self._currentFile.getFilename()), "origin": self._currentFile.getFileLocation() }) elif pause and self.isPrinting(): @@ -463,6 +467,7 @@ class MachineCom(object): eventManager().fire(Events.PRINT_PAUSED, { "file": self._currentFile.getFilename(), + "filename": os.path.basename(self._currentFile.getFilename()), "origin": self._currentFile.getFileLocation() }) @@ -600,7 +605,11 @@ class MachineCom(object): ##~~ 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 self._sdFileList and isGcodeFileName(line.strip().lower()) and not 'End file list' in line: - self._sdFiles.append(line.strip().lower()) + filename = line.strip().lower() + if filterNonAscii(filename): + self._logger.warn("Got a file from printer's SD that has a non-ascii filename (%s), that shouldn't happen according to the protocol" % filename) + else: + self._sdFiles.append(filename) continue ##~~ Temperature processing @@ -665,8 +674,9 @@ class MachineCom(object): self._changeState(self.STATE_OPERATIONAL) eventManager().fire(Events.PRINT_DONE, { "file": self._currentFile.getFilename(), + "filename": os.path.basename(self._currentFile.getFilename()), "origin": self._currentFile.getFileLocation(), - "time": time.time() - self._currentFile.getStartTime() + "time": self.getPrintTime() }) elif 'Done saving file' in line: self.refreshSdFiles() @@ -914,7 +924,7 @@ class MachineCom(object): if ret == '': #self._log("Recv: TIMEOUT") return '' - self._log("Recv: %s" % (unicode(ret, 'ascii', 'replace').encode('ascii', 'replace').rstrip())) + self._log("Recv: %s" % sanitizeAscii(ret)) return ret def _sendNext(self): @@ -928,7 +938,7 @@ class MachineCom(object): payload = { "local": self._currentFile.getLocalFilename(), "remote": self._currentFile.getRemoteFilename(), - "time": time.time() - self._currentFile.getStartTime() + "time": self.getPrintTime() } self._currentFile = None @@ -939,8 +949,9 @@ class MachineCom(object): else: payload = { "file": self._currentFile.getFilename(), + "filename": os.path.basename(self._currentFile.getFilename()), "origin": self._currentFile.getFileLocation(), - "time": time.time() - self._currentFile.getStartTime() + "time": self.getPrintTime() } self._callback.mcPrintjobDone() self._changeState(self.STATE_OPERATIONAL)