Skip to content

Commit

Permalink
Merge pull request #114 from smartin015/expanded_edit
Browse files Browse the repository at this point in the history
Editable inprogress jobs, remainder-based input, custom events
  • Loading branch information
smartin015 committed Aug 25, 2022
2 parents a9ba38c + 0c4b1dc commit 81be789
Show file tree
Hide file tree
Showing 22 changed files with 361 additions and 176 deletions.
5 changes: 5 additions & 0 deletions continuousprint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
PRINTER_PROFILES,
GCODE_SCRIPTS,
Keys,
CustomEvents,
ASSETS,
TEMPLATES,
update_info,
Expand Down Expand Up @@ -58,6 +59,7 @@ def on_startup(self, host=None, port=None):
self._logger,
self._identifier,
self._basefolder,
self._event_bus.fire,
)

def on_after_startup(self):
Expand All @@ -73,6 +75,9 @@ def on_after_startup(self):

# ------------------------ Begin EventHandlerPlugin --------------------

def register_custom_events(*args, **kwargs):
return [CustomEvents.__members__.values()]

def on_event(self, event, payload):
if not hasattr(self, "_plugin"):
return
Expand Down
8 changes: 8 additions & 0 deletions continuousprint/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@
GCODE_SCRIPTS = dict((d["name"], d) for d in yaml.safe_load(f.read())["GScript"])


class CustomEvents(Enum):
START_PRINT = "continuousprint_start_print"
COOLDOWN = "continuousprint_cooldown"
CLEAR_BED = "continuousprint_clear_bed"
FINISH = "continuousprint_finish"
CANCEL = "continuousprint_cancel"


class Keys(Enum):
# TODO migrate old setting names to enum names
QUEUE = ("cp_queue", None)
Expand Down
4 changes: 3 additions & 1 deletion continuousprint/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,9 @@ def _state_inactive(self, a: Action, p: Printer):
if p == Printer.IDLE:
self._set_status("Inactive (click Start Managing)")
else:
self._set_status("Inactive (active print continues unmanaged)")
self._set_status(
"Inactive (active print continues unmanaged)", StatusType.NEEDS_ACTION
)

def _state_idle(self, a: Action, p: Printer):
self.q.release()
Expand Down
5 changes: 5 additions & 0 deletions continuousprint/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def __init__(
logger,
identifier,
basefolder,
fire_event,
):
self._basefolder = basefolder
self._printer = printer
Expand All @@ -74,6 +75,7 @@ def __init__(
self._set_add_awaiting_metadata = dict()
self._reconnect_attempts = 0
self._next_reconnect = 0
self._fire_event = fire_event

def start(self):
self._setup_thirdparty_plugin_integration()
Expand Down Expand Up @@ -313,6 +315,7 @@ def _init_driver(self, srcls=ScriptRunner, dcls=Driver):
self._logger,
self._printer,
self._sync_state,
self._fire_event,
)
self.d = dcls(
queue=self.q,
Expand Down Expand Up @@ -402,6 +405,8 @@ def _enqueue_analysis_backlog(self):
self._logger.info(f"Enqueued {counter} files for CPQ analysis")

def _enqueue(self, path, high_priority=False):
if path in TEMP_FILES.values():
return False # Exclude temp files from analysis
queue_entry = QueueEntry(
name=path.split("/")[-1],
path=path,
Expand Down
1 change: 1 addition & 0 deletions continuousprint/plugin_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def mockplugin():
settings=MockSettings(),
file_manager=MagicMock(),
plugin_manager=MagicMock(),
fire_event=MagicMock(),
queries=MagicMock(),
data_folder=None,
logger=logging.getLogger(),
Expand Down
2 changes: 2 additions & 0 deletions continuousprint/queues/lan_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ def _jbase(self, path="a.gcode"):
s.sd = False
s.count = 1
s.remaining = 1
s.completed = 0
s.profile_keys = ""
s.rank = 1
s.material_keys = ""
Expand Down Expand Up @@ -112,6 +113,7 @@ def test_submit_job(self):
"rank": 1,
"sd": False,
"remaining": 1,
"completed": 0,
}
],
"created": 100,
Expand Down
12 changes: 10 additions & 2 deletions continuousprint/script_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from octoprint.filemanager.destinations import FileDestinations
from octoprint.printer import InvalidFileLocation, InvalidFileType
from .storage.lan import ResolveError
from .data import Keys, TEMP_FILES
from .data import Keys, TEMP_FILES, CustomEvents


class ScriptRunner:
Expand All @@ -17,13 +17,15 @@ def __init__(
logger,
printer,
refresh_ui_state,
fire_event,
):
self._msg = msg
self._get_key = get_key
self._file_manager = file_manager
self._logger = logger
self._printer = printer
self._refresh_ui_state = refresh_ui_state
self._fire_event = fire_event

