Skip to content

Commit bd96945

Browse files
authored
Merge pull request #48 from smartin015/printer_profiles
Add printer profiles / settings
2 parents e8cd800 + ad6d22c commit bd96945

File tree

9 files changed

+334
-127
lines changed

9 files changed

+334
-127
lines changed

continuousprint/__init__.py

Lines changed: 22 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import octoprint.util
66
import flask
77
import json
8+
import yaml
9+
import os
810
import time
911
from io import BytesIO
1012
from octoprint.server.util.flask import restricted_access
@@ -53,27 +55,24 @@ def _update_driver_settings(self):
5355

5456
# part of SettingsPlugin
5557
def get_settings_defaults(self):
58+
base = os.path.dirname(__file__)
59+
with open(os.path.join(base, "data/printer_profiles.yaml"), "r") as f:
60+
self._printer_profiles = yaml.safe_load(f.read())['PrinterProfile']
61+
with open(os.path.join(base, "data/gcode_scripts.yaml"), "r") as f:
62+
self._gcode_scripts = yaml.safe_load(f.read())['GScript']
63+
5664
d = {}
5765
d[QUEUE_KEY] = "[]"
58-
d[CLEARING_SCRIPT_KEY] = (
59-
"M17 ;enable steppers\n"
60-
"G91 ; Set relative for lift\n"
61-
"G0 Z10 ; lift z by 10\n"
62-
"G90 ;back to absolute positioning\n"
63-
"M190 R25 ; set bed to 25 and wait for cooldown\n"
64-
"G0 X200 Y235 ;move to back corner\n"
65-
"G0 X110 Y235 ;move to mid bed aft\n"
66-
"G0 Z1 ;come down to 1MM from bed\n"
67-
"G0 Y0 ;wipe forward\n"
68-
"G0 Y235 ;wipe aft\n"
69-
"G28 ; home"
70-
)
71-
d[FINISHED_SCRIPT_KEY] = (
72-
"M18 ; disable steppers\n"
73-
"M104 T0 S0 ; extruder heater off\n"
74-
"M140 S0 ; heated bed heater off\n"
75-
"M300 S880 P300 ; beep to show its finished"
76-
)
66+
d[CLEARING_SCRIPT_KEY] = ""
67+
d[FINISHED_SCRIPT_KEY] = ""
68+
69+
for s in self._gcode_scripts:
70+
name = s['name']
71+
gcode = s['gcode']
72+
if name == "Pause":
73+
d[CLEARING_SCRIPT_KEY] = gcode
74+
elif name == "Generic Off":
75+
d[FINISHED_SCRIPT_KEY] = gcode
7776
d[RESTART_MAX_RETRIES_KEY] = 3
7877
d[RESTART_ON_PAUSE_KEY] = False
7978
d[RESTART_MAX_TIME_KEY] = 60 * 60
@@ -441,25 +440,15 @@ def reset(self):
441440
# part of TemplatePlugin
442441
def get_template_vars(self):
443442
return dict(
444-
cp_enabled=(self.d.active if hasattr(self, "d") else False),
445-
cp_bed_clearing_script=self._settings.get([CLEARING_SCRIPT_KEY]),
446-
cp_queue_finished=self._settings.get([FINISHED_SCRIPT_KEY]),
447-
cp_restart_on_pause_enabled=self._settings.get_boolean(
448-
[RESTART_ON_PAUSE_KEY]
449-
),
450-
cp_restart_on_pause_max_seconds=self._settings.get_int(
451-
[RESTART_MAX_TIME_KEY]
452-
),
453-
cp_restart_on_pause_max_restarts=self._settings.get_int(
454-
[RESTART_MAX_RETRIES_KEY]
455-
),
443+
printer_profiles=self._printer_profiles,
444+
gcode_scripts=self._gcode_scripts
456445
)
457446

