Added controls for changing feed and flow rate factors to Controls tab

Closes #362
This commit is contained in:
Gina Häußge 2015-02-22 21:20:56 +01:00
parent e476e4154e
commit 4abcf1054b
10 changed files with 195 additions and 34 deletions

View file

@ -32,6 +32,7 @@
- Home: Home X and Y axes
- End: Home Z axes
- 1, 2, 3, 4: change step size used (0.1, 1, 10, 100mm)
* Controls for adjusting feed and flow rate factor added to Controls ([#362](https://github.com/foosel/OctoPrint/issues/362))
### Improvements

View file

@ -199,8 +199,13 @@ Issue a print head command
* ``axes``: A list of axes which to home, valid values are one or more of ``x``, ``y``, ``z``.
All of these commands may only be sent if the printer is currently operational and not printing. Otherwise a
:http:statuscode:`409` is returned.
feedrate
Changes the feedrate factor to apply to the movement's of the axes.
* ``factor``: The new factor, percentage as integer or float (percentage divided by 100) between 50 and 200%.
All of these commands except ``feedrate`` may only be sent if the printer is currently operational and not printing.
Otherwise a :http:statuscode:`409` is returned.
Upon success, a status code of :http:statuscode:`204` and an empty body is returned.
@ -246,13 +251,34 @@ Issue a print head command
HTTP/1.1 204 No Content
**Example feed rate request**
Set the feed rate factor to 105%.
.. sourcecode:: http
POST /api/printer/printhead HTTP/1.1
Host: example.com
Content-Type: application/json
X-Api-Key: abcdef...
{
"command": "feedrate",
"factor": 105
}
.. sourcecode:: http
HTTP/1.1 204 No Content
:json string command: The command to issue, either ``jog`` or ``home``.
:json number x: ``jog`` command: The amount to travel on the X axis in mm.
:json number y: ``jog`` command: The amount to travel on the Y axis in mm.
:json number z: ``jog`` command: The amount to travel on the Z axis in mm.
:json array axes: ``home`` command: The axes which to home, valid values are one or more of ``x``, ``y`` and ``z``.
:json number factor: ``feedrate`` command: The factor to apply to the feed rate, percentage between 50 and 200% as integer or float.
:statuscode 204: No error
:statuscode 400: Invalid axis specified, invalid value for travel amount for a jog command or otherwise invalid
:statuscode 400: Invalid axis specified, invalid value for travel amount for a jog command or factor for feed rate or otherwise invalid
request.
:statuscode 409: If the printer is not operational or currently printing.
@ -288,6 +314,10 @@ Issue a tool command
* ``amount``: The amount of filament to extrude in mm. May be negative to retract.
flowrate
Changes the flow rate factor to apply to extrusion of the tool.
* ``factor``: The new factor, percentage as integer or float (percentage divided by 100) between 75 and 125%.
All of these commands may only be sent if the printer is currently operational and -- in case of ``select`` and
``extrude`` -- not printing. Otherwise a :http:statuscode:`409` is returned.
@ -400,14 +430,35 @@ Issue a tool command
HTTP/1.1 204 No Content
**Example flow rate request**
Set the flow rate factor to 95%.
.. sourcecode:: http
POST /api/printer/tool HTTP/1.1
Host: example.com
Content-Type: application/json
X-Api-Key: abcdef...
{
"command": "flowrate",
"factor": 95
}
.. sourcecode:: http
HTTP/1.1 204 No Content
:json string command: The command to issue, either ``target``, ``offset``, ``select`` or ``extrude``.
:json object targets: ``target`` command: The target temperatures to set. Valid properties have to match the format ``tool{n}``.
:json object offsets: ``offset`` command: The offset temperature to set. Valid properties have to match the format ``tool{n}``.
:json object tool: ``select`` command: The tool to select, value has to match the format ``tool{n}``.
:json object amount: ``extrude`` command: The amount of filament to extrude from the currently selected tool.
:json number factor: ``flowrate`` command: The factor to apply to the flow rate, percentage between 75 and 125% as integer or float.
:statuscode 204: No error
:statuscode 400: If ``targets`` or ``offsets`` contains a property or ``tool`` contains a value not matching the format
``tool{n}``, the target/offset temperature or extrusion amount is not a valid number or outside of
``tool{n}``, the target/offset temperature, extrusion amount or flow rate factor is not a valid number or outside of
the supported range, or if the request is otherwise invalid.
:statuscode 409: If the printer is not operational or -- in case of ``select`` or ``extrude`` -- currently printing.

View file

@ -303,6 +303,26 @@ class Printer():
self._comm.setTemperatureOffset(tool, bed)
self._stateMonitor.setTempOffsets(validatedOffsets)
def _convertRateValue(self, factor, min=0, max=200):
if not isinstance(factor, (int, float, long)):
raise ValueError("factor is not a number")
if isinstance(factor, float):
factor = int(factor * 100.0)
if factor < min or factor > max:
raise ValueError("factor must be a value between %f and %f" % (min, max))
return factor
def feedRate(self, factor):
factor = self._convertRateValue(factor, min=50, max=200)
self.command("M220 S%d" % factor)
def flowRate(self, factor):
factor = self._convertRateValue(factor, min=75, max=125)
self.command("M221 S%d" % factor)
def selectFile(self, filename, sd, printAfterSelect=False):
if self._comm is None or (self._comm.isBusy() or self._comm.isStreaming()):
self._logger.info("Cannot load file: printer not connected or currently busy")

View file

@ -61,7 +61,8 @@ def printerToolCommand():
"select": ["tool"],
"target": ["targets"],
"offset": ["offsets"],
"extrude": ["amount"]
"extrude": ["amount"],
"flowrate": ["factor"]
}
command, data, response = util.getJsonCommandFromRequest(request, valid_commands)
if response is not None:
@ -125,6 +126,15 @@ def printerToolCommand():
return make_response("Not a number for extrusion amount: %r" % amount, 400)
printer.extrude(amount)
elif command == "flowrate":
factor = data["factor"]
if not isinstance(factor, (int, long, float)):
return make_response("Not a number for flow rate: %r" % factor, 400)
try:
printer.flowRate(factor)
except ValueError as e:
return make_response("Invalid value for flow rate: %s" % e.message, 400)
return NO_CONTENT
@ -219,7 +229,8 @@ def printerPrintheadCommand():
valid_commands = {
"jog": [],
"home": ["axes"]
"home": ["axes"],
"feedrate": ["factor"]
}
command, data, response = util.getJsonCommandFromRequest(request, valid_commands)
if response is not None:
@ -253,6 +264,15 @@ def printerPrintheadCommand():
# execute the home command
printer.home(validated_values)
elif command == "feedrate":
factor = data["factor"]
if not isinstance(factor, (int, long, float)):
return make_response("Not a number for feed rate: %r" % factor, 400)
try:
printer.feedRate(factor)
except ValueError as e:
return make_response("Invalid value for feed rate: %s" % e.message, 400)
return NO_CONTENT

File diff suppressed because one or more lines are too long

View file

@ -24,6 +24,9 @@ function ControlViewModel(loginStateViewModel, settingsViewModel) {
self.tools = ko.observableArray([]);
self.feedRate = ko.observable(100);
self.flowRate = ko.observable(100);
self.feedbackControlLookup = {};
self.controlsFromServer = [];
@ -181,27 +184,20 @@ function ControlViewModel(loginStateViewModel, settingsViewModel) {
};
data[axis] = distance * multiplier;
$.ajax({
url: API_BASEURL + "printer/printhead",
type: "POST",
dataType: "json",
contentType: "application/json; charset=UTF-8",
data: JSON.stringify(data)
});
self.sendPrintHeadCommand(data);
};
self.sendHomeCommand = function(axis) {
var data = {
self.sendPrintHeadCommand({
"command": "home",
"axes": axis
};
});
};
$.ajax({
url: API_BASEURL + "printer/printhead",
type: "POST",
dataType: "json",
contentType: "application/json; charset=UTF-8",
data: JSON.stringify(data)
self.sendFeedRateCommand = function() {
self.sendPrintHeadCommand({
"command": "feedrate",
"factor": self.feedRate()
});
};
@ -213,17 +209,35 @@ function ControlViewModel(loginStateViewModel, settingsViewModel) {
self._sendECommand(-1);
};
self.sendFlowRateCommand = function() {
self.sendToolCommand({
"command": "flowrate",
"factor": self.flowRate()
});
};
self._sendECommand = function(dir) {
var length = self.extrusionAmount();
if (!length) length = self.settings.printer_defaultExtrusionLength();
var data = {
self.sendToolCommand({
command: "extrude",
amount: length * dir
};
});
};
self.sendSelectToolCommand = function(data) {
if (!data || !data.key()) return;
self.sendToolCommand({
command: "select",
tool: data.key()
});
};
self.sendPrintHeadCommand = function(data) {
$.ajax({
url: API_BASEURL + "printer/tool",
url: API_BASEURL + "printer/printhead",
type: "POST",
dataType: "json",
contentType: "application/json; charset=UTF-8",
@ -231,20 +245,13 @@ function ControlViewModel(loginStateViewModel, settingsViewModel) {
});
};
self.sendSelectToolCommand = function(data) {
if (!data || !data.key()) return;
var payload = {
command: "select",
tool: data.key()
};
self.sendToolCommand = function(data) {
$.ajax({
url: API_BASEURL + "printer/tool",
type: "POST",
dataType: "json",
contentType: "application/json; charset=UTF-8",
data: JSON.stringify(payload)
data: JSON.stringify(data)
});
};

View file

@ -0,0 +1,44 @@
/**
* Based on bootstrap-slider-knockout-binding, written by Cosmin Stefan-Dobrin, https://github.com/cosminstefanxp,
* licensed under MIT License
*
* Github: https://github.com/cosminstefanxp/bootstrap-slider-knockout-binding
*/
ko.bindingHandlers.slider = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var params = valueAccessor();
// Check whether the value observable is either placed directly or in the paramaters object.
if (!(ko.isObservable(params) || params['value']))
throw "You need to define an observable value for the sliderValue. Either pass the observable directly or as the 'value' field in the parameters.";
// Identify the value and initialize the slider
var valueObservable;
if (ko.isObservable(params)) {
valueObservable = params;
$(element).slider({value: ko.utils.unwrapObservable(params)});
} else {
valueObservable = params['value'];
// Replace the 'value' field in the options object with the actual value
params['value'] = ko.utils.unwrapObservable(valueObservable);
$(element).slider(params);
}
// Make sure we update the observable when changing the slider value
$(element).on('slide', function (ev) {
valueObservable(ev.value);
});
},
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var modelValue = valueAccessor();
var valueObservable;
if (ko.isObservable(modelValue)) {
valueObservable = modelValue;
} else {
valueObservable = modelValue['value'];
}
$(element).slider('setValue', parseFloat(ko.isObservable(valueObservable) ? valueObservable() : valueObservable));
}
};

View file

@ -534,6 +534,16 @@ ul.dropdown-menu li a {
height: 30px;
}
.slider {
margin-bottom: 10px;
}
.slider-handle {
width: 14px;
height: 14px;
margin-left: -7px;
margin-top: -3px;
}
}
/** GCODE viewer */

View file

@ -24,6 +24,7 @@
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/detectmobilebrowser.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/md5.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/pnotify.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/bootstrap-slider-knockout-binding.js') }}"></script>
<!-- Include OctoPrint files -->
<!-- TODO: merge/minimize in the future -->

View file

@ -62,6 +62,9 @@
</div>
</div>
<!-- Feed rate -->
<input type="number" style="width: 153px" data-bind="slider: {min: 50, max: 150, step: 1, value: feedRate, tooltip: 'hide'}">
<button class="btn btn-block" style="width: 169px" data-bind="enable: isOperational() && loginState.isUser(), click: function() { $root.sendFeedRateCommand() }">{{ _('Feed rate:') }}<span data-bind="text: feedRate() + '%'"></span></button>
</div>
<!-- Extrusion control panel -->
<div class="jog-panel" style="display: none;" data-bind="visible: loginState.isUser">
@ -82,6 +85,10 @@
</div>
<button class="btn btn-block control-box" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendExtrudeCommand() }">{{ _('Extrude') }}</button>
<button class="btn btn-block control-box" data-bind="enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendRetractCommand() }">{{ _('Retract') }}</button>
<!-- Flow rate -->
<input style="width: 100px" type="number" data-bind="slider: {min: 75, max: 125, step: 1, value: flowRate, tooltip: 'hide'}">
<button class="btn btn-block control-box" data-bind="enable: isOperational() && loginState.isUser(), click: function() { $root.sendFlowRateCommand() }">{{ _('Flow rate:') }}<span data-bind="text: flowRate() + '%'"></span></button>
</div>
</div>
<!-- General control panel -->