From 146b3c25f114bba59217821a4927a43345bb9cfe Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Tue, 8 Dec 2015 09:36:00 +0100 Subject: [PATCH 01/10] first steps towards nozzle camera supported nozzle calibration UI --- octoprint_OctoPNP/__init__.py | 34 ++++++++++++++++--- octoprint_OctoPNP/static/js/OctoPNP.js | 2 +- .../templates/OctoPNP_settings.jinja2 | 17 ++++++++++ 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/octoprint_OctoPNP/__init__.py b/octoprint_OctoPNP/__init__.py index 4e05c84..3ec697e 100644 --- a/octoprint_OctoPNP/__init__.py +++ b/octoprint_OctoPNP/__init__.py @@ -22,6 +22,7 @@ import octoprint.plugin +import flask import re from subprocess import call import os @@ -50,7 +51,8 @@ class OctoPNP(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplatePlugin, octoprint.plugin.EventHandlerPlugin, octoprint.plugin.SettingsPlugin, - octoprint.plugin.AssetPlugin): + octoprint.plugin.AssetPlugin, + octoprint.plugin.SimpleApiPlugin): STATE_NONE = 0 STATE_PICK = 1 @@ -114,16 +116,40 @@ def get_settings_defaults(self): def get_template_configs(self): return [ - dict(type="tab", custom_bindings=True), - dict(type="settings", custom_bindings=False) + dict(type="tab", template="OctoPNP_tab.jinja2", custom_bindings=True), + dict(type="settings", template="OctoPNP_settings.jinja2", custom_bindings=True) + #dict(type="settings", custom_bindings=True) ] def get_assets(self): return dict( js=["js/OctoPNP.js", - "js/smdTray.js"] + "js/smdTray.js", + "js/settings.js"] ) + # Define possible requests from GUI + def get_api_commands(self): + return dict( + getCameraImage=["imagetype"] + ) + + def on_api_command(self, command, data): + import flask + if command == "getCameraImage": + if data.get("imagetype") == "HEAD": + # Retrieve image from camera + if self._grabImages(): + headPath = self._settings.get(["camera", "head", "path"]) + + #update UI + self._updateUI("HEADIMAGE", headPath) + self._logger.info("getCameraImage, type: " + data.get("imagetype")) + + def on_api_get(self, request): + print request + return flask.jsonify(foo="bar") + # Use the on_event hook to extract XML data every time a new file has been loaded by the user def on_event(self, event, payload): #extraxt part informations from inline xmly diff --git a/octoprint_OctoPNP/static/js/OctoPNP.js b/octoprint_OctoPNP/static/js/OctoPNP.js index 7f32db1..777c2a1 100644 --- a/octoprint_OctoPNP/static/js/OctoPNP.js +++ b/octoprint_OctoPNP/static/js/OctoPNP.js @@ -101,6 +101,6 @@ $(function() { ["settingsViewModel", "controlViewModel", "connectionViewModel"], // Finally, this is the list of all elements we want this view model to be bound to. - [document.getElementById("tab_plugin_OctoPNP"), document.getElementById("settings_plugin_OctoPNP")] + "#tab_plugin_OctoPNP" ]); }); diff --git a/octoprint_OctoPNP/templates/OctoPNP_settings.jinja2 b/octoprint_OctoPNP/templates/OctoPNP_settings.jinja2 index eb49d15..d99d272 100644 --- a/octoprint_OctoPNP/templates/OctoPNP_settings.jinja2 +++ b/octoprint_OctoPNP/templates/OctoPNP_settings.jinja2 @@ -1,4 +1,5 @@
+ test:
@@ -257,5 +258,21 @@
+ + + +
+ +
+
+
+ +
+
+
+
+
\ No newline at end of file From e1b120ca04724d8ade095be154b595fc40871d8f Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Mon, 14 Dec 2015 17:45:58 +0100 Subject: [PATCH 02/10] crosshair-canvas, key-controls and UI --- octoprint_OctoPNP/__init__.py | 32 ++++++++-- .../templates/OctoPNP_settings.jinja2 | 58 ++++++++++++++++++- 2 files changed, 84 insertions(+), 6 deletions(-) diff --git a/octoprint_OctoPNP/__init__.py b/octoprint_OctoPNP/__init__.py index 3ec697e..b826bdf 100644 --- a/octoprint_OctoPNP/__init__.py +++ b/octoprint_OctoPNP/__init__.py @@ -52,7 +52,8 @@ class OctoPNP(octoprint.plugin.StartupPlugin, octoprint.plugin.EventHandlerPlugin, octoprint.plugin.SettingsPlugin, octoprint.plugin.AssetPlugin, - octoprint.plugin.SimpleApiPlugin): + octoprint.plugin.SimpleApiPlugin, + octoprint.plugin.BlueprintPlugin): STATE_NONE = 0 STATE_PICK = 1 @@ -147,9 +148,27 @@ def on_api_command(self, command, data): self._logger.info("getCameraImage, type: " + data.get("imagetype")) def on_api_get(self, request): - print request + #if "force" in flask.request.values and flask.request.values["force"] + print request.values + #flask.request.values return flask.jsonify(foo="bar") + @octoprint.plugin.BlueprintPlugin.route("/camera_image", methods=["GET"]) + def getCameraImage(self): + result = "" + if "imagetype" in flask.request.values: + if flask.request.values["imagetype"] == "HEAD": + if self._grabImages(): + headPath = self._settings.get(["camera", "head", "path"]) + try: + f = open(headPath,"r") + result = flask.jsonify(src="data:image/" + os.path.splitext(headPath)[1] + ";base64,"+base64.b64encode(bytes(f.read()))) + except IOError: + result = flask.jsonify(error="Unable to open Image after fetching. Image path: " + headPath) + else: + result = flask.jsonify(error="Unable to fetch image. Check octoprint log for details.") + return flask.make_response(result, 200) + # Use the on_event hook to extract XML data every time a new file has been loaded by the user def on_event(self, event, payload): #extraxt part informations from inline xmly @@ -396,8 +415,13 @@ def _grabImages(self): result = True grabScript = self._settings.get(["camera", "grabScriptPath"]) #os.path.dirname(os.path.realpath(__file__)) + "/cameras/grab.sh" - if call([grabScript]) != 0: - self._logger.info("ERROR: camera not ready!") + try: + if call([grabScript]) != 0: + self._logger.info("ERROR: camera not ready!") + result = False + except: + self._logger.info("ERROR: Unable to execute camera grab script!") + self._logger.info("Script path: " + grabScript) result = False return result diff --git a/octoprint_OctoPNP/templates/OctoPNP_settings.jinja2 b/octoprint_OctoPNP/templates/OctoPNP_settings.jinja2 index d99d272..c23dbbf 100644 --- a/octoprint_OctoPNP/templates/OctoPNP_settings.jinja2 +++ b/octoprint_OctoPNP/templates/OctoPNP_settings.jinja2 @@ -1,5 +1,4 @@
- test:
@@ -267,8 +266,63 @@
+ +
+
+ This is an experimental "calibration wizard". +
+
+ +
+
+
+ + +
+ +
{{ _("Keyboard controls active") }}
+ +
+
+ / : {{ _("X Axis") }} +/-
+ / : {{ _("Y Axis") }} +/-
+
+
+ 1, 2, 3, 4: {{ _("Stepsize") }} 0.1, 1, 10, 100mm +
+
+
+
+ {{ _("Hint: If you move your mouse over the picture, you enter keyboard control mode.") }} +
+
+
+
+

