Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# RewriteM600

Implement M600 for pinters that can't support M600 by default (TFT with out Marlin Mode support, like Artilelry X1 and Genius). You can use M600, is going to stop wait until you change and press resume. If you have a TFT 28 (Like the Artillery) i would recomend you to check [Rawr TFT Firmware](https://github.com/wgcv/RAWR-TFT-Firmware-Artillery3D) to implement M600 direct in the TFT and do not need Octoprint.
Implement M600 for pinters that can't support M600 by default (TFT with out Marlin Mode support, like the Artillery Sidewinder X1 and Genius). You can use M600, it will stop and wait until you change and press resume. If you have a TFT 28 (like the Artillery) I would recomend you check out [Rawr TFT Firmware](https://github.com/wgcv/RAWR-TFT-Firmware-Artillery3D) to implement M600 direct in the TFT then do not need Octoprint.

## Setup

Expand All @@ -14,12 +14,16 @@ Copy or manually using this URL:

https://github.com/wgcv/RewriteM600/archive/master.zip

## Configuration


As this plugin only tries to recognize the correct pausing position as a response to the `M114`-marlin-command, it is possible to change the default behaviour.
The plugin provides the dict `cached_position` (with members `x`, `y`, `z` and `e`) to the GCODE-Script `beforePrintResumed`. You can access them via `{{plugins.RewriteM600.cached_position.(x|y|z|e)}}`.

## Screenshots

![Screenshot](https://github.com/wgcv/plugins.octoprint.org/raw/gh-pages/assets/img/plugins/RewriteM600/M600-in-action.png
)

