Skip to content

Commit

Permalink
Merge branch 'pre-release'
Browse files Browse the repository at this point in the history
  • Loading branch information
cp2004 committed Dec 31, 2021
2 parents abc805a + a2155fd commit 5b8209b
Show file tree
Hide file tree
Showing 21 changed files with 377 additions and 70 deletions.
56 changes: 43 additions & 13 deletions octoprint_eeprom_marlin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import octoprint.plugin
from octoprint.access import ADMIN_GROUP, READONLY_GROUP, USER_GROUP
from octoprint.access.permissions import Permissions
from octoprint.util.version import is_octoprint_compatible

from octoprint_eeprom_marlin import (
_version,
Expand Down Expand Up @@ -52,6 +53,7 @@ class EEPROMMarlinPlugin(

# Flags
collecting_eeprom = False
collecting_stats = False

def initialize(self):
# Initialise is called when all injections are complete
Expand All @@ -60,6 +62,7 @@ def initialize(self):

# Data models
self._firmware_info = data.FirmwareInfo()
self._firmware_stats = data.FirmwareStats()
self._eeprom_data = data.EEPROMData(self)

# Useful classes - watch the inheritance order:
Expand All @@ -75,10 +78,12 @@ def initialize(self):

# Registering UI components
def get_assets(self):
return {
"js": ["js/eeprom_marlin.js"],
"css": ["css/fontawesome5_stripped.css", "css/eeprom_marlin.css"],
}
# OctoPrint 1.5.0+ bundles FontAwesome 5, earlier versions do not
css_assets = ["css/eeprom_marlin.css"]
if is_octoprint_compatible("<1.5.0"):
css_assets += ["css/fontawesome5_stripped.css"]

return {"js": ["js/eeprom_marlin.js"], "css": css_assets}

def is_wizard_required(self):
return False
Expand Down Expand Up @@ -150,9 +155,11 @@ def comm_protocol_firmware_info(self, comm, name, fw_data, *args, **kwargs):
old_is_marlin = deepcopy(self._firmware_info.is_marlin)
self._firmware_info.is_marlin = self._parser.is_marlin(name)
if not old_is_marlin and self._firmware_info.is_marlin:
# Connected and need to send M503
command = "M503" if self._settings.get_boolean(["use_m503"]) else "M501"
self._printer.commands(command)
# Connected and need to send M503 (& optionally M78)
commands = ["M503" if self._settings.get(["use_m503"]) else "M501"]
if self._settings.get_boolean(["m78"]):
commands += ["M78"]
self._printer.commands(commands)
self._firmware_info.name = name
self._firmware_info.additional_info_from_dict(fw_data)

Expand All @@ -179,9 +186,13 @@ def comm_protocol_gcode_sending(
self._logger.info("{} detected, collecting data".format(cmd))
self.collecting_eeprom = True

if cmd == "M78":
self._logger.info("M87 detected, collecting stats")
self.collecting_stats = True

def comm_protocol_gcode_received(self, comm, line, *args, **kwargs):
# https://docs.octoprint.org/en/master/plugins/hooks.html#octoprint-comm-protocol-gcode-received
if self.collecting_eeprom:
if self.collecting_eeprom or self.collecting_stats:
if "ok" in line.lower():
# Send the new data to the UI to be reloaded
self._logger.info("Finished data collection, updating UI")
Expand All @@ -190,13 +201,27 @@ def comm_protocol_gcode_received(self, comm, line, *args, **kwargs):
{
"eeprom": self._eeprom_data.to_dict(),
"info": self._firmware_info.to_dict(),
"stats": self._firmware_stats.get_stats(),
},
)
self.collecting_eeprom = False
else:
parsed = self._parser.parse_eeprom_data(line)
if parsed:
self._eeprom_data.from_dict(parsed, ui=False)
self.collecting_stats = False

if "printer locked" in line.lower():
self.send_message("locked", {})
self._firmware_info.locked = True
# Disable data collection
self.collecting_eeprom = False
self.collecting_stats = False

if self.collecting_eeprom:
parsed = self._parser.parse_eeprom_data(line)
if parsed:
self._eeprom_data.from_dict(parsed, ui=False)
elif self.collecting_stats:
stats = self._parser.parse_stats_line(line)
if stats:
self._firmware_stats.update_stats(stats)

return line

Expand All @@ -208,6 +233,11 @@ def comm_protocol_atcommand_sending(
# Useful for virtual printer testing, where the responses are not implemented.
self.collecting_eeprom = True

if cmd.upper() == "EEPROM_STATS_DEBUG":
# Trigger stats data collection manually using @EEPROM_STATS_DEBUG, for sending test files at the parser
# Useful for virtual printer testing, where the responses are not implemented.
self.collecting_stats = True

def get_additional_permissions(self, *args, **kwargs):
return [
{
Expand Down Expand Up @@ -273,7 +303,7 @@ def get_update_information(self):
"""
__plugin_author__ = "Charlie Powell"
__plugin_license__ = "AGPLv3"
__plugin_url__ = "https://github.com/cp2004/OctoPrint-EEPROM-Marlin."
__plugin_url__ = "https://github.com/cp2004/OctoPrint-EEPROM-Marlin"
__plugin_pythoncompat__ = ">=2.7,<4"
__plugin_version__ = __version__

Expand Down
16 changes: 13 additions & 3 deletions octoprint_eeprom_marlin/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def __init__(self, plugin):
self._logger = plugin._logger # noqa
self._printer = plugin._printer # noqa
self._firmware_info = plugin._firmware_info # noqa
self._firmware_stats = plugin._firmware_stats # noqa
self._eeprom_data = plugin._eeprom_data # noqa
self._backup_handler = plugin._backup_handler # noqa
self._plugin = plugin
Expand All @@ -53,9 +54,17 @@ def on_api_command(self, command, data):

# Get data from printer
if self._printer.is_ready():
self._printer.commands(
"M503" if self._settings.get(["use_m503"]) else "M501"
)
commands = ["M503" if self._settings.get(["use_m503"]) else "M501"]
if self._settings.get_boolean(["m78"]):
commands += ["M78"]
if self._firmware_info.locked:
# If the printer was locked on startup, we will need to check the name using M115 first.
commands = ["M115"] + commands
# Assume the user unlocked the printer - if they didn't, the locked state will return as soon as we
# send these commands.
self._firmware_info.locked = False
self._printer.commands(commands)

elif command == CMD_SAVE:
if not Permissions.PLUGIN_EEPROM_MARLIN_EDIT.can():
# Insufficient rights
Expand Down Expand Up @@ -107,6 +116,7 @@ def on_api_get(self, request):
return flask.jsonify(
{
"info": self._firmware_info.to_dict(),
"stats": self._firmware_stats.get_stats(),
"eeprom": self._eeprom_data.to_dict(),
"backups": self._backup_handler.get_backups(quick=True),
}
Expand Down
44 changes: 44 additions & 0 deletions octoprint_eeprom_marlin/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
Dict = Optional = None
pass

from octoprint.util import dict_merge

# This defines the data structure used in both the frontend and the backend. Must be kept in sync with
# the observables in the viewmodel. UI will update automatically from this structure using the Jinja templates.
Expand All @@ -34,6 +35,7 @@
"E": {"type": "float1", "label": "E Steps", "units": "steps/mm"},
},
"name": "Steps",
"link": "https://marlinfw.org/docs/gcode/M092.html",
},
"feedrate": {
"command": "M203",
Expand All @@ -44,6 +46,7 @@
"E": {"type": "float1", "label": "E axis", "units": "mm/s"},
},
"name": "Feedrate",
"link": "https://marlinfw.org/docs/gcode/M203.html",
},
"max_acceleration": {
"command": "M201",
Expand All @@ -70,6 +73,7 @@
},
},
"name": "Maximum Acceleration",
"link": "https://marlinfw.org/docs/gcode/M201.html",
},
"print_acceleration": {
"command": "M204",
Expand All @@ -79,6 +83,7 @@
"T": {"type": "float1", "label": "Travel acceleration", "units": "mm/s2"},
},
"name": "Print Acceleration",
"link": "https://marlinfw.org/docs/gcode/M204.html",
},
"probe_offset": {
"command": "M851",
Expand All @@ -88,6 +93,7 @@
"Z": {"type": "float2", "label": "Z probe Z offset", "units": "mm"},
},
"name": "Probe Offset",
"link": "https://marlinfw.org/docs/gcode/M851.html",
},
"home_offset": {
"command": "M206",
Expand All @@ -97,6 +103,7 @@
"Z": {"type": "float2", "label": "Z home offset", "units": "mm"},
},
"name": "Home Offset",
"link": "https://marlinfw.org/docs/gcode/M206.html",
},
"endstop": {
"command": "M666",
Expand All @@ -106,6 +113,7 @@
"Z": {"type": "float2", "label": "Adjustment for Z", "units": "mm"},
},
"name": "Endstop Offsets",
"link": "https://marlinfw.org/docs/gcode/M666.html",
},
"delta": {
"command": "M665",
Expand All @@ -122,18 +130,21 @@
"C": {"type": "float2", "label": "Gamma (Tower 3) diagonal rod trim"},
},
"name": "Delta Configuration",
"link": "https://marlinfw.org/docs/gcode/M665.html",
},
"linear": {
"command": "M900",
"params": {"K": {"type": "float2", "label": "K factor"}},
"name": "Linear Advance",
"link": "https://marlinfw.org/docs/gcode/M900.html",
},
"filament": {
"command": "M200",
"params": {
"D": {"type": "float2", "label": "Filament Diameter", "units": "mm"}
},
"name": "Filament Settings",
"link": "https://marlinfw.org/docs/gcode/M200.html",
},
"hotend_pid": {
"command": "M301",
Expand All @@ -143,6 +154,7 @@
"D": {"type": "float2", "label": "Hotend kD"},
},
"name": "Hotend PID",
"link": "https://marlinfw.org/docs/gcode/M301.html",
},
"bed_pid": {
"command": "M304",
Expand All @@ -152,6 +164,7 @@
"D": {"type": "float2", "label": "Bed kD"},
},
"name": "Bed PID",
"link": "https://marlinfw.org/docs/gcode/M304.html",
},
"advanced": {
"command": "M205",
Expand All @@ -174,6 +187,7 @@
"Z": {"type": "float1", "label": "Z max jerk", "units": "mm/s"},
},
"name": "Advanced",
"link": "https://marlinfw.org/docs/gcode/M205.html",
},
"autolevel": {
"command": "M420",
Expand All @@ -182,6 +196,7 @@
"Z": {"type": "float1", "label": "Z fade height", "units": "mm"},
},
"name": "Autolevel",
"link": "https://marlinfw.org/docs/gcode/M420.html",
},
"material1": {
"command": "M145",
Expand All @@ -196,6 +211,7 @@
}, # This should NOT be in the UI
},
"name": "Material Preset (1)",
"link": "https://marlinfw.org/docs/gcode/M145.html",
},
"material2": {
"command": "M145",
Expand All @@ -210,6 +226,7 @@
}, # This should NOT be in the UI
},
"name": "Material Preset (2)",
"link": "https://marlinfw.org/docs/gcode/M145.html",
},
"filament_change": {
"command": "M603",
Expand All @@ -218,6 +235,7 @@
"U": {"type": "float1", "label": "Unload length", "units": "mm"},
},
"name": "Filament Change",
"link": "https://marlinfw.org/docs/gcode/M603.html",
},
"filament_runout": {
"command": "M412",
Expand All @@ -227,6 +245,7 @@
"S": {"type": "bool", "label": "Enable filament runout sensor"},
},
"name": "Filament Runout Sensor",
"link": "https://marlinfw.org/docs/gcode/M412.html",
},
"tmc_current": {
"command": "M906",
Expand All @@ -236,6 +255,7 @@
"Y": {"type": "float2", "label": "Current for Y Stepper", "units": "mA"},
"Z": {"type": "float2", "label": "Current for Z Stepper", "units": "mA"},
},
"link": "https://marlinfw.org/docs/gcode/M906.html",
},
"tmc_hybrid": {
"command": "M913",
Expand All @@ -245,6 +265,7 @@
"Z": {"type": "float2", "label": "Hybrid Threshold for Z axis"},
"E": {"type": "float2", "label": "Hybrid Threshold for E axis"},
},
"link": "https://marlinfw.org/docs/gcode/M913.html",
},
}

Expand All @@ -266,6 +287,7 @@ class FirmwareInfo:
is_marlin = False # type: bool
additional_info = {} # type: dict
capabilities = {} # type: dict
locked = False

def additional_info_from_dict(self, data):
self.additional_info = {}
Expand All @@ -283,6 +305,7 @@ def to_dict(self):
"is_marlin": self.is_marlin,
"additional": self.additional_info,
"capabilities": self.capabilities,
"locked": self.locked,
}


Expand Down Expand Up @@ -400,3 +423,24 @@ def to_list(self):
result.append({"name": key, "command": data["command"], "params": params})

return result


class FirmwareStats:
def __init__(self):
self.stats = copy.deepcopy(blank_stats)

def update_stats(self, new_stats):
self.stats = dict_merge(self.stats, new_stats)

def get_stats(self):
return self.stats


blank_stats = {
"prints": 0,
"finished": 0,
"failed": 0,
"total_time": "",
"longest": "",
"filament": "",
}
20 changes: 20 additions & 0 deletions octoprint_eeprom_marlin/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ def regex_creator(value_type, param_letter):

regex_command = re.compile(r"echo:\s*(?P<gcode>M(?P<value>\d{1,3}))")

regex_stats = {
"prints": re.compile(r"Prints: (\d*)"),
"finished": re.compile(r"Finished: (\d*)"),
"failed": re.compile(r"Failed: (\d*)"),
"total_time": re.compile(r"Total time: (.*),"),
"longest": re.compile(r"Longest job: (.*)"),
"filament": re.compile(r"Filament used: (.*m)"),
}


class Parser:
def __init__(self, logger):
Expand Down Expand Up @@ -122,3 +131,14 @@ def parse_eeprom_data(self, line):
@staticmethod
def is_marlin(name):
return "marlin" in name.lower()

@staticmethod
def parse_stats_line(line):
# Run all the regexes against the line to grab the params
stats = {}
for key, regex in regex_stats.items():
match = regex.search(line)
if match:
stats[key] = match.group(1)

return stats
Loading

0 comments on commit 5b8209b

Please sign in to comment.