E0 <-> HeadCamera

+ + +

+ +

Tray position

+ +
+ + + +

+

Object pos. on printbed

+
+ + +
+ +
+
- +
+ Current X-offset: 0 + correction: + Current Y-offset: 0 + correction: +
From e6f72137bb909154a757a0d67fbe50b756404411 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Mon, 14 Dec 2015 18:24:10 +0100 Subject: [PATCH 03/10] forgot settings javascript file... --- octoprint_OctoPNP/static/js/settings.js | 218 ++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 octoprint_OctoPNP/static/js/settings.js diff --git a/octoprint_OctoPNP/static/js/settings.js b/octoprint_OctoPNP/static/js/settings.js new file mode 100644 index 0000000..d557bd5 --- /dev/null +++ b/octoprint_OctoPNP/static/js/settings.js @@ -0,0 +1,218 @@ +$(function() { + function OctoPNPSettingsViewModel(parameters) { + var self = this; + + self.settings = parameters[0]; + self.control = parameters[1]; + self.connection = parameters[2]; + + self._headCanvas = document.getElementById('headCanvas'); + + self.objectPositionX = ko.observable(0.0); + self.objectPositionY = ko.observable(0.0); + + self.offsetCorrectionX = ko.observable(0.0); + self.offsetCorrectionY = ko.observable(0.0); + self.jogDistance = ko.observable(1.0); + + self.isConnected = ko.computed(function() { + return self.connection.isOperational() || self.connection.isReady() || self.connection.isPaused(); + }); + + self.statusCameraOffset = ko.observable(false); + + self.keycontrolPossible = ko.observable(false); + self.keycontrolActive = ko.observable(false); + self.showKeycontrols = ko.observable(true); + self.keycontrolHelpActive = ko.observable(true); + + + // This will get called before the ViewModel gets bound to the DOM, but after its depedencies have + // already been initialized. It is especially guaranteed that this method gets called _after_ the settings + // have been retrieved from the OctoPrint backend and thus the SettingsViewModel been properly populated. + self.onBeforeBinding = function() { + self.settings = self.settings.settings; + }; + + // Calibrate offset between primary extruder and head-camera + self.cameraOffset = function() { + self.statusCameraOffset(true); + + // Switch to primary extruder + self.control.sendCustomCommand({command: "T0"}); + + //move camera to object + var x = self.objectPositionX() - self.settings.plugins.OctoPNP.camera.head.x(); + var y = self.objectPositionY() - self.settings.plugins.OctoPNP.camera.head.y(); + self.control.sendCustomCommand({command: "G1 X" + x + " Y" + y + " Z" + self.settings.plugins.OctoPNP.camera.head.z() + " F3000"}); + + //reset offset correction values + self.offsetCorrectionX(0.0); + self.offsetCorrectionY(0.0); + + //activate Keycontrol + self.keycontrolPossible(true); + + //trigger immage fetching + setTimeout(function() {self._getImage('HEAD');}, 5000); + } + + self.saveCameraOffset = function() { + //save values... + + //deactivate Keycontrol + self.keycontrolPossible(false); + self.statusCameraOffset(false); + } + + self.requestImage = function() { + self._getImage("HEAD"); + }; + + self._getImage = function(imagetype, callback) { + $.ajax({ + url: PLUGIN_BASEURL + "OctoPNP/camera_image?imagetype=" + imagetype, + type: "GET", + dataType: "json", + contentType: "application/json; charset=UTF-8", + //data: JSON.stringify(data), + success: function(response) { + if(response.hasOwnProperty("src")) { + self._drawHeadImage(response.src); + } + if(response.hasOwnProperty("error")) { + alert(response.error); + } + if (callback) callback(); + } + }); + }; + + self._drawHeadImage = function(img) { + var ctx=self._headCanvas.getContext("2d"); + var localimg = new Image(); + localimg.src = img; + localimg.onload = function () { + var w = localimg.width; + var h = localimg.height; + var scale = Math.min(ctx.canvas.clientWidth/w, ctx.canvas.clientHeight/h,1); + ctx.drawImage(localimg, 0, 0, w*scale, h*scale); + + // crosshairs + ctx.beginPath(); + ctx.strokeStyle = "#000000"; + ctx.lineWidth = 1; + ctx.fillStyle = "#000000"; + ctx.fillRect(0, ((h*scale)/2)-0.5, w*scale, 1); + ctx.fillRect(((w*scale)/2)-0.5, 0, 1, h*scale); + }; + }; + + self.onFocus = function (data, event) { + if (!self.keycontrolPossible) return; + self.keycontrolActive(true); + }; + + self.onMouseOver = function (data, event) { + if (!self.keycontrolPossible) return; + $("#webcam_container").focus(); + self.keycontrolActive(true); + }; + + self.onMouseOut = function (data, event) { + $("#webcam_container").blur(); + self.keycontrolActive(false); + }; + + self.toggleKeycontrolHelp = function () { + self.keycontrolHelpActive(!self.keycontrolHelpActive()); + }; + + self.onKeyDown = function (data, event) { + var refreshImage = false; + + switch (event.which) { + case 37: // left arrow key + // X- + self.control.sendJogCommand("x", -1, self.jogDistance()); + self.offsetCorrectionX(self.offsetCorrectionX()-self.jogDistance()); + refreshImage = true; + break; + case 38: // up arrow key + // Y+ + self.control.sendJogCommand("y", 1, self.jogDistance()); + self.offsetCorrectionY(self.offsetCorrectionY()+self.jogDistance()); + refreshImage = true; + break; + case 39: // right arrow key + // X+ + self.control.sendJogCommand("x", 1, self.jogDistance()); + self.offsetCorrectionX(self.offsetCorrectionX()+self.jogDistance()); + refreshImage = true; + break; + case 40: // down arrow key + // Y- + self.control.sendJogCommand("y", -1, self.jogDistance()); + self.offsetCorrectionY(self.offsetCorrectionY()-self.jogDistance()); + refreshImage = true; + break; + case 49: // number 1 + case 97: // numpad 1 + // Distance 0.1 + self.jogDistance(0.1); + break; + case 50: // number 2 + case 98: // numpad 2 + // Distance 1 + self.jogDistance(1.0); + break; + case 51: // number 3 + case 99: // numpad 3 + // Distance 10 + self.jogDistance(10.0); + break; + case 52: // number 4 + case 100: // numpad 4 + // Distance 100 + self.jogDistance(100.0); + break; + case 33: // page up key + case 87: // w key + // z lift up + break; + case 34: // page down key + case 83: // s key + // z lift down + break; + case 36: // home key + // xy home + break; + case 35: // end key + // z home + break; + default: + event.preventDefault(); + return false; + } + if(refreshImage) { + setTimeout(function() {self._getImage('HEAD');}, 200); + } + }; + } + + + // This is how our plugin registers itself with the application, by adding some configuration information to + // the global variable ADDITIONAL_VIEWMODELS + ADDITIONAL_VIEWMODELS.push([ + // This is the constructor to call for instantiating the plugin + OctoPNPSettingsViewModel, + + // This is a list of dependencies to inject into the plugin, the order which you request here is the order + // in which the dependencies will be injected into your view model upon instantiation via the parameters + // argument + ["settingsViewModel", "controlViewModel", "connectionViewModel"], + + // Finally, this is the list of all elements we want this view model to be bound to. + "#settings_plugin_OctoPNP" + ]); +}); From 92c6f533e249c848a1c2ecb6c075b4d3362f98b0 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Tue, 15 Dec 2015 13:37:07 +0100 Subject: [PATCH 04/10] camera and tray calibration working, extruder and vacnozzle in alpha state --- octoprint_OctoPNP/__init__.py | 4 +- octoprint_OctoPNP/static/js/settings.js | 121 +++++++++++++++++- .../templates/OctoPNP_settings.jinja2 | 18 ++- 3 files changed, 134 insertions(+), 9 deletions(-) diff --git a/octoprint_OctoPNP/__init__.py b/octoprint_OctoPNP/__init__.py index b826bdf..0bf6b99 100644 --- a/octoprint_OctoPNP/__init__.py +++ b/octoprint_OctoPNP/__init__.py @@ -157,9 +157,9 @@ def on_api_get(self, request): def getCameraImage(self): result = "" if "imagetype" in flask.request.values: - if flask.request.values["imagetype"] == "HEAD": + if (flask.request.values["imagetype"] == "HEAD") or (flask.request.values["imagetype"] == "BED"): if self._grabImages(): - headPath = self._settings.get(["camera", "head", "path"]) + headPath = self._settings.get(["camera", flask.request.values["imagetype"].lower(), "path"]) try: f = open(headPath,"r") result = flask.jsonify(src="data:image/" + os.path.splitext(headPath)[1] + ";base64,"+base64.b64encode(bytes(f.read()))) diff --git a/octoprint_OctoPNP/static/js/settings.js b/octoprint_OctoPNP/static/js/settings.js index d557bd5..3a256bc 100644 --- a/octoprint_OctoPNP/static/js/settings.js +++ b/octoprint_OctoPNP/static/js/settings.js @@ -20,6 +20,8 @@ $(function() { }); self.statusCameraOffset = ko.observable(false); + self.statusTrayPosition = ko.observable(false); + self.statusExtruderOffset = ko.observable(false); self.keycontrolPossible = ko.observable(false); self.keycontrolActive = ko.observable(false); @@ -36,6 +38,9 @@ $(function() { // Calibrate offset between primary extruder and head-camera self.cameraOffset = function() { + //deactivate other processes + self.statusTrayPosition(false); + self.statusExtruderOffset(false); self.statusCameraOffset(true); // Switch to primary extruder @@ -59,14 +64,124 @@ $(function() { self.saveCameraOffset = function() { //save values... + self.settings.plugins.OctoPNP.camera.head.x(self.settings.plugins.OctoPNP.camera.head.x()-self.offsetCorrectionX()); + self.settings.plugins.OctoPNP.camera.head.y(self.settings.plugins.OctoPNP.camera.head.x()-self.offsetCorrectionY()); //deactivate Keycontrol self.keycontrolPossible(false); self.statusCameraOffset(false); - } + }; + + // Calibrate offset between primary and other extruder + self.extruderOffset = function() { + // initialize process... + self.cameraOffset(); + // and correct status variables + self.statusCameraOffset(false); + self.statusExtruderOffset(true); + + }; + + self.saveExtruderOffset = function() { + // Steps to save values: + // get current offset for extruder x from eeprom + // get steps per mm for x and y axis + // compute offset steps from offsetCorrection values + // save to eeprom + + // deactivate Keycontrol + self.keycontrolPossible(false); + self.statusExtruderOffset(false); + }; + + // calibrate tray position relative to primary extruder + self.trayPosition = function(corner) { + //deactivate other processes + self.statusCameraOffset(false); + self.statusExtruderOffset(false); + self.statusTrayPosition(true); + + // Switch to primary extruder + self.control.sendCustomCommand({command: "T0"}); + + //computer corner position + var cornerOffsetX = 0.0; + var cornerOffsetY = 0.0; + switch (corner) { + case "TL": + var rows = self.settings.plugins.OctoPNP.tray.rows(); + cornerOffsetY = rows*self.settings.plugins.OctoPNP.tray.boxsize() + (rows+1)*self.settings.plugins.OctoPNP.tray.rimsize(); + self.statusTrayPosition(false); + break; + case "TR": + var rows = self.settings.plugins.OctoPNP.tray.rows(); + var cols = self.settings.plugins.OctoPNP.tray.cols(); + cornerOffsetY = rows*self.settings.plugins.OctoPNP.tray.boxsize() + (rows+1)*self.settings.plugins.OctoPNP.tray.rimsize(); + cornerOffsetX = cols*self.settings.plugins.OctoPNP.tray.boxsize() + (cols+1)*self.settings.plugins.OctoPNP.tray.rimsize(); + self.statusTrayPosition(false); + break; + case "BR": + var cols = self.settings.plugins.OctoPNP.tray.cols(); + cornerOffsetX = cols*self.settings.plugins.OctoPNP.tray.boxsize() + (cols+1)*self.settings.plugins.OctoPNP.tray.rimsize(); + self.statusTrayPosition(false); + break; + default: + // BL is default case, the tray position is allways computed for this point. Saving the calibration + // is only possible for this case. + break; + } + + //move camera to tray + var x = self.settings.plugins.OctoPNP.tray.x() + cornerOffsetX - self.settings.plugins.OctoPNP.camera.head.x(); + var y = self.settings.plugins.OctoPNP.tray.y() + cornerOffsetY - self.settings.plugins.OctoPNP.camera.head.y(); + var z = self.settings.plugins.OctoPNP.tray.z() + self.settings.plugins.OctoPNP.camera.head.z(); + self.control.sendCustomCommand({command: "G1 X" + x + " Y" + y + " Z" + z + " F3000"}); + + //reset offset correction values + self.offsetCorrectionX(0.0); + self.offsetCorrectionY(0.0); - self.requestImage = function() { - self._getImage("HEAD"); + //activate Keycontrol + self.keycontrolPossible(true); + + //trigger immage fetching + setTimeout(function() {self._getImage('HEAD');}, 5000); + }; + + self.saveTrayPosition = function() { + //save values + self.settings.plugins.OctoPNP.tray.x(self.settings.plugins.OctoPNP.tray.x()+self.offsetCorrectionX()); + self.settings.plugins.OctoPNP.tray.y(self.settings.plugins.OctoPNP.tray.y()+self.offsetCorrectionY()); + + //deactivate Keycontrol + self.keycontrolPossible(false); + self.statusTrayPosition(false); + }; + + // Move Vacuum Nozzle to bed camera. It is currently not possible to save any offset here. + self.bedCameraPosition = function() { + //deactivate other processes + self.statusTrayPosition(false); + self.statusExtruderOffset(false); + self.statusCameraOffset(false); + + // Switch to VacNozzle extruder + self.control.sendCustomCommand({command: self.settings.plugins.OctoPNP.vacnozzle.extruder_nr().toString()}); + + //move camera to object + var x = self.settings.plugins.OctoPNP.camera.bed.x() - self.settings.plugins.OctoPNP.vacnozzle.x(); + var y = self.settings.plugins.OctoPNP.camera.bed.y() - self.settings.plugins.OctoPNP.vacnozzle.y(); + self.control.sendCustomCommand({command: "G1 X" + x + " Y" + y + " Z" + self.settings.plugins.OctoPNP.camera.bed.z() + " F3000"}); + + //reset offset correction values + self.offsetCorrectionX(0.0); + self.offsetCorrectionY(0.0); + + //activate Keycontrol + self.keycontrolPossible(true); + + //trigger immage fetching + setTimeout(function() {self._getImage('HEAD');}, 5000); }; self._getImage = function(imagetype, callback) { diff --git a/octoprint_OctoPNP/templates/OctoPNP_settings.jinja2 b/octoprint_OctoPNP/templates/OctoPNP_settings.jinja2 index c23dbbf..3ee3423 100644 --- a/octoprint_OctoPNP/templates/OctoPNP_settings.jinja2 +++ b/octoprint_OctoPNP/templates/OctoPNP_settings.jinja2 @@ -303,11 +303,21 @@

+

E0 <-> Ex

+ + +

+

Tray position

- -
- - + +
+ + + +

+ +

VacNozzle <-> BedCamera

+

Object pos. on printbed

From b7ddbb6fc223183a609932d995e68a3fa4c8ee0c Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Tue, 15 Dec 2015 14:03:23 +0100 Subject: [PATCH 05/10] fixed string conversion --- octoprint_OctoPNP/static/js/settings.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/octoprint_OctoPNP/static/js/settings.js b/octoprint_OctoPNP/static/js/settings.js index 3a256bc..12111ea 100644 --- a/octoprint_OctoPNP/static/js/settings.js +++ b/octoprint_OctoPNP/static/js/settings.js @@ -115,13 +115,13 @@ $(function() { break; case "TR": var rows = self.settings.plugins.OctoPNP.tray.rows(); - var cols = self.settings.plugins.OctoPNP.tray.cols(); + var cols = self.settings.plugins.OctoPNP.tray.columns(); cornerOffsetY = rows*self.settings.plugins.OctoPNP.tray.boxsize() + (rows+1)*self.settings.plugins.OctoPNP.tray.rimsize(); cornerOffsetX = cols*self.settings.plugins.OctoPNP.tray.boxsize() + (cols+1)*self.settings.plugins.OctoPNP.tray.rimsize(); self.statusTrayPosition(false); break; case "BR": - var cols = self.settings.plugins.OctoPNP.tray.cols(); + var cols = self.settings.plugins.OctoPNP.tray.columns(); cornerOffsetX = cols*self.settings.plugins.OctoPNP.tray.boxsize() + (cols+1)*self.settings.plugins.OctoPNP.tray.rimsize(); self.statusTrayPosition(false); break; @@ -134,7 +134,10 @@ $(function() { //move camera to tray var x = self.settings.plugins.OctoPNP.tray.x() + cornerOffsetX - self.settings.plugins.OctoPNP.camera.head.x(); var y = self.settings.plugins.OctoPNP.tray.y() + cornerOffsetY - self.settings.plugins.OctoPNP.camera.head.y(); - var z = self.settings.plugins.OctoPNP.tray.z() + self.settings.plugins.OctoPNP.camera.head.z(); + var z = parseFloat(self.settings.plugins.OctoPNP.tray.z()) + parseFloat(self.settings.plugins.OctoPNP.camera.head.z()); + console.log(self.settings.plugins.OctoPNP.tray.z()); + console.log(self.settings.plugins.OctoPNP.camera.head.z()); + console.log(z); self.control.sendCustomCommand({command: "G1 X" + x + " Y" + y + " Z" + z + " F3000"}); //reset offset correction values @@ -310,7 +313,7 @@ $(function() { return false; } if(refreshImage) { - setTimeout(function() {self._getImage('HEAD');}, 200); + setTimeout(function() {self._getImage('HEAD');}, 300); } }; } From 82536991dad91033c7700c9fe56d9a41afe8701b Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Tue, 15 Dec 2015 14:16:15 +0100 Subject: [PATCH 06/10] fixed more string conversions --- octoprint_OctoPNP/static/js/settings.js | 36 ++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/octoprint_OctoPNP/static/js/settings.js b/octoprint_OctoPNP/static/js/settings.js index 12111ea..705d7fe 100644 --- a/octoprint_OctoPNP/static/js/settings.js +++ b/octoprint_OctoPNP/static/js/settings.js @@ -47,8 +47,8 @@ $(function() { self.control.sendCustomCommand({command: "T0"}); //move camera to object - var x = self.objectPositionX() - self.settings.plugins.OctoPNP.camera.head.x(); - var y = self.objectPositionY() - self.settings.plugins.OctoPNP.camera.head.y(); + var x = self.objectPositionX() - parseFloat(self.settings.plugins.OctoPNP.camera.head.x()); + var y = self.objectPositionY() - parseFloat(self.settings.plugins.OctoPNP.camera.head.y()); self.control.sendCustomCommand({command: "G1 X" + x + " Y" + y + " Z" + self.settings.plugins.OctoPNP.camera.head.z() + " F3000"}); //reset offset correction values @@ -64,8 +64,8 @@ $(function() { self.saveCameraOffset = function() { //save values... - self.settings.plugins.OctoPNP.camera.head.x(self.settings.plugins.OctoPNP.camera.head.x()-self.offsetCorrectionX()); - self.settings.plugins.OctoPNP.camera.head.y(self.settings.plugins.OctoPNP.camera.head.x()-self.offsetCorrectionY()); + self.settings.plugins.OctoPNP.camera.head.x(parseFloat(self.settings.plugins.OctoPNP.camera.head.x())-self.offsetCorrectionX()); + self.settings.plugins.OctoPNP.camera.head.y(parseFloat(self.settings.plugins.OctoPNP.camera.head.x())-self.offsetCorrectionY()); //deactivate Keycontrol self.keycontrolPossible(false); @@ -109,20 +109,20 @@ $(function() { var cornerOffsetY = 0.0; switch (corner) { case "TL": - var rows = self.settings.plugins.OctoPNP.tray.rows(); - cornerOffsetY = rows*self.settings.plugins.OctoPNP.tray.boxsize() + (rows+1)*self.settings.plugins.OctoPNP.tray.rimsize(); + var rows = parseFloat(self.settings.plugins.OctoPNP.tray.rows()); + cornerOffsetY = rows*parseFloat(self.settings.plugins.OctoPNP.tray.boxsize()) + (rows+1)*parseFloat(self.settings.plugins.OctoPNP.tray.rimsize()); self.statusTrayPosition(false); break; case "TR": - var rows = self.settings.plugins.OctoPNP.tray.rows(); - var cols = self.settings.plugins.OctoPNP.tray.columns(); - cornerOffsetY = rows*self.settings.plugins.OctoPNP.tray.boxsize() + (rows+1)*self.settings.plugins.OctoPNP.tray.rimsize(); - cornerOffsetX = cols*self.settings.plugins.OctoPNP.tray.boxsize() + (cols+1)*self.settings.plugins.OctoPNP.tray.rimsize(); + var rows = parseFloat(self.settings.plugins.OctoPNP.tray.rows()); + var cols = parseFloat(self.settings.plugins.OctoPNP.tray.columns()); + cornerOffsetY = rows*parseFloat(self.settings.plugins.OctoPNP.tray.boxsize()) + (rows+1)*parseFloat(self.settings.plugins.OctoPNP.tray.rimsize()); + cornerOffsetX = cols*parseFloat(self.settings.plugins.OctoPNP.tray.boxsize()) + (cols+1)*parseFloat(self.settings.plugins.OctoPNP.tray.rimsize()); self.statusTrayPosition(false); break; case "BR": - var cols = self.settings.plugins.OctoPNP.tray.columns(); - cornerOffsetX = cols*self.settings.plugins.OctoPNP.tray.boxsize() + (cols+1)*self.settings.plugins.OctoPNP.tray.rimsize(); + var cols = parseFloat(self.settings.plugins.OctoPNP.tray.columns()); + cornerOffsetX = cols*parseFloat(self.settings.plugins.OctoPNP.tray.boxsize()) + (cols+1)*parseFloat(self.settings.plugins.OctoPNP.tray.rimsize()); self.statusTrayPosition(false); break; default: @@ -132,8 +132,8 @@ $(function() { } //move camera to tray - var x = self.settings.plugins.OctoPNP.tray.x() + cornerOffsetX - self.settings.plugins.OctoPNP.camera.head.x(); - var y = self.settings.plugins.OctoPNP.tray.y() + cornerOffsetY - self.settings.plugins.OctoPNP.camera.head.y(); + var x = parseFloat(self.settings.plugins.OctoPNP.tray.x()) + cornerOffsetX - parseFloat(self.settings.plugins.OctoPNP.camera.head.x()); + var y = parseFloat(self.settings.plugins.OctoPNP.tray.y()) + cornerOffsetY - parseFloat(self.settings.plugins.OctoPNP.camera.head.y()); var z = parseFloat(self.settings.plugins.OctoPNP.tray.z()) + parseFloat(self.settings.plugins.OctoPNP.camera.head.z()); console.log(self.settings.plugins.OctoPNP.tray.z()); console.log(self.settings.plugins.OctoPNP.camera.head.z()); @@ -153,8 +153,8 @@ $(function() { self.saveTrayPosition = function() { //save values - self.settings.plugins.OctoPNP.tray.x(self.settings.plugins.OctoPNP.tray.x()+self.offsetCorrectionX()); - self.settings.plugins.OctoPNP.tray.y(self.settings.plugins.OctoPNP.tray.y()+self.offsetCorrectionY()); + self.settings.plugins.OctoPNP.tray.x(parseFloat(self.settings.plugins.OctoPNP.tray.x())+self.offsetCorrectionX()); + self.settings.plugins.OctoPNP.tray.y(parseFloat(self.settings.plugins.OctoPNP.tray.y())+self.offsetCorrectionY()); //deactivate Keycontrol self.keycontrolPossible(false); @@ -172,8 +172,8 @@ $(function() { self.control.sendCustomCommand({command: self.settings.plugins.OctoPNP.vacnozzle.extruder_nr().toString()}); //move camera to object - var x = self.settings.plugins.OctoPNP.camera.bed.x() - self.settings.plugins.OctoPNP.vacnozzle.x(); - var y = self.settings.plugins.OctoPNP.camera.bed.y() - self.settings.plugins.OctoPNP.vacnozzle.y(); + var x = parseFloat(self.settings.plugins.OctoPNP.camera.bed.x()) - parseFloat(self.settings.plugins.OctoPNP.vacnozzle.x()); + var y = parseFloat(self.settings.plugins.OctoPNP.camera.bed.y()) - parseFloat(self.settings.plugins.OctoPNP.vacnozzle.y()); self.control.sendCustomCommand({command: "G1 X" + x + " Y" + y + " Z" + self.settings.plugins.OctoPNP.camera.bed.z() + " F3000"}); //reset offset correction values From 29af167b734cf5b4e1184e9bb4d703f20a70f7c6 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Tue, 15 Dec 2015 14:43:53 +0100 Subject: [PATCH 07/10] bed/head camera differentiation --- octoprint_OctoPNP/static/js/settings.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/octoprint_OctoPNP/static/js/settings.js b/octoprint_OctoPNP/static/js/settings.js index 705d7fe..28a1938 100644 --- a/octoprint_OctoPNP/static/js/settings.js +++ b/octoprint_OctoPNP/static/js/settings.js @@ -22,6 +22,7 @@ $(function() { self.statusCameraOffset = ko.observable(false); self.statusTrayPosition = ko.observable(false); self.statusExtruderOffset = ko.observable(false); + self.statusBedCamera = ko.observable(false); self.keycontrolPossible = ko.observable(false); self.keycontrolActive = ko.observable(false); @@ -41,6 +42,7 @@ $(function() { //deactivate other processes self.statusTrayPosition(false); self.statusExtruderOffset(false); + self.statusBedCamera(false); self.statusCameraOffset(true); // Switch to primary extruder @@ -99,6 +101,7 @@ $(function() { //deactivate other processes self.statusCameraOffset(false); self.statusExtruderOffset(false); + self.statusBedCamera(false); self.statusTrayPosition(true); // Switch to primary extruder @@ -167,6 +170,7 @@ $(function() { self.statusTrayPosition(false); self.statusExtruderOffset(false); self.statusCameraOffset(false); + self.statusBedCamera(true); // Switch to VacNozzle extruder self.control.sendCustomCommand({command: self.settings.plugins.OctoPNP.vacnozzle.extruder_nr().toString()}); @@ -184,7 +188,7 @@ $(function() { self.keycontrolPossible(true); //trigger immage fetching - setTimeout(function() {self._getImage('HEAD');}, 5000); + setTimeout(function() {self._getImage('BED');}, 5000); }; self._getImage = function(imagetype, callback) { @@ -196,7 +200,7 @@ $(function() { //data: JSON.stringify(data), success: function(response) { if(response.hasOwnProperty("src")) { - self._drawHeadImage(response.src); + self._drawImage(response.src); } if(response.hasOwnProperty("error")) { alert(response.error); @@ -206,7 +210,7 @@ $(function() { }); }; - self._drawHeadImage = function(img) { + self._drawImage = function(img) { var ctx=self._headCanvas.getContext("2d"); var localimg = new Image(); localimg.src = img; @@ -313,7 +317,11 @@ $(function() { return false; } if(refreshImage) { - setTimeout(function() {self._getImage('HEAD');}, 300); + if(self.statusBedCamera()) { + setTimeout(function() {self._getImage('BED');}, 300); + }else{ + setTimeout(function() {self._getImage('HEAD');}, 300); + } } }; } From 95058e6bb50469b4b836938386fa2d5518eb1d11 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Tue, 15 Dec 2015 15:07:14 +0100 Subject: [PATCH 08/10] head camera offset save dialog typo --- octoprint_OctoPNP/static/js/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octoprint_OctoPNP/static/js/settings.js b/octoprint_OctoPNP/static/js/settings.js index 28a1938..b4c2506 100644 --- a/octoprint_OctoPNP/static/js/settings.js +++ b/octoprint_OctoPNP/static/js/settings.js @@ -67,7 +67,7 @@ $(function() { self.saveCameraOffset = function() { //save values... self.settings.plugins.OctoPNP.camera.head.x(parseFloat(self.settings.plugins.OctoPNP.camera.head.x())-self.offsetCorrectionX()); - self.settings.plugins.OctoPNP.camera.head.y(parseFloat(self.settings.plugins.OctoPNP.camera.head.x())-self.offsetCorrectionY()); + self.settings.plugins.OctoPNP.camera.head.y(parseFloat(self.settings.plugins.OctoPNP.camera.head.y())-self.offsetCorrectionY()); //deactivate Keycontrol self.keycontrolPossible(false); From 57ed3019058d1495b3f2b8cb6940a2f9710f1907 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Wed, 16 Dec 2015 19:26:40 +0100 Subject: [PATCH 09/10] save extruder object to eeprom (only for Repetier firmware) --- octoprint_OctoPNP/static/js/settings.js | 133 ++++++++++++++++++ .../templates/OctoPNP_settings.jinja2 | 43 +++--- 2 files changed, 152 insertions(+), 24 deletions(-) diff --git a/octoprint_OctoPNP/static/js/settings.js b/octoprint_OctoPNP/static/js/settings.js index b4c2506..eac89d4 100644 --- a/octoprint_OctoPNP/static/js/settings.js +++ b/octoprint_OctoPNP/static/js/settings.js @@ -15,6 +15,8 @@ $(function() { self.offsetCorrectionY = ko.observable(0.0); self.jogDistance = ko.observable(1.0); + self.selectedExtruder = ko.observable(1); + self.isConnected = ko.computed(function() { return self.connection.isOperational() || self.connection.isReady() || self.connection.isPaused(); }); @@ -29,6 +31,13 @@ $(function() { self.showKeycontrols = ko.observable(true); self.keycontrolHelpActive = ko.observable(true); + // helpers for eeprom access + self.firmwareRegEx = /FIRMWARE_NAME:([^\s]+)/i; + self.repetierRegEx = /Repetier_([^\s]*)/i; + self.eepromDataRegEx = /EPR:(\d+) (\d+) ([^\s]+) (.+)/; + self.isRepetierFirmware = ko.observable(false); + self.eepromData = ko.observableArray([]); + // This will get called before the ViewModel gets bound to the DOM, but after its depedencies have // already been initialized. It is especially guaranteed that this method gets called _after_ the settings @@ -76,6 +85,9 @@ $(function() { // Calibrate offset between primary and other extruder self.extruderOffset = function() { + // fetch eeprom values + self.loadEeprom(); + // initialize process... self.cameraOffset(); // and correct status variables @@ -87,10 +99,22 @@ $(function() { self.saveExtruderOffset = function() { // Steps to save values: // get current offset for extruder x from eeprom + var oldOffsetX = parseFloat(self._getEepromValue("Extr." + self.selectedExtruder() + " X-offset")); + var oldOffsetY = parseFloat(self._getEepromValue("Extr." + self.selectedExtruder() + " Y-offset")); // get steps per mm for x and y axis + var stepsPerMMX = parseFloat(self._getEepromValue("X-axis steps per mm")); + var stepsPerMMY = parseFloat(self._getEepromValue("Y-axis steps per mm")); // compute offset steps from offsetCorrection values + var offsetX = oldOffsetX + self.offsetCorrectionX() * stepsPerMMX; + var offsetY = oldOffsetY + self.offsetCorrectionY() * stepsPerMMY; // save to eeprom + //self._setEepromValue("Extr." + self.selectedExtruder() + " X-offset", offsetX); + //self._setEepromValue("Extr." + self.selectedExtruder() + " Y-offset", offsetY); + console.log(offsetX); + console.log(offsetY); + //self.saveEeprom(); + // deactivate Keycontrol self.keycontrolPossible(false); self.statusExtruderOffset(false); @@ -324,6 +348,115 @@ $(function() { } } }; + + + // The following functions provide "infrastructure" to access and modify eeprom values + //self.onStartup = function() { + /*$('#settings_plugin_autocalibration_link a').on('show', function(e) { + if (self.isConnected() && !self.isRepetierFirmware()) + self._requestFirmwareInfo(); + });*/ + //} + + self.fromHistoryData = function(data) { + _.each(data.logs, function(line) { + var match = self.firmwareRegEx.exec(line); + if (match != null) { + if (self.repetierRegEx.exec(match[0])) + self.isRepetierFirmware(true); + } + }); + }; + + self.fromCurrentData = function(data) { + if (!self.isRepetierFirmware()) { + _.each(data.logs, function (line) { + var match = self.firmwareRegEx.exec(line); + if (match) { + if (self.repetierRegEx.exec(match[0])) + self.isRepetierFirmware(true); + } + }); + } + else + { + _.each(data.logs, function (line) { + var match = self.eepromDataRegEx.exec(line); + if (match) { + self.eepromData.push({ + dataType: match[1], + position: match[2], + origValue: match[3], + value: match[3], + description: match[4] + }); + } + }); + } + }; + + self.onEventConnected = function() { + self._requestFirmwareInfo(); + } + + self.onEventDisconnected = function() { + self.isRepetierFirmware(false); + }; + + self.loadEeprom = function() { + self.eepromData([]); + self._requestEepromData(); + }; + + self.saveEeprom = function() { + var eepromData = self.eepromData(); + _.each(eepromData, function(data) { + if (data.origValue != data.value) { + self._requestSaveDataToEeprom(data.dataType, data.position, data.value); + data.origValue = data.value; + } + }); + }; + + self._getEepromValue = function(description) { + var eepromData = self.eepromData(); + var result = false; + _.each(eepromData, function(data) { + if ((new RegExp(description)).test(data.description)) { + result = data.value; + } + }); + return result; + } + + self._setEepromValue = function(description, value) { + var eepromData = self.eepromData(); + var result = false; + _.each(eepromData, function(data) { + if ((new RegExp(description)).test(data.description)) { + data.value = value; + } + }); + } + + self._requestFirmwareInfo = function() { + self.control.sendCustomCommand({ command: "M115" }); + }; + + self._requestEepromData = function() { + self.control.sendCustomCommand({ command: "M205" }); + } + self._requestSaveDataToEeprom = function(data_type, position, value) { + var cmd = "M206 T" + data_type + " P" + position; + if (data_type == 3) { + cmd += " X" + value; + self.control.sendCustomCommand({ command: cmd }); + } + else { + cmd += " S" + value; + self.control.sendCustomCommand({ command: cmd }); + } + } } diff --git a/octoprint_OctoPNP/templates/OctoPNP_settings.jinja2 b/octoprint_OctoPNP/templates/OctoPNP_settings.jinja2 index 3ee3423..4dc724a 100644 --- a/octoprint_OctoPNP/templates/OctoPNP_settings.jinja2 +++ b/octoprint_OctoPNP/templates/OctoPNP_settings.jinja2 @@ -266,20 +266,16 @@
- -
-
- This is an experimental "calibration wizard". -
-
-
+
+ This is an experimental "calibration wizard". +
- +
{{ _("Keyboard controls active") }}
@@ -296,16 +292,29 @@ {{ _("Hint: If you move your mouse over the picture, you enter keyboard control mode.") }}
+
+ Current X-correction:
+ Current Y-correction: +
+

Object pos. on printbed

+
+ + +
+

+

E0 <-> HeadCamera



E0 <-> Ex

- - +
+ E: + + Repetier firmware required.

Tray position

@@ -318,20 +327,6 @@

VacNozzle <-> BedCamera

- -

-

Object pos. on printbed

-
- - -
- -
-
-
-
- Current X-offset: 0 + correction: - Current Y-offset: 0 + correction:
From eaf4d9df8dda2d2a040c4b99854d8f32a4e69cd8 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Thu, 17 Dec 2015 11:07:18 +0100 Subject: [PATCH 10/10] removed debug logs and fixed default setting --- octoprint_OctoPNP/static/js/settings.js | 2 +- octoprint_OctoPNP/templates/OctoPNP_settings.jinja2 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/octoprint_OctoPNP/static/js/settings.js b/octoprint_OctoPNP/static/js/settings.js index eac89d4..39ed3f5 100644 --- a/octoprint_OctoPNP/static/js/settings.js +++ b/octoprint_OctoPNP/static/js/settings.js @@ -15,7 +15,7 @@ $(function() { self.offsetCorrectionY = ko.observable(0.0); self.jogDistance = ko.observable(1.0); - self.selectedExtruder = ko.observable(1); + self.selectedExtruder = ko.observable(2); self.isConnected = ko.computed(function() { return self.connection.isOperational() || self.connection.isReady() || self.connection.isPaused(); diff --git a/octoprint_OctoPNP/templates/OctoPNP_settings.jinja2 b/octoprint_OctoPNP/templates/OctoPNP_settings.jinja2 index 4dc724a..760ead3 100644 --- a/octoprint_OctoPNP/templates/OctoPNP_settings.jinja2 +++ b/octoprint_OctoPNP/templates/OctoPNP_settings.jinja2 @@ -313,7 +313,7 @@

E0 <-> Ex


E: - + Repetier firmware required.