def _wrap_stream(self, name, gcode):
return StreamWrapper(name, BytesIO(gcode.encode("utf-8")))
Expand All @@ -44,20 +46,25 @@ def _execute_gcode(self, key):

def run_finish_script(self):
self._msg("Print Queue Complete", type="complete")
return self._execute_gcode(Keys.FINISHED_SCRIPT)
result = self._execute_gcode(Keys.FINISHED_SCRIPT)
self._fire_event(CustomEvents.FINISH)
return result

def cancel_print(self):
self._msg("Print cancelled", type="error")
self._printer.cancel_print()
self._fire_event(CustomEvents.CANCEL)

def start_cooldown(self):
self._msg("Running bed cooldown script")
self._execute_gcode(Keys.BED_COOLDOWN_SCRIPT)
self._printer.set_temperature("bed", 0) # turn bed off
self._fire_event(CustomEvents.COOLDOWN)

def clear_bed(self):
self._msg("Clearing bed")
self._execute_gcode(Keys.CLEARING_SCRIPT)
self._fire_event(CustomEvents.CLEAR_BED)

def start_print(self, item):
self._msg(f"{item.job.name}: printing {item.path}")
Expand All @@ -80,6 +87,7 @@ def start_print(self, item):
try:
self._logger.info(f"Attempting to print {path} (sd={item.sd})")
self._printer.select_file(path, sd=item.sd, printAfterSelect=True)
self._fire_event(CustomEvents.START_PRINT)
except InvalidFileLocation as e:
self._logger.error(e)
self._msg("File not found: " + path, type="error")
Expand Down
10 changes: 10 additions & 0 deletions continuousprint/script_runner_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from collections import namedtuple
from unittest.mock import MagicMock
from .script_runner import ScriptRunner
from .data import CustomEvents
import logging

logging.basicConfig(level=logging.DEBUG)
Expand All @@ -20,6 +21,7 @@ def setUp(self):
logger=logging.getLogger(),
printer=MagicMock(),
refresh_ui_state=MagicMock(),
fire_event=MagicMock(),
)
self.s._wrap_stream = MagicMock(return_value=None)

Expand All @@ -31,10 +33,12 @@ def test_run_finish_script(self):
sd=False,
printAfterSelect=True,
)
self.s._fire_event.assert_called_with(CustomEvents.FINISH)

def test_cancel_print(self):
self.s.cancel_print()
self.s._printer.cancel_print.assert_called()
self.s._fire_event.assert_called_with(CustomEvents.CANCEL)

def test_clear_bed(self):
self.s.clear_bed()
Expand All @@ -43,18 +47,21 @@ def test_clear_bed(self):
sd=False,
printAfterSelect=True,
)
self.s._fire_event.assert_called_with(CustomEvents.CLEAR_BED)

def test_start_print_local(self):
self.assertEqual(self.s.start_print(LI(False, "a.gcode", LJ("job1"))), True)
self.s._printer.select_file.assert_called_with(
"a.gcode", sd=False, printAfterSelect=True
)
self.s._fire_event.assert_called_with(CustomEvents.START_PRINT)

def test_start_print_sd(self):
self.assertEqual(self.s.start_print(LI(True, "a.gcode", LJ("job1"))), True)
self.s._printer.select_file.assert_called_with(
"a.gcode", sd=True, printAfterSelect=True
)
self.s._fire_event.assert_called_with(CustomEvents.START_PRINT)

def test_start_print_lan(self):
class NetItem:
Expand All @@ -69,11 +76,14 @@ def resolve(self):
self.s._printer.select_file.assert_called_with(
"net/a.gcode", sd=False, printAfterSelect=True
)
self.s._fire_event.assert_called_with(CustomEvents.START_PRINT)

def test_start_print_invalid_location(self):
self.s._printer.select_file.side_effect = InvalidFileLocation()
self.assertEqual(self.s.start_print(LI(True, "a.gcode", LJ("job1"))), False)
self.s._fire_event.assert_not_called()

