Skip to content

Commit

Permalink
Refactored MMU processor to work with moonraker
Browse files Browse the repository at this point in the history
  • Loading branch information
kieraneglin committed Sep 6, 2023
1 parent af32c0d commit d7ea2ee
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 181 deletions.
70 changes: 70 additions & 0 deletions components/mmu_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import logging, os, re, fileinput

class MmuServer:
TOOL_DISCOVERY_REGEX = r'((^MMU_CHANGE_TOOL.*?TOOL=)|(^T))(?P<tool>\d{1,2})'
METADATA_REPLACEMENT_STRING = "!mmu_inject_tools_used!"

def __init__(self, config):
self.config = config
self.server = config.get_server()
self.file_manager = self.server.lookup_component('file_manager')
self.enable_file_preprocessor = config.getboolean('enable_file_preprocessor', True)

self.server.register_event_handler("file_manager:filelist_changed", self._filelist_changed)

def _filelist_changed(self, response):
if not self.enable_file_preprocessor:
return

if response['action'] == 'create_file':
filepath = os.path.join(self.file_manager.get_directory(), response['item']['path'])

if filepath.endswith(".gcode"):
self._write_mmu_metadata(filepath)

def _write_mmu_metadata(self, file_path):
self._log('Checking for MMU metadata placeholder in file: ' + file_path)
has_placeholder, tools_used = self._enumerate_used_tools(file_path)

# An edit-in-place is seen by Moonraker as a file change (rightly so),
# BUT it's seen this way even if no changes are made. We use `has_placeholder`
# to determine whether there are any changes to make to prevent an infinite loop.
if has_placeholder:
self._log('Writing MMU metadata to file: ' + file_path)
return self._inject_tool_usage(file_path, tools_used)
else :
self._log('No MMU metadata placeholder found in file: ' + file_path)
return False

def _log(self, message):
logging.info('mmu_file_processor ' + message)

def _enumerate_used_tools(self, file_path):
regex = re.compile(self.TOOL_DISCOVERY_REGEX, re.IGNORECASE)
tools_used = set()
has_placeholder = False

with open(file_path, 'r') as f:
for line in f:
if not has_placeholder and self.METADATA_REPLACEMENT_STRING in line:
has_placeholder = True

match = regex.match(line)
if match:
tool = match.group('tool')
tools_used.add(int(tool))

return (has_placeholder, sorted(tools_used))

def _inject_tool_usage(self, file_path, tools_used):
with fileinput.FileInput(file_path, inplace=1) as file:
for line in file:
if self.METADATA_REPLACEMENT_STRING in line:
print(line.replace(self.METADATA_REPLACEMENT_STRING, ','.join(map(str, tools_used))), end='')
else:
print(line, end='')

return True

def load_component(config):
return MmuServer(config)
119 changes: 0 additions & 119 deletions extras/mmu_file_processor.py

This file was deleted.

68 changes: 68 additions & 0 deletions tests/components/mmu_server_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import os
import shutil
import unittest
from unittest.mock import MagicMock

from components.mmu_server import MmuServer

class TestMmuServerFileProcessor(unittest.TestCase):
TOOLCHANGE_FILEPATH = 'tests/support/toolchange.gcode'
NO_TOOLCHANGE_FILEPATH = 'tests/support/no_toolchange.gcode'

def setUp(self):
self.subject = MmuServer(MagicMock())
shutil.copyfile('tests/support/toolchange.orig.gcode', self.TOOLCHANGE_FILEPATH)
shutil.copyfile('tests/support/no_toolchange.orig.gcode', self.NO_TOOLCHANGE_FILEPATH)

def tearDown(self):
os.remove(self.TOOLCHANGE_FILEPATH)
os.remove(self.NO_TOOLCHANGE_FILEPATH)

def test_filelist_callback_when_enabled(self):
self.subject.enable_file_preprocessor = True
self.subject._write_mmu_metadata = MagicMock()

self.subject._filelist_changed({'action': 'create_file', 'item': {'path': 'test.gcode'}})

self.subject._write_mmu_metadata.assert_called_once()

def test_filelist_callback_when_disabled(self):
self.subject.enable_file_preprocessor = False
self.subject._write_mmu_metadata = MagicMock()

self.subject._filelist_changed({'action': 'create_file', 'item': {'path': 'test.gcode'}})

self.subject._write_mmu_metadata.assert_not_called()

def test_filelist_callback_when_wrong_event(self):
self.subject.enable_file_preprocessor = False
self.subject._write_mmu_metadata = MagicMock()

self.subject._filelist_changed({'action': 'move_file', 'item': {'path': 'test.gcode'}})

self.subject._write_mmu_metadata.assert_not_called()

def test_filelist_callback_when_wrong_file_type(self):
self.subject.enable_file_preprocessor = False
self.subject._write_mmu_metadata = MagicMock()

self.subject._filelist_changed({'action': 'create_file', 'item': {'path': 'test.txt'}})

self.subject._write_mmu_metadata.assert_not_called()

def test_write_mmu_metadata_when_writing_to_files(self):
self.subject._write_mmu_metadata(self.TOOLCHANGE_FILEPATH)

with open(self.TOOLCHANGE_FILEPATH, 'r') as f:
file_contents = f.read()
self.assertIn('PRINT_START MMU_TOOLS_USED=0,1,2,5,11\n', file_contents)
self.assertNotIn('[mmu_inject_tools_used]', file_contents)

def test_write_mmu_metadata_when_no_toolchanges(self):
self.subject._write_mmu_metadata(self.NO_TOOLCHANGE_FILEPATH)

with open(self.NO_TOOLCHANGE_FILEPATH, 'r') as f:
file_contents = f.read()
self.assertIn('PRINT_START MMU_TOOLS_USED=\n', file_contents)
self.assertNotIn('[mmu_inject_tools_used]', file_contents)

62 changes: 0 additions & 62 deletions tests/mmu_file_processor_test.py

This file was deleted.

1 change: 1 addition & 0 deletions tests/support/no_toolchange.orig.gcode
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
PRINT_START MMU_TOOLS_USED=!mmu_inject_tools_used!
G1 F1200
G1 X167.759 Y180.16 E.00802
G1 X167.263 Y180.262 E.02305
Expand Down
5 changes: 5 additions & 0 deletions tests/support/toolchange.orig.gcode
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
PRINT_START MMU_TOOLS_USED=!mmu_inject_tools_used!
T0
G1 F1200
G1 X167.759 Y180.16 E.00802
Expand All @@ -11,8 +12,12 @@ G1 X164.865 Y178.289 E.02538
G1 X164.172 Y177.944 E.03524
G1 X163.551 Y177.578 E.03281
T0

; T7
; The above shouldn't count
G1 X162.963 Y177.124 E.03382
G1 X162.489 Y176.633 E.03107
T11 ; Testing 2-digit numbers
G1 X162.239 Y176.218 E.02205
G1 X162.031 Y175.724 E.0244
MMU_CHANGE_TOOL TOOL=1
Expand Down

0 comments on commit d7ea2ee

Please sign in to comment.