Support for shared nozzle setups

Support multi-extruder setups that share a single nozzle and heater,
like the E3D Cyclops, Diamon hotend or probably the upcoming Prusa Mk2
multi-extruder upgrade.

The Control tab will still allow tool switching and extruding for
the configured extruders, the Temperature tab will only display
one hotend temperature though.

Printer profiles have been extended by a new option
extruder.sharedNozzle that defaults to False. Extruder offsets are
not displayed in the profile editor if that setting is checked and
reset to (0,0).
This commit is contained in:
Gina Häußge 2017-01-26 13:33:42 +01:00
parent 8f3b6dca79
commit 7ab3eabe0f
7 changed files with 71 additions and 30 deletions

View file

@ -54,8 +54,12 @@ class VirtualPrinter(object):
self._send(item)
self.currentExtruder = 0
self.temp = [0.0] * settings().getInt(["devel", "virtualPrinter", "numExtruders"])
self.targetTemp = [0.0] * settings().getInt(["devel", "virtualPrinter", "numExtruders"])
self.extruderCount = settings().getInt(["devel", "virtualPrinter", "numExtruders"])
self.sharedNozzle = settings().getBoolean(["devel", "virtualPrinter", "sharedNozzle"])
self.temperatureCount = (1 if self.sharedNozzle else self.extruderCount)
self.temp = [0.0] * self.temperatureCount
self.targetTemp = [0.0] * self.temperatureCount
self.lastTempAt = time.time()
self.bedTemp = 1.0
self.bedTargetTemp = 1.0
@ -301,8 +305,10 @@ class VirtualPrinter(object):
##~~ command implementations
def _gcode_T(self, code, data):
self.currentExtruder = int(code)
self._send("Active Extruder: %d" % self.currentExtruder)
t = int(code)
if 0 <= t <= self.extruderCount:
self.currentExtruder = t
self._send("Active Extruder: %d" % self.currentExtruder)
def _gcode_F(self, code, data):
if self._supportF:
@ -628,7 +634,7 @@ class VirtualPrinter(object):
includeOk = not self._okBeforeCommandOutput
# send simulated temperature data
if settings().getInt(["devel", "virtualPrinter", "numExtruders"]) > 1:
if self.temperatureCount > 1:
allTemps = []
for i in range(len(self.temp)):
allTemps.append((i, self.temp[i], self.targetTemp[i]))
@ -672,7 +678,7 @@ class VirtualPrinter(object):
except:
pass
if tool >= settings().getInt(["devel", "virtualPrinter", "numExtruders"]):
if tool >= self.temperatureCount:
return
try:

View file

@ -124,7 +124,10 @@ class PrinterProfileManager(object):
- Extruder offsets relative to first extruder, list of (x, y) tuples, first is always (0,0)
* - ``extruder.nozzleDiameter``
- ``float``
- Diameter of the printer nozzle
- Diameter of the printer nozzle(s)
* - ``extruder.sharedNozzle``
- ``boolean``
- Whether there's only one nozzle shared among all extruders (true) or one nozzle per extruder (false).
* - ``axes``
- ``dict``
- Information about the printer axes
@ -185,7 +188,8 @@ class PrinterProfileManager(object):
offsets = [
(0, 0)
],
nozzleDiameter = 0.4
nozzleDiameter = 0.4,
sharedNozzle = False
),
axes=dict(
x = dict(speed=6000, inverted=False),
@ -390,6 +394,13 @@ class PrinterProfileManager(object):
def _load_default(self):
default_overrides = settings().get(["printerProfiles", "defaultProfile"])
if self._migrate_profile(default_overrides):
try:
settings().set(["printerProfiles", "defaultProfile"], default_overrides)
settings().save()
except:
self._logger.exception("Tried to save default profile after migrating it while loading, ran into exception")
profile = self._ensure_valid_profile(dict_merge(copy.deepcopy(self.__class__.default), default_overrides))
if not profile:
self._logger.warn("Invalid default profile after applying overrides")
@ -424,6 +435,14 @@ class PrinterProfileManager(object):
profile["volume"]["custom_box"] = False
modified = True
if "extruder" in profile and not "sharedNozzle" in profile["extruder"]:
profile["extruder"]["sharedNozzle"] = False
modified = True
if "extruder" in profile and "sharedNozzle" in profile["extruder"] and profile["extruder"]["sharedNozzle"]:
profile["extruder"]["offsets"] = [(0.0, 0.0)]
modified = True
return modified
def _ensure_valid_profile(self, profile):
@ -462,7 +481,7 @@ class PrinterProfileManager(object):
return False
# convert booleans
for path in (("axes", "x", "inverted"), ("axes", "y", "inverted"), ("axes", "z", "inverted")):
for path in (("axes", "x", "inverted"), ("axes", "y", "inverted"), ("axes", "z", "inverted"), ("extruder", "sharedNozzle")):
try:
convert_value(profile, path, bool)
except Exception as e:

View file

@ -356,7 +356,8 @@ default_settings = {
"echoOnM117": True,
"brokenM29": True,
"supportF": False,
"firmwareName": "Virtual Marlin 1.0"
"firmwareName": "Virtual Marlin 1.0",
"sharedNozzle": False
}
}
}