def test_start_print_invalid_filetype(self):
self.s._printer.select_file.side_effect = InvalidFileType()
self.assertEqual(self.s.start_print(LI(True, "a.gcode", LJ("job1"))), False)
self.s._fire_event.assert_not_called()
63 changes: 52 additions & 11 deletions continuousprint/static/css/continuousprint.css
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@
padding-right: var(--cpq-pad);
padding-top: var(--cpq-pad);
}
#tab_plugin_continuousprint .job.draft .job-gutter .text-error {
width: 100%;
line-height: 1;
text-align: right;
padding-right: var(--cpq-pad2);
}
#tab_plugin_continuousprint .job .floatedit {
position: absolute;
top: var(--cpq-pad2);
Expand All @@ -170,16 +176,56 @@
margin-bottom: 0;
line-height: 1.6;
}
#tab_plugin_continuousprint .queue-row-container .total {
width: 40px;
#tab_plugin_continuousprint .total,
#tab_plugin_continuousprint .completed,
#tab_plugin_continuousprint .remaining,
#tab_plugin_continuousprint .count {
width: 67px;
text-align: center;
margin-left: var(--cpq-pad);
padding: 2px;
border: 1px transparent solid;
}
#tab_plugin_continuousprint .loading {
opacity: 0.3;
cursor: default !important;
}
#tab_plugin_continuousprint .edit-header {
display: flex;
justify-content: space-between;
font-weight: bold;
opacity: 0.5;
}
#tab_plugin_continuousprint .job-stats {
display: flex;
justify-content: space-between;
font-weight: bold;
border-top: 2px #ccc solid;
}
#tab_plugin_continuousprint .has_title {
cursor: help;
}
#tab_plugin_continuousprint .has_title > sup {
opacity: 0.5;
}
#tab_plugin_continuousprint .progresstext {
width: 100%;
position: absolute;
text-align:center;
}
#tab_plugin_continuousprint .set_warning {
flex: 1;
text-align: center;
}
#tab_plugin_continuousprint .draft .set_warning {
flex: 0;
}
#tab_plugin_continuousprint .sets .progress {
margin-left: 17px;
}
#tab_plugin_continuousprint .progress {
width: 150px;
position: relative;
flex: 1;
display: flex; /* allow for stacking progresses in jobs */
}
#tab_plugin_continuousprint .accordion-heading-button {
Expand Down Expand Up @@ -217,13 +263,9 @@
display: flex;
justify-content: space-between;
font-weight: bold;
padding: var(--cpq-pad) 8px 0 28px;
opacity: 0.5;
margin-left: 55px;
}
#tab_plugin_continuousprint .repeats {
width: 85px;
text-align: center;
align-items: flex-end;
}
#tab_plugin_continuousprint .queue-header > div, #settings_continuousprint_queues .queue-header > div {
text-align: center;
Expand Down Expand Up @@ -285,12 +327,11 @@
}

#tab_plugin_continuousprint .count-box{
text-align: left;
min-width:30px;
max-width: 30px;
max-height:15px;
margin-bottom: 3px;
-moz-appearance: textfield;
text-align: center;
border: 1px #ccc solid !important;
}
#tab_plugin_continuousprint .count-box::-webkit-outer-spin-button,
#tab_plugin_continuousprint .count-box::-webkit-inner-spin-button {
Expand Down
37 changes: 24 additions & 13 deletions continuousprint/static/js/continuousprint_job.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ function CPJob(obj, peers, api, profile) {

self._update = function(result) {
self.draft(result.draft);
self.count(result.count);
self.remaining(result.remaining); // Adjusted when count is mutated
self.completed(result.count - result.remaining); // Adjusted when count is mutated
self.count(result.count); // Adjusted when remaining is mutated
self.remaining(result.remaining);
self.completed(result.count - result.remaining); // Adjusted when remaining is mutated
self.id(result.id); // May change if no id to start with
self._name(result.name);
let cpss = [];
Expand Down Expand Up @@ -98,24 +98,35 @@ function CPJob(obj, peers, api, profile) {
self.editCancel = function() {
api.edit(api.JOB, {id: self.id(), draft: false}, self._update);
}
self.onBlur = function(vm, e) {
let cl = e.target.classList;
let v = parseInt(e.target.value, 10);
if (isNaN(v)) {
return;
}
vm.count(vm.completed() + v);
}
self.editEnd = function() {
let data = self.as_object();
data.draft = false;
api.edit(api.JOB, data, self._update);
}

self.length = ko.computed(function() {
let l = 0;
let c = self.count();
for (let qs of self.sets()) {
l += qs.count()*c;
self._safeParse = function(v) {
v = parseInt(v, 10);
if (isNaN(v)) {
return 0;
}
return l;
});
self.length_completed = ko.computed(function() {
let r = 0;
return v;
}

self.totals = ko.computed(function() {
let r = {count: 0, completed: 0, remaining: 0, total: 0};
for (let qs of self.sets()) {
r += qs.length_completed();
r.remaining += self._safeParse(qs.remaining());
r.total += self._safeParse(qs.length_remaining());
r.count += self._safeParse(qs.count());
r.completed += self._safeParse(qs.completed());
}
return r;
});
Expand Down
Loading

0 comments on commit 81be789

Please sign in to comment.