From 7b548763f5198b64300e28048cd4230e96996f1a Mon Sep 17 00:00:00 2001 From: Teja Date: Thu, 9 Jul 2015 16:12:54 +0200 Subject: [PATCH] way faster update of laser position. --- src/octoprint/events.py | 1 + src/octoprint/printer/__init__.py | 232 ----------------- src/octoprint/server/views.py | 2 - src/octoprint/static/js/app/dataupdater.js | 4 +- .../static/js/app/viewmodels/printerstate.js | 11 +- src/octoprint/util/comm_acc.py | 233 +++++++++--------- 6 files changed, 126 insertions(+), 357 deletions(-) diff --git a/src/octoprint/events.py b/src/octoprint/events.py index bcd80527..f91720a7 100644 --- a/src/octoprint/events.py +++ b/src/octoprint/events.py @@ -89,6 +89,7 @@ class Events(object): # GRBL LIMITS_HIT = "LimitsHit" + RT_STATE = "RealTimeState" def eventManager(): diff --git a/src/octoprint/printer/__init__.py b/src/octoprint/printer/__init__.py index f604cd95..b518cb20 100644 --- a/src/octoprint/printer/__init__.py +++ b/src/octoprint/printer/__init__.py @@ -467,239 +467,7 @@ class PrinterCallback(object): Arguments: data (dict): The current data in the format as specified above. """ -#<<<<<<< HEAD -# Returns a human readable string corresponding to the current communication state. -# """ -# if self._comm is None: -# return "Offline" -# else: -# return self._comm.getStateString() -# -# def getCurrentData(self): -# return self._stateMonitor.getCurrentData() -# -# def getCurrentJob(self): -# currentData = self._stateMonitor.getCurrentData() -# return currentData["job"] -# -# def getCurrentTemperatures(self): -# if self._comm is not None: -# tempOffset, bedTempOffset = self._comm.getOffsets() -# else: -# tempOffset = {} -# bedTempOffset = None -# -# result = {} -# if self._temp is not None: -# for tool in self._temp.keys(): -# result["tool%d" % tool] = { -# "actual": self._temp[tool][0], -# "target": self._temp[tool][1], -# "offset": tempOffset[tool] if tool in tempOffset.keys() and tempOffset[tool] is not None else 0 -# } -# if self._bedTemp is not None: -# result["bed"] = { -# "actual": self._bedTemp[0], -# "target": self._bedTemp[1], -# "offset": bedTempOffset -# } -# -# return result -# -# def getTemperatureHistory(self): -# return self._temps -# -# def getCurrentConnection(self): -# if self._comm is None: -# return "Closed", None, None, None -# -# port, baudrate = self._comm.getConnection() -# printer_profile = self._printerProfileManager.get_current_or_default() -# return self._comm.getStateString(), port, baudrate, printer_profile -# -# def isClosedOrError(self): -# return self._comm is None or self._comm.isClosedOrError() -# -# def isOperational(self): -# return self._comm is not None and self._comm.isOperational() -# -# def isLocked(self): -# return self._comm is not None and self._comm.isLocked() -# -# def isPrinting(self): -# return self._comm is not None and self._comm.isPrinting() -# -# def isPaused(self): -# return self._comm is not None and self._comm.isPaused() -# -# def isError(self): -# return self._comm is not None and self._comm.isError() -# -# def isReady(self): -# return self.isOperational() and not self._comm.isStreaming() -# -# def isSdReady(self): -# if not settings().getBoolean(["feature", "sdSupport"]) or self._comm is None: -# return False -# else: -# return self._comm.isSdReady() -# -#class StateMonitor(object): -# def __init__(self, ratelimit, updateCallback, addTemperatureCallback, addLogCallback, addMessageCallback): -# self._ratelimit = ratelimit -# self._updateCallback = updateCallback -# self._addTemperatureCallback = addTemperatureCallback -# self._addLogCallback = addLogCallback -# self._addMessageCallback = addMessageCallback -# -# self._state = None -# self._jobData = None -# self._gcodeData = None -# self._sdUploadData = None -# self._currentZ = None -# self._machinePosition = None -# self._workPosition = None -# self._progress = None -# -# self._offsets = {} -# -# self._changeEvent = threading.Event() -# self._stateMutex = threading.Lock() -# -# self._lastUpdate = time.time() -# self._worker = threading.Thread(target=self._work) -# self._worker.daemon = True -# self._worker.start() -# -# def reset(self, state=None, jobData=None, progress=None, currentZ=None, machinePosition=None, workPosition=None): -# self.setState(state) -# self.setJobData(jobData) -# self.setProgress(progress) -# self.setMachinePosition(machinePosition) -# self.setWorkPosition(workPosition) -# self.setCurrentZ(currentZ) -# -# def addTemperature(self, temperature): -# self._addTemperatureCallback(temperature) -# self._changeEvent.set() -# - -# -# def addLog(self, log): -# self._addLogCallback(log) -# self._changeEvent.set() -# -# def addMessage(self, message): -# self._addMessageCallback(message) -# self._changeEvent.set() -# -# def setCurrentZ(self, currentZ): -# self._currentZ = currentZ -# self._changeEvent.set() -# -# def setState(self, state): -# with self._stateMutex: -# self._state = state -# self._changeEvent.set() -# -# def setJobData(self, jobData): -# self._jobData = jobData -# self._changeEvent.set() -# -# def setProgress(self, progress): -# self._progress = progress -# self._changeEvent.set() -# -# def setTempOffsets(self, offsets): -# self._offsets = offsets -# self._changeEvent.set() -# -# def _work(self): -# while True: -# self._changeEvent.wait() -# -# with self._stateMutex: -# now = time.time() -# delta = now - self._lastUpdate -# additionalWaitTime = self._ratelimit - delta -# if additionalWaitTime > 0: -# time.sleep(additionalWaitTime) -# -# data = self.getCurrentData() -# self._updateCallback(data) -# self._lastUpdate = time.time() -# self._changeEvent.clear() -# -# def getCurrentData(self): -# return { -# "state": self._state, -# "job": self._jobData, -# "machinePosition": self._machinePosition, -# "workPosition": self._workPosition, -# "currentZ": self._currentZ, -# "progress": self._progress, -# "offsets": self._offsets -# } -# -# -#class TimeEstimationHelper(object): -# -# STABLE_THRESHOLD = 0.1 -# STABLE_COUNTDOWN = 250 -# STABLE_ROLLING_WINDOW = 250 -# -# def __init__(self): -# import collections -# self._distances = collections.deque([], self.__class__.STABLE_ROLLING_WINDOW) -# self._totals = collections.deque([], self.__class__.STABLE_ROLLING_WINDOW) -# self._sum_total = 0 -# self._count = 0 -# self._stable_counter = None -# -# def is_stable(self): -# return self._stable_counter is not None and self._stable_counter >= self.__class__.STABLE_COUNTDOWN -# -# def update(self, newEstimate): -# old_average_total = self.average_total -# -# self._sum_total += newEstimate -# self._totals.append(newEstimate) -# self._count += 1 -# -# if old_average_total: -# self._distances.append(abs(self.average_total - old_average_total)) -# -# if -1.0 * self.__class__.STABLE_THRESHOLD < self.average_distance < self.__class__.STABLE_THRESHOLD: -# if self._stable_counter is None: -# self._stable_counter = 0 -# else: -# self._stable_counter += 1 -# else: -# self._stable_counter = None -# -# @property -# def average_total(self): -# if not self._count: -# return None -# else: -# return self._sum_total / self._count -# -# @property -# def average_total_rolling(self): -# if not self._count or self._count < self.__class__.STABLE_ROLLING_WINDOW: -# return None -# else: -# return sum(self._totals) / len(self._totals) -# -# @property -# def average_distance(self): -# if not self._count or self._count < self.__class__.STABLE_ROLLING_WINDOW + 1: -# return None -# else: -# return sum(self._distances) / len(self._distances) -#======= pass -#>>>>>>> upstream/maintenance class UnknownScript(Exception): def __init__(self, name, *args, **kwargs): diff --git a/src/octoprint/server/views.py b/src/octoprint/server/views.py index f0bc70d9..058b4848 100644 --- a/src/octoprint/server/views.py +++ b/src/octoprint/server/views.py @@ -168,9 +168,7 @@ def index(): plugin_names = set() plugins_with_hidden_settings = ['pluginmanager'] for implementation in template_plugins: - print('plugin ', implementation._identifier) if(implementation._identifier in plugins_with_hidden_settings): - print("blocked") continue name = implementation._identifier plugin_names.add(name) diff --git a/src/octoprint/static/js/app/dataupdater.js b/src/octoprint/static/js/app/dataupdater.js index 63382bbc..0a734694 100644 --- a/src/octoprint/static/js/app/dataupdater.js +++ b/src/octoprint/static/js/app/dataupdater.js @@ -173,7 +173,7 @@ function DataUpdater(allViewModels) { break; } case "event": { - console.log("Event", data); + //console.log("Event", data); var type = data["type"]; var payload = data["payload"]; var html = ""; @@ -235,6 +235,8 @@ function DataUpdater(allViewModels) { hide: false, text: msg }); + } else if (type === "RealTimeState") { + } var legacyEventHandlers = { diff --git a/src/octoprint/static/js/app/viewmodels/printerstate.js b/src/octoprint/static/js/app/viewmodels/printerstate.js index 3af36bb6..7a6da552 100644 --- a/src/octoprint/static/js/app/viewmodels/printerstate.js +++ b/src/octoprint/static/js/app/viewmodels/printerstate.js @@ -122,9 +122,6 @@ $(function() { self._processProgressData(data.progress); self._processZData(data.currentZ); self._processBusyFiles(data.busyFiles); - if(data.workPosition){ - self._processPos(data.workPosition); - } }; self._processStateData = function(data) { var prevPaused = self.isPaused(); @@ -272,12 +269,8 @@ $(function() { }); }; - self._processPos = function (posStr) { - // example posStr: "X: 73.0000 Y: 192.0000 Z: 0.0000" - var parts = posStr.split(" "); - var x = parseFloat(parts[1]).toFixed(2); - var y = parseFloat(parts[3]).toFixed(2); - self.currentPos({x: x, y: y}); + self.onEventRealTimeState = function(payload){ + self.currentPos({x: payload.wx, y: payload.wy}); }; } diff --git a/src/octoprint/util/comm_acc.py b/src/octoprint/util/comm_acc.py index 938e11a7..73e34f82 100644 --- a/src/octoprint/util/comm_acc.py +++ b/src/octoprint/util/comm_acc.py @@ -156,19 +156,22 @@ class MachineCom(object): self._currentExtruder = 0 self._timeout = None + self._temperature_timer = None + self._alwaysSendChecksum = settings().getBoolean(["feature", "alwaysSendChecksum"]) self._currentLine = 1 self._resendDelta = None self._lastLines = deque([], 50) - # enabled grbl mode if requested self._grbl = settings().getBoolean(["feature", "grbl"]) self.line_lengths = [] # store char count of all send commands until ok is received self.gcode_line_counter = 0 # counter of successful sent gcode lines self.RX_BUFFER_SIZE = 127 # size of the Arduino RX Buffer. TODO: put in machine profile + self._laserOn = False # flag if laser is active or not + # hooks self._pluginManager = octoprint.plugin.plugin_manager() self._gcode_hooks = self._pluginManager.get_hooks("octoprint.comm.protocol.gcode") @@ -360,8 +363,16 @@ class MachineCom(object): return self._port, self._baudrate ##~~ external interface - + def close(self, isError = False): + if self._temperature_timer is not None: + try: + self._temperature_timer.cancel() + except: + pass + #self._monitoring_active = False + #self._send_queue_active = False + printing = self.isPrinting() or self.isPaused() if self._serial is not None: if isError: @@ -508,13 +519,8 @@ class MachineCom(object): self._pauseWaitStartTime = None self._changeState(self.STATE_PRINTING) - if(self._laserOn): - self.sendCommand('M3') self.sendCommand('~') - if self.isSdFileSelected(): - self.sendCommand("M24") - else: - self._sendNext() + self._sendNext() eventManager().fire(Events.PRINT_RESUMED, { "file": self._currentFile.getFilename(), @@ -525,12 +531,9 @@ class MachineCom(object): if not self._pauseWaitStartTime: self._pauseWaitStartTime = time.time() - self.sendCommand('M5') self.sendCommand('!') self._changeState(self.STATE_PAUSED) - if self.isSdFileSelected(): - self.sendCommand("M25") # pause print - + eventManager().fire(Events.PRINT_PAUSED, { "file": self._currentFile.getFilename(), "filename": os.path.basename(self._currentFile.getFilename()), @@ -666,7 +669,7 @@ class MachineCom(object): self._bedTemp = (actual, None) def _monitor(self): - ### TODO hack + ### TODO hack. Should trigger a pause? #feedbackControls = settings().getFeedbackControls() feedbackControls = None #pauseTriggers = settings().getPauseTriggers() @@ -687,13 +690,12 @@ class MachineCom(object): #Start monitoring the serial port. self._timeout = get_new_timeout("communication") - tempRequestTimeout = get_new_timeout("temperature") - sdStatusRequestTimeout = get_new_timeout("sdStatus") + #tempRequestTimeout = get_new_timeout("temperature") + #sdStatusRequestTimeout = get_new_timeout("sdStatus") startSeen = not settings().getBoolean(["feature", "waitForStartOnConnect"]) heatingUp = False swallowOk = False - supportRepetierTargetTemp = settings().getBoolean(["feature", "repetierTargetTemp"]) grblMoving = True grblLastStatus = "" @@ -702,8 +704,8 @@ class MachineCom(object): line = self._readline() if line is None: break - if line.strip() is not "": - self._timeout = get_new_timeout("communication") + #if line.strip() is not "": + # self._timeout = get_new_timeout("communication") ##~~ debugging output handling if line.startswith("//"): @@ -836,11 +838,7 @@ class MachineCom(object): self._baudrateDetectRetry -= 1 self._serial.write('\n') self._log("Baudrate test retry: %d" % (self._baudrateDetectRetry)) - # self._sendCommand("M105") - if self._grbl: - self._sendCommand("$") - else: - self._sendCommand("M105") + self._sendCommand("$") self._testingBaudrate = True else: baudrate = self._baudrateDetectList.pop(0) @@ -850,58 +848,25 @@ class MachineCom(object): self._log("Trying baudrate: %d" % (baudrate)) self._baudrateDetectRetry = 5 self._baudrateDetectTestOk = 0 - self._timeout = get_new_timeout("communication") + #self._timeout = get_new_timeout("communication") self._serial.write('\n') - # self._sendCommand("M105") - if self._grbl: - self._sendCommand("$") - else: - self._sendCommand("M105") + self._sendCommand("$") self._testingBaudrate = True except: self._log("Unexpected error while setting baudrate: %d %s" % (baudrate, get_exception_string())) elif self._grbl and '$$' in line: self._log("Baudrate test ok: %d" % (self._baudrateDetectTestOk)) self._changeState(self.STATE_OPERATIONAL) - elif 'ok' in line and 'T:' in line: - self._baudrateDetectTestOk += 1 - if self._baudrateDetectTestOk < 10: - self._log("Baudrate test ok: %d" % (self._baudrateDetectTestOk)) - self._sendCommand("M105") - else: - self._sendCommand("M999") - self._serial.timeout = settings().getFloat(["serial", "timeout", "connection"]) - self._changeState(self.STATE_OPERATIONAL) - if self._sdAvailable: - self.refreshSdFiles() - else: - self.initSdCard() - eventManager().fire(Events.CONNECTED, {"port": self._port, "baudrate": self._baudrate}) else: self._testingBaudrate = False ### Connection attempt elif self._state == self.STATE_CONNECTING: - if self._grbl: - if "Grbl" in line: - self._changeState(self.STATE_LOCKED) - self.line_lengths = [] - self._log("connected. reseting character counter.") + if "Grbl" in line: + self._onConnected() - else: - if (line == "" or "wait" in line) and startSeen: - self._sendCommand("M105") - elif "start" in line: - startSeen = True - elif "ok" in line and startSeen: - self._changeState(self.STATE_OPERATIONAL) - if self._sdAvailable: - self.refreshSdFiles() - else: - self.initSdCard() - eventManager().fire(Events.CONNECTED, {"port": self._port, "baudrate": self._baudrate}) - elif time.time() > self._timeout: - self.close() + elif time.time() > self._timeout: + self.close() ### Operational elif self._state == self.STATE_OPERATIONAL or self._state == self.STATE_PAUSED or self._state == self.STATE_LOCKED: @@ -912,15 +877,13 @@ class MachineCom(object): elif not self._commandQueue.empty(): self._sendCommand(self._commandQueue.get()) else: - if self._grbl: - self._sendCommand("?") - else: - self._sendCommand("M105") - - if self._grbl: - tempRequestTimeout = get_new_timeout("position") - else: - tempRequestTimeout = get_new_timeout("detection") + pass +# self._sendCommand("?") +# +# if self._grbl: +# tempRequestTimeout = get_new_timeout("position") +# else: +# tempRequestTimeout = get_new_timeout("detection") ###print(tempRequestTimeout) # resend -> start resend procedure from requested line @@ -938,41 +901,35 @@ class MachineCom(object): else: line = "" - if self.isSdPrinting(): - if time.time() > tempRequestTimeout and not heatingUp: - if not self._grbl: - self._sendCommand("M105") - tempRequestTimeout = get_new_timeout("temperature") + # Even when printing request the temperature every 5 seconds. +# if time.time() > tempRequestTimeout and not self.isStreaming(): +# if self._grbl: +# self._commandQueue.put("?") +# tempRequestTimeout = get_new_timeout("position") +# else: +# self._commandQueue.put("M105") +# tempRequestTimeout = get_new_timeout("temperature") - if time.time() > sdStatusRequestTimeout and not heatingUp: - self._sendCommand("M27") - sdStatusRequestTimeout = get_new_timeout("sdStatus") - else: - # Even when printing request the temperature every 5 seconds. - if time.time() > tempRequestTimeout and not self.isStreaming(): - if self._grbl: - # disabled, caused race conditions during streaming resulting in a Invalid Gcode ID24 - self._commandQueue.put("?") - tempRequestTimeout = get_new_timeout("position") - #pass - else: - self._commandQueue.put("M105") - tempRequestTimeout = get_new_timeout("temperature") + if "ok" in line and swallowOk: + swallowOk = False + elif "ok" in line: - if "ok" in line and swallowOk: - swallowOk = False - elif "ok" in line: + if self._resendDelta is not None: + self._resendNextCommand() + elif not self._commandQueue.empty() and not self.isStreaming(): + self._sendCommand(self._commandQueue.get(), True) + else: + self._sendNext() + elif line.lower().startswith("resend") or line.lower().startswith("rs"): + if settings().get(["feature", "swallowOkAfterResend"]): + swallowOk = True + self._handleResendRequest(line) + +# # pos update +# if time.time() > tempRequestTimeout and not self.isStreaming(): +# self._commandQueue.put("?") +# tempRequestTimeout = get_new_timeout("position") - if self._resendDelta is not None: - self._resendNextCommand() - elif not self._commandQueue.empty() and not self.isStreaming(): - self._sendCommand(self._commandQueue.get(), True) - else: - self._sendNext() - elif line.lower().startswith("resend") or line.lower().startswith("rs"): - if settings().get(["feature", "swallowOkAfterResend"]): - swallowOk = True - self._handleResendRequest(line) except: self._logger.exception("Something crashed inside the serial connection loop, please report this in OctoPrint's bug tracker:") @@ -983,6 +940,27 @@ class MachineCom(object): eventManager().fire(Events.ERROR, {"error": self.getErrorString()}) self._log("Connection closed, closing down monitor") + def _poll_temperature(self): + """ + Polls the temperature after the temperature timeout, re-enqueues itself. + + If the printer is not operational, not printing from sd, busy with a long running command or heating, no poll + will be done. + """ + if (self.isOperational() or self.isLocked() or self.isPrinting()) and not self.isStreaming() : + #self.sendCommand("?", cmd_type="temperature_poll") + self.sendCommand("?") + + def _onConnected(self): + self._changeState(self.STATE_LOCKED) + self.line_lengths = [] + self._log("connected. reseting character counter.") + #self._temperature_timer = RepeatedTimer(lambda: get_interval("0.1"), self._poll_temperature, run_first=True) + self._temperature_timer = RepeatedTimer(0.1, self._poll_temperature, run_first=True) + self._temperature_timer.start() + eventManager().fire(Events.CONNECTED, {"port": self._port, "baudrate": self._baudrate}) + + def _openSerial(self): if self._port == 'AUTO': self._changeState(self.STATE_DETECT_SERIAL) @@ -1201,15 +1179,15 @@ class MachineCom(object): realtime_cmd = False if(cmd in REALTIME_COMMANDS): realtime_cmd = True - - self._log("Send: %s" % cmd) + + if(cmd != '?'): + self._log("Send: %s" % cmd) chars_in_buffer = sum(self.line_lengths) # TODO needs debugging. it interferes with ordinary commands. if(not realtime_cmd and (chars_in_buffer + len(cmd)+1 >= self.RX_BUFFER_SIZE-1)): # queue command if arduino serial buffer is full self._log("RX buffer [%d/%d], queuing cmd %d : %s" % (chars_in_buffer, self.RX_BUFFER_SIZE, self.gcode_line_counter, cmd)) self._commandQueue.put(cmd) else: - #if(not realtime_cmd): self.line_lengths.append(len(cmd)+1) # count chars sent to the arduino try: @@ -1319,15 +1297,43 @@ class MachineCom(object): return cmd def _update_grbl_pos(self, line): - parts = line.strip("\r\n").split(":") + # line example: + # + try: + idx_mx_begin = line.index('MPos:') + 5 + idx_mx_end = line.index('.', idx_mx_begin) + 2 + idx_my_begin = line.index(',', idx_mx_end) + 1 + idx_my_end = line.index('.', idx_my_begin) + 2 + idx_mz_begin = line.index(',', idx_my_end) + 1 + idx_mz_end = line.index('.', idx_mz_begin) + 2 - pos = parts[1].split(",") - MPos = (float(pos[0]), float(pos[1]), float(pos[2])) + idx_wx_begin = line.index('WPos:') + 5 + idx_wx_end = line.index('.', idx_wx_begin) + 2 + idx_wy_begin = line.index(',', idx_wx_end) + 1 + idx_wy_end = line.index('.', idx_wy_begin) + 2 + idx_wz_begin = line.index(',', idx_wy_end) + 1 + idx_wz_end = line.index('.', idx_wz_begin) + 2 + + idx_intensity_begin = line.index('S:', idx_wz_end) + 2 + idx_intensity_end = line.index(',', idx_intensity_begin) - pos = parts[2].split(",") - WPos = (float(pos[0]), float(pos[1]), float( pos[2].strip(">") )) + idx_laserstate_begin = line.index('laser ', idx_intensity_end) + 6 + idx_laserstate_end = line.index(':', idx_laserstate_begin) - self._callback.on_comm_pos_update(MPos, WPos) + payload = { + "mx": line[idx_mx_begin:idx_mx_end], + "my": line[idx_my_begin:idx_my_end], + "mz": line[idx_mz_begin:idx_mz_end], + "wx": line[idx_wx_begin:idx_wx_end], + "wy": line[idx_wy_begin:idx_wy_end], + "wz": line[idx_wz_begin:idx_wz_end], + "laser": line[idx_laserstate_begin:idx_laserstate_end], + "intensity": line[idx_intensity_begin:idx_intensity_end] + } + eventManager().fire(Events.RT_STATE, payload) + except ValueError: + pass + ### MachineCom callback ################################################################################################ @@ -1578,7 +1584,8 @@ class StreamingGcodeFileInformation(PrintingGcodeFileInformation): def get_new_timeout(type): now = time.time() - return now + get_interval(type) + interval = get_interval(type) + return now + interval def get_interval(type):