458447
def get_template_configs(self):
459448
return [
460449
dict(
461450
type="settings",
462-
custom_bindings=False,
451+
custom_bindings=True,
463452
template="continuousprint_settings.jinja2",
464453
),
465454
dict(
@@ -480,6 +469,7 @@ def get_assets(self):
480469
"js/continuousprint_queueset.js",
481470
"js/continuousprint_job.js",
482471
"js/continuousprint_viewmodel.js",
472+
"js/continuousprint_settings.js",
483473
"js/continuousprint.js",
484474
],
485475
css=["css/continuousprint.css"],
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
GScript:
2+
- name: "Sweep Gantry"
3+
description: >
4+
This script example assumes a box style printer with a vertical Z axis and a 200mm x 235mm XY build area.
5+
It uses the printer's extruder to push the part off the build plate."
6+
version: 0.0.1
7+
gcode: |
8+
M17 ;enable steppers
9+
G91 ; Set relative for lift
10+
G0 Z10 ; lift z by 10
11+
G90 ;back to absolute positioning
12+
M190 R25 ; set bed to 25 and wait for cooldown
13+
G0 X200 Y235 ;move to back corner
14+
G0 X110 Y235 ;move to mid bed aft
15+
G0 Z1 ;come down to 1MM from bed
16+
G0 Y0 ;wipe forward
17+
G0 Y235 ;wipe aft
18+
G28 ; home
19+
- name: "Advance Belt Short"
20+
description: >
21+
This script works with a belt printer (specifically, a Creality CR-30). The belt is advanced to move
22+
the print out of the way before starting another print.
23+
version: 0.0.1
24+
gcode: |
25+
M17 ; enable steppers
26+
G91 ; Set relative for lift
27+
G21 ; Set units to mm
28+
G0 Z10 ; advance bed (Z) by 10mm
29+
G90 ; back to absolute positioning
30+
M104 S0; Set Hot-end to 0C (off)
31+
M140 S0; Set bed to 0C (off)
32+
- name: "Pause"
33+
description: >
34+
Use this script if you want to remove the print yourself but use the queue to keep track of your
35+
prints. It uses an @ Command to tell OctoPrint to pause the print. The printer will stay paused
36+
until you press "Resume" on the OctoPrint UI.
37+
version: 0.0.1
38+
gcode: |
39+
M18 ; disable steppers
40+
M104 T0 S0 ; extruder heater off
41+
M140 S0 ; heated bed heater off
42+
@pause ; wait for user input
43+
- name: "Generic Off"
44+
description: >
45+
This is a generic "heaters and motors off" script which should be compatible with most printers.
46+
version: 0.0.1
47+
gcode: |
48+
M18 ; disable steppers
49+
M104 T0 S0 ; extruder heater off
50+
M140 S0 ; heated bed heater off
51+
M300 S880 P300 ; beep to show its finished
52+
- name: "Advance Belt Long"
53+
description: >
54+
The same idea as "Advance Belt Short", but with a longer advancement to roll off all completed prints.
55+
version: 0.0.1
56+
gcode: |
57+
M17 ; enable steppers
58+
G91 ; Set relative for lift
59+
G21 ; Set units to mm
60+
G0 Z300 ; advance bed (Z) to roll off all parts
61+
M18 ; Disable steppers
62+
G90 ; back to absolute positioning
63+
M104 S0; Set Hot-end to 0C (off)
64+
M140 S0; Set bed to 0C (off)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
PrinterProfile:
2+
- name: "Generic"
3+
make: "Generic"
4+
model: "Generic"
5+
width: 150
6+
depth: 150
7+
height: 150
8+
formFactor: "rectangular"
9+
selfClearing: false
10+
defaults:
11+
clearBed: "Pause"
12+
finished: "Generic Off"
13+
- name: "Creality CR30"
14+
make: "Creality"
15+
model: "CR30"
16+
width: 200
17+
depth: 1000
18+
height: 200
19+
formFactor: "rectangular"
20+
selfClearing: true
21+
defaults:
22+
clearBed: "Advance Belt Short"
23+
finished: "Advance Belt Long"
24+
- name: "Monoprice Mini Delta V2"
25+
make: "Monoprice"
26+
model: "Mini Delta V2"
27+
width: 110
28+
depth: 110
29+
height: 120
30+
formFactor: "circular"
31+
selfClearing: false
32+
defaults:
33+
clearBed: "Pause"
34+
finished: "Generic Off"

continuousprint/static/js/continuousprint.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,11 @@ $(function() {
3232
],
3333
elements: ["#tab_plugin_continuousprint"]
3434
});
35+
OCTOPRINT_VIEWMODELS.push({
36+
construct: CPSettingsViewModel,
37+
dependencies: [
38+
"settingsViewModel",
39+
],
40+
elements: ["#settings_plugin_continuousprint"]
41+
});
3542
});
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
if (typeof log === "undefined" || log === null) {
2+
// In the testing environment, dependencies must be manually imported
3+
ko = require('knockout');
4+
log = {
5+
"getLogger": () => {return console;}
6+
};
7+
CP_PRINTER_PROFILES = [];
8+
CP_GCODE_SCRIPTS = [];
9+
}
10+
11+
function CPSettingsViewModel(parameters, profiles=CP_PRINTER_PROFILES, scripts=CP_GCODE_SCRIPTS) {
12+
var self = this;
13+
self.PLUGIN_ID = "octoprint.plugins.continuousprint";
14+
self.log = log.getLogger(self.PLUGIN_ID);
15+
self.settings = parameters[0];
16+
17+
// Constants defined in continuousprint_settings.jinja2, passed from the plugin (see `get_template_vars()` in __init__.py)
18+
self.profiles = {};
19+
for (let prof of profiles) {
20+
if (self.profiles[prof.make] === undefined) {
21+
self.profiles[prof.make] = {};
22+
}
23+
self.profiles[prof.make][prof.model] = prof;
24+
}
25+
self.scripts = {};
26+
for (let s of scripts) {
27+
self.scripts[s.name] = s.gcode;
28+
}
29+
30+
self.selected_make = ko.observable();
31+
let makes = Object.keys(self.profiles);
32+
makes.unshift("Select one");
33+
self.printer_makes = ko.observable(makes);
34+
self.selected_model = ko.observable();
35+
self.printer_models = ko.computed(function() {
36+
let models = self.profiles[self.selected_make()];
37+
if (models === undefined) {
38+
return ["-"];
39+
}
40+
let result = Object.keys(models);
41+
result.unshift("-");
42+
return result;
43+
});
44+
45+
self.modelChanged = function() {
46+
let profile = (self.profiles[self.selected_make()] || {})[self.selected_model()];
47+
if (profile === undefined) {
48+
return;
49+
}
50+
let cpset = self.settings.settings.plugins.continuousprint;
51+
cpset.cp_bed_clearing_script(self.scripts[profile.defaults.clearBed]);
52+
cpset.cp_queue_finished_script(self.scripts[profile.defaults.finished]);
53+
}
54+
}
55+
56+
57+
try {
58+
module.exports = {
59+
CPSettingsViewModel,
60+
CP_PRINTER_PROFILES,
61+
CP_GCODE_SCRIPTS,
62+
};
63+
} catch {}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Import must happen after declaring constants
2+
const VM = require('./continuousprint_settings');
3+
4+
const PROFILES = [
5+
{
6+
name: 'Generic',
7+
make: 'Generic',
8+
model: 'Generic',
9+
defaults: {
10+
clearBed: 'script1',
11+
finished: 'script2',
12+
},
13+
},
14+
{
15+
name: 'TestPrinter',
16+
make: 'Test',
17+
model: 'Printer',
18+
defaults: {
19+
clearBed: 'script1',
20+
finished: 'script2',
21+
},
22+
},
23+
]
24+
25+
const SCRIPTS = [
26+
{
27+
name: 'script1',
28+
gcode: 'test1',
29+
},
30+
{
31+
name: 'script2',
32+
gcode: 'test2',
33+
},
34+
];
35+
36+
37+
function mocks() {
38+
return [
39+
{
40+
settings: {
41+
plugins: {
42+
continuousprint: {
43+
cp_bed_clearing_script: jest.fn(),
44+
cp_queue_finished_script: jest.fn(),
45+
},
46+
},
47+
},
48+
},
49+
];
50+
}
51+
52+
test('makes are populated', () => {
53+
let v = new VM.CPSettingsViewModel(mocks(), PROFILES, SCRIPTS);
54+
expect(v.printer_makes().length).toBeGreaterThan(1); // Not just "Select one"
55+
});
56+
57+
test('models are populated based on selected_make', () => {
58+
let v = new VM.CPSettingsViewModel(mocks(), PROFILES, SCRIPTS);
59+
v.selected_make("Test");
60+
expect(v.printer_models()).toEqual(["-", "Printer"]);
61+
});
62+
63+
test('valid model change updates settings scripts', () => {
64+
let v = new VM.CPSettingsViewModel(mocks(), PROFILES, SCRIPTS);
65+
v.selected_make("Test");
66+
v.selected_model("Printer");
67+
v.modelChanged();
68+
expect(v.settings.settings.plugins.continuousprint.cp_bed_clearing_script).toHaveBeenCalledWith("test1");
69+
expect(v.settings.settings.plugins.continuousprint.cp_queue_finished_script).toHaveBeenCalledWith("test2");
70+
});
71+
72+
test('invalid model change is ignored', () => {
73+
let v = new VM.CPSettingsViewModel(mocks(), PROFILES, SCRIPTS);
74+
v.modelChanged();
75+
expect(v.settings.settings.plugins.continuousprint.cp_bed_clearing_script).not.toHaveBeenCalled();
76+
expect(v.settings.settings.plugins.continuousprint.cp_queue_finished_script).not.toHaveBeenCalled();
77+
});

0 commit comments

Comments
 (0)