View file

@ -174,7 +174,7 @@ $(function() {
currentProfileData = self.settings.printerProfiles.currentProfileData();
}
if (currentProfileData && currentProfileData.extruder && currentProfileData.extruder.offsets()) {
if (currentProfileData && currentProfileData.extruder && currentProfileData.extruder.offsets() && !currentProfileData.extruder.sharedNozzle()) {
var offsets = [];
_.each(currentProfileData.extruder.offsets(), function(offset) {
offsets.push({x: offset[0], y: offset[1]})

View file

@ -25,7 +25,8 @@ $(function() {
offsets: [
[0,0]
],
nozzleDiameter: 0.4
nozzleDiameter: 0.4,
sharedNozzle: false
}
}
};
@ -63,6 +64,7 @@ $(function() {
self.nozzleDiameter = ko.observable();
self.extruders = ko.observable();
self.extruderOffsets = ko.observableArray();
self.sharedNozzle = ko.observable();
self.axisXSpeed = ko.observable();
self.axisYSpeed = ko.observable();
@ -209,6 +211,7 @@ $(function() {
self.heatedBed(data.heatedBed);
self.nozzleDiameter(data.extruder.nozzleDiameter);
self.sharedNozzle(data.extruder.sharedNozzle);
self.extruders(data.extruder.count);
var offsets = [];
if (data.extruder.count > 1) {
@ -271,7 +274,8 @@ $(function() {
offsets: [
[0.0, 0.0]
],
nozzleDiameter: validFloat(self.nozzleDiameter(), defaultProfile.extruder.nozzleDiameter)
nozzleDiameter: validFloat(self.nozzleDiameter(), defaultProfile.extruder.nozzleDiameter),
sharedNozzle: self.sharedNozzle()
},
axes: {
x: {

View file

@ -49,7 +49,8 @@ $(function() {
// tools
var currentProfileData = self.settingsViewModel.printerProfiles.currentProfileData();
var numExtruders = (currentProfileData ? currentProfileData.extruder.count() : 0);
if (numExtruders && numExtruders > 1) {
var sharedNozzle = (currentProfileData ? currentProfileData.extruder.sharedNozzle() : false);
if (numExtruders && numExtruders > 1 && !sharedNozzle) {
// multiple extruders
for (var extruder = 0; extruder < numExtruders; extruder++) {
color = graphColors.shift();
@ -62,7 +63,7 @@ $(function() {
tools[extruder]["name"](gettext("Tool") + " " + extruder);
tools[extruder]["key"]("tool" + extruder);
}
} else if (numExtruders == 1) {
} else if (numExtruders == 1 || sharedNozzle) {
// only one extruder, no need to add numbers
color = graphColors[0];
heaterOptions["tool0"] = {name: "T", color: color};

View file

@ -17,28 +17,38 @@
</div>
<div data-bind="visible: extruders() > 1" style="display: none">
<div class="control-group">
<label class="control-label">{{ _('Shared nozzle') }}</label>
<div class="controls">
{{ _('Please specify the nozzle offsets of the extruders relative to the first nozzle T0.') }}
<input type="checkbox" data-bind="checked: sharedNozzle">
<span class="help-block">{{ _('Check this if your printer has a single nozzle and heater shared among its extruders instead of one nozzle and heater per extruder') }}</span>
</div>
</div>
<!-- ko foreach: koExtruderOffsets -->
<div class="control-group">
<label class="control-label">{{ _('Offset') }} T<span data-bind="text: idx"></span></label>
<div class="controls form-inline">
<label>X</label>
<div class="input-append">
<input type="number" step="0.01" class="input-mini text-right" data-bind="value: x" placeholder="0">
<span class="add-on">mm</span>
</div>
<label>Y</label>
<div class="input-append">
<input type="number" step="0.01" class="input-mini text-right" data-bind="value: y" placeholder="0">
<span class="add-on">mm</span>
<div data-bind="visible: !sharedNozzle()" style="display: none">
<div class="control-group">
<div class="controls">
{{ _('Please specify the nozzle offsets of the extruders relative to the first nozzle T0.') }}
</div>
</div>
<!-- ko foreach: koExtruderOffsets -->
<div class="control-group">
<label class="control-label">{{ _('Offset') }} T<span data-bind="text: idx"></span></label>
<div class="controls form-inline">
<label>X</label>
<div class="input-append">
<input type="number" step="0.01" class="input-mini text-right" data-bind="value: x" placeholder="0">
<span class="add-on">mm</span>
</div>
<label>Y</label>
<div class="input-append">
<input type="number" step="0.01" class="input-mini text-right" data-bind="value: y" placeholder="0">
<span class="add-on">mm</span>
</div>
</div>
</div>
<!-- /ko -->
</div>
<!-- /ko -->
</div>
<p>