## Support
You can help this project reporting issues, making PR or Sponsor it [PayPal](https://paypal.me/wgcvl).

You can help this project by reporting issues, making PR or Sponsor it [PayPal](https://paypal.me/wgcv).
112 changes: 92 additions & 20 deletions octoprint_RewriteM600/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# coding=utf-8
from __future__ import absolute_import
import re

### (Don't forget to remove me)
# This is a basic skeleton for your plugin's __init__.py. You probably want to adjust the class name of your plugin
Expand All @@ -12,40 +13,110 @@
import octoprint.plugin

class Rewritem600Plugin(octoprint.plugin.AssetPlugin, octoprint.plugin.TemplatePlugin, octoprint.plugin.SettingsPlugin):
cached_position = {"x": "NOT SET", "y": "NOT SET", "z": "NOT SET", "e": "NOT SET"}
listening = False
waiting = False
waitingForPause = False;
waitingForResume = False;
resumeScript = None;

def on_settings_initialized(self):
scripts = self._settings.listScripts("gcode")
if not "rewrite_m600_pause" in scripts:
self._settings.saveScript("gcode", "rewrite_m600_pause", u'' + self._settings.get(["pauseCommand"]))
if not "rewrite_m600_resume" in scripts:
self._settings.saveScript("gcode", "rewrite_m600_resume", u'' + self._settings.get(["resumeCommand"]))

def on_settings_save(self, data):
if 'pauseCommand' in data:
script = data["pauseCommand"]
self._settings.saveScript("gcode", "rewrite_m600_pause", u'' + script.replace("\r\n", "\n").replace("\r", "\n"))
# data.pop('pauseCommand')
if 'resumeCommand' in data:
script = data["resumeCommand"]
self._settings.saveScript("gcode", "rewrite_m600_resume", u'' + script.replace("\r\n", "\n").replace("\r", "\n"))
octoprint.plugin.SettingsPlugin.on_settings_save(self, data)

def rewrite_m600(self, comm_instance, phase, cmd, cmd_type, gcode, *args, **kwargs):
if gcode and gcode == "M600":
self._plugin_manager.send_plugin_message(self._identifier, dict(type="popup", msg="Please change the filament and resume the print"))
comm_instance.setPause(True)
cmd = [("M117 Filament Change",),"G91","M83", "G1 Z+"+self._settings.get(["zDistance"])+" E-0.8 F4500", "M82", "G90", "G1 X0 Y0"]
self._logger.info("rewrite_m600")
self._plugin_manager.send_plugin_message(self._identifier,
dict(type = "popup",
msg = "Please change the filament and resume the print"))
self.listening = True
cmd = ["G4 S" + str(self._settings.get(["timeG4"])), "M114", "G4 S" + str(self._settings.get(["timeG4"])), "@setpause"]
self._logger.info(cmd)
self._logger.info(self.cached_position)
self.waiting = True
self.waitingForPause = True;
return cmd

def toggle_pause(self, comm_instance, phase, command, parameters, tags=None, *args, **kwargs):
if command == "setpause" and self.waitingForPause:
self._logger.info("setpause")
if self._settings.get(["defaultPause"]):
comm_instance.setPause(True)
else:
comm_instance.setPause(True, local_handling = False) # sorgt dafür, dass das Pause-Script nicht ausgeführt wird!
self._printer.script("rewrite_m600_pause", must_be_set=False)
self.waitingForPause = False
if command == "afterResumed" and self.waitingForResume:
self._logger.info("afterResumed")
self._settings.saveScript("gcode", "beforePrintResumed", u'' + self.resumeScript)
self.waitingForResume = False

def detect_position(self, comm_instance, line, *args, **kwargs):
# ok X:20.0 Y:20.0 Z:0.6 E:20.01306 Count: A:2000 B:2000 C:60
# match = re.match("X:([0-9.]+) Y:([0-9.]+) Z:([0-9.]+) E:-?([0-9.]+) Count X|A:([0-9]+) Y|B:([0-9]+) Z|C:([0-9]+)", line)
match = re.search(self._settings.get(["regularExpression"]), line)
if match is not None and self.listening:
self._logger.info("DetectPosition: " + line)
self.cached_position["x"] = match.group(self._settings.get(["x"]))
self.cached_position["y"] = match.group(self._settings.get(["y"]))
self.cached_position["z"] = match.group(self._settings.get(["z"]))
self.cached_position["e"] = match.group(self._settings.get(["e"]))
self._logger.info("CachedPosition: " + self.cached_position["x"])
self._plugin_manager.send_plugin_message(self._identifier, dict(type="popup", msg = "Saved location at X:" +
self.cached_position["x"] + " Y:" + self.cached_position["y"] +
" Z:" + self.cached_position["z"]))
self.listening = False # Reset so we don't listen to the update called on print pause
self._logger.info(self.cached_position)
return line

def after_resume(self, comm_instance, script_type, script_name, *args, **kwargs):
self._logger.info("Received queued command: " + script_name)
if self.waiting and script_type == "gcode" and script_name == "beforePrintResumed":
self._logger.info("Resuming from Filament Change")
self.waiting = False
self._plugin_manager.send_plugin_message(self._identifier, dict(type = "popup", msg = "Resuming to location at X:" +
self.cached_position["x"] + " Y:" + self.cached_position["y"] +
" Z:" + self.cached_position["z"]))
variables = dict(cached=self.cached_position)
if not self._settings.get(["defaultResume"]):
# Script ausführen
self._printer.script("rewrite_m600_resume", context=variables, must_be_set=False)
# script laden & leeren
self.resumeScript = self._settings.loadScript("gcode", "beforePrintResumed", source=True)
self._settings.saveScript("gcode", "beforePrintResumed", u'')
self.waitingForResume = True
self.cached_position = {"x": "NOT SET", "y": "NOT SET", "z": "NOT SET", "e": "NOT SET"}
return None, ["@afterResumed"], variables

def after_resume(self, comm_instance, phase, cmd, parameters, tags=None, *args, **kwargs):
if cmd and cmd == "resume":
if(comm_instance.pause_position.x):
cmd = []
cmd =["M83","G1 E-0.8 F4500", "G1 E0.8 F4500", "G1 E0.8 F4500", "M82", "G90", "G92 E"+comm_instance.pause_position.e, "M83", "G1 X"+comm_instance.pause_position.x+" Y"+comm_instance.pause_position.y+" Z"+comm_instance.pause_position.z+" F4500"]
if(comm_instance.pause_position.f):
cmd.append("G1 F" + comm_instance.pause_position.f)
comm_instance.commands(cmd)
comm_instance.setPause(False)
return
def get_settings_defaults(self):
return dict(zDistance=80)
return dict(x = 1, y = 2, z = 3, e = 4, timeG4 = 5, pauseCommand = "", resumeCommand = "", defaultPause = True, defaultResume = True, regularExpression = "X:([0-9.]+) Y:([0-9.]+) Z:([0-9.]+) E:-?([0-9.]+) Count:? (X|A):([0-9]+) (Y|B):([0-9]+) (Z|C):([0-9]+)")

def get_template_configs(self):
return [
dict(type="navbar", custom_bindings=False),
dict(type="settings", custom_bindings=False)
]

##~~ AssetPlugin mixin

def get_assets(self):
# Define your plugin's asset files to automatically include in the
# core UI here.
return dict(
js=["js/RewriteM600.js"],
css=["css/RewriteM600.css"],
less=["less/RewriteM600.less"]
js = ["js/RewriteM600.js"]
)

##~~ Softwareupdate hook
Expand Down Expand Up @@ -91,6 +162,7 @@ def __plugin_load__():
__plugin_hooks__ = {
"octoprint.plugin.softwareupdate.check_config": __plugin_implementation__.get_update_information,
"octoprint.comm.protocol.gcode.queuing": __plugin_implementation__.rewrite_m600,
"octoprint.comm.protocol.atcommand.queuing": __plugin_implementation__.after_resume,
"octoprint.comm.protocol.scripts": __plugin_implementation__.after_resume,
"octoprint.comm.protocol.gcode.received": __plugin_implementation__.detect_position,
"octoprint.comm.protocol.atcommand.queuing": __plugin_implementation__.toggle_pause,
}

16 changes: 13 additions & 3 deletions octoprint_RewriteM600/static/js/RewriteM600.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,20 @@
$(function() {
function Rewritem600ViewModel(parameters) {
var self = this;

self.settings = parameters[0];
self.defaultPause = ko.observable();
self.defaultResume = ko.observable();

// assign the injected parameters, e.g.:
// self.loginStateViewModel = parameters[0];
// self.settingsViewModel = parameters[1];

self.onBeforeBinding = function() {
console.log(self.settings);
self.defaultPause(self.settings.settings.plugins.RewriteM600.defaultPause());
self.defaultResume(self.settings.settings.plugins.RewriteM600.defaultResume());
}

// TODO: Implement your plugin's view model here.
self.onDataUpdaterPluginMessage = function(plugin, data) {
Expand All @@ -23,7 +33,7 @@ $(function() {
title: 'M600',
text: data.msg,
type: "info",
hide: false
hide: true
});
}
}
Expand All @@ -36,8 +46,8 @@ $(function() {
OCTOPRINT_VIEWMODELS.push({
construct: Rewritem600ViewModel,
// ViewModels your plugin depends on, e.g. loginStateViewModel, settingsViewModel, ...
dependencies: [ /* "loginStateViewModel", "settingsViewModel" */ ],
dependencies: [ "settingsViewModel"/* "loginStateViewModel", "settingsViewModel" */ ],
// Elements to bind to, e.g. #settings_plugin_RewriteM600, #tab_plugin_RewriteM600, ...
elements: [ /* ... */ ]
elements: [ ]
});
});
75 changes: 72 additions & 3 deletions octoprint_RewriteM600/templates/RewriteM600_settings.jinja2
Original file line number Diff line number Diff line change
@@ -1,8 +1,77 @@
<h4>Rewrite M600</h4>

<form class="form-horizontal">
<div class="control-group">
<label class="control-label">{{ _('Z-Distance(mm): ') }}</label>
<label class="control-label" for="settings-rewritem600-timeg4">{{ _('Time for G4: ') }}</label>
<div class="controls">
<input type="number" class="input-block-level" data-bind="value: settings.plugins.RewriteM600.zDistance">
<span class="input-append">
<input type="number" class="input-block-level" data-bind="value: settings.plugins.RewriteM600.timeG4" id="settings-rewritem600-timeg4">
<span class="add-on">sec</span>
</span>
<br>
<small style="inline-block"><code>G4</code> is the dwell-command. It is used to make sure the <code>M114</code> (report current position) reports the latest known position.</small>
</div>
</div>
</form>

<div class="control-group" title="{{ _('GCODE to execute when rewriting M600') }}">
<label class="control-label" for="settings-rewritem600-checkbox">{{ _('Default @pause') }}</label>
<div class="controls">
<input type="checkbox" class="inline" id="settings-rewritem600-checkbox" data-bind="checked: settings.plugins.RewriteM600.defaultPause">
<small style="display: inline-block;">If checked, default script <code>afterPrintPaused</code> (from GCODE Scripts) is sent to the printer. Otherwise, the gcode stated in the following textarea is used.</small>
</div>
</div>
<div class="control-group" title="{{ _('GCODE to execute when rewriting M600') }}">
<label class="control-label" for="settings-rewritem600-rewriteGcode">{{ _('GCODE for pause') }}</label>
<div class="controls">
<textarea rows="5" class="input-block-level" data-bind="disable: settings.plugins.RewriteM600.defaultPause, value: settings.plugins.RewriteM600.pauseCommand" id="settings-rewritem600-rewriteGcode"></textarea>
</div>
</div>

<div class="control-group" title="{{ _('GCODE to execute when resuming M600') }}">
<label class="control-label" for="settings-rewritem600-resumeCheckbox">{{ _('Default @resume') }}</label>
<div class="controls">
<input type="checkbox" class="inline" id="settings-rewritem600-resumeCheckbox" data-bind="checked: settings.plugins.RewriteM600.defaultResume">
<small style="display: inline-block;">If checked, default script <code>beforePrintResumed</code> (from GCODE Scripts) is sent to the printer. Otherwise, the gcode stated in the following textarea is used.</small>
</div>
</div>
<div class="control-group" title="{{ _('GCODE to execute when resuming') }}">
<label class="control-label" for="settings-rewritem600-resume">{{ _('GCODE for resume') }}</label>
<div class="controls">
<textarea rows="5" class="input-block-level" data-bind="disable: settings.plugins.RewriteM600.defaultResume, value: settings.plugins.RewriteM600.resumeCommand" id="settings-rewritem600-resume"></textarea>
</div>
</div>
<div class="advanced_options">
<div><small><a href="#" class="muted" data-bind="toggleContent: { class: 'fa-caret-right fa-caret-down', parent: '.advanced_options', container: '.hide' }"><i class="fas fa-caret-right"></i> {{ _('Advanced options') }}</a></small></div>
<div class="hide">
<div class="control-group" title="{{ _('Regex to process position (answer M114)') }}">
<label class="control-label" for="settings-rewritem600-regex">{{ _('Regular Expression') }}</label>
<div class="controls">
<input type="text" class="input-block-level" data-bind="value: settings.plugins.RewriteM600.regularExpression" id="settings-rewritem600-regex">
<small style="display: inline-block;">Regular Expression to recognize the position as answer to <code>M114</code>.</small>
</div>
</div>
<div class="control-group" title="{{ _('Datagroups for RegEx') }}">
<span class="control-label">Match Groups</span>
<div class="controls">
<span class="input-prepend" style="float:left;width:10%;margin-right:7%">
<span class="add-on">{{ _('X') }}</span>
<input type="number" class="input-block-level" data-bind="value: settings.plugins.RewriteM600.x" id="settings-rewritem600-x">
</span>
<span class="input-prepend" style="float:left;width:10%;margin-right:7%">
<span class="add-on">{{ _('Y') }}</span>
<input type="number" class="input-block-level" data-bind="value: settings.plugins.RewriteM600.y" id="settings-rewritem600-y">
</span>
<span class="input-prepend" style="float:left;width:10%;margin-right:7%">
<span class="add-on">{{ _('Z') }}</span>
<input type="number" class="input-block-level" data-bind="value: settings.plugins.RewriteM600.z" id="settings-rewritem600-z">
</span>
<span class="input-prepend" style="float:left;width:10%;margin-right:7%">
<span class="add-on">{{ _('E') }}</span>
<input type="number" class="input-block-level" data-bind="value: settings.plugins.RewriteM600.e" id="settings-rewritem600-e">
</span>
<small style="display: block; clear:both;">Match groups for the regular expression above.</small>
</div>
</div>
</div>
</div>
</form>
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
plugin_name = "RewriteM600"

# The plugin's version. Can be overwritten within OctoPrint's internal data via __plugin_version__ in the plugin module
plugin_version = "1.0.1"
plugin_version = "1.0.5"

# The plugin's description. Can be overwritten within OctoPrint's internal data via __plugin_description__ in the plugin
# module
plugin_description = """Implement M600 for pinters that can't support M600 by default (TFT with out marlin mode support, like Artilelry X1 and Genius)"""
plugin_description = """Implement M600 for pinters that can't support M600 by default (TFT with out marlin mode support, like Artillery X1 and Genius)"""

# The plugin's author. Can be overwritten within OctoPrint's internal data via __plugin_author__ in the plugin module
plugin_author = "Gustavo Cevallos"
Expand Down