Base cut off point for temperature graph on timestamps instead of data points
Cut off of the temperature graph is now not based on the number of data points any more but on the actual time of the data points. Anything older than ``n`` minutes will be cut off, with ``n`` defaulting to 30min. This value can be changed under "Temperatures" in the Settings Closes #343
This commit is contained in:
parent
ede2e8c593
commit
f19d0f0360
8 changed files with 79 additions and 11 deletions
|
|
@ -124,6 +124,9 @@
|
|||
* Made baudrate detection a bit more solid, still can't perform wonders.
|
||||
* Only show configuration options for additional extruders if more than one is available, and don't include offset
|
||||
configuration for first nozzle which acts as reference for the other offsets ([#677](https://github.com/foosel/OctoPrint/issues/677)).
|
||||
* Cut off of the temperature graph is now not based on the number of data points any more but on the actual time of the
|
||||
data points. Anything older than ``n`` minutes will be cut off, with ``n`` defaulting to 30min. This value can be
|
||||
changed under "Temperatures" in the Settings ([#343](https://github.com/foosel/OctoPrint/issues/343)).
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ from octoprint.printer import PrinterInterface, PrinterCallback, UnknownScript
|
|||
from octoprint.printer.estimation import TimeEstimationHelper
|
||||
from octoprint.settings import settings
|
||||
from octoprint.util import comm as comm
|
||||
from octoprint.util import InvariantContainer
|
||||
|
||||
|
||||
class Printer(PrinterInterface, comm.MachineComPrintCallback):
|
||||
|
|
@ -46,7 +47,7 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback):
|
|||
self._bedTemp = None
|
||||
self._targetTemp = None
|
||||
self._targetBedTemp = None
|
||||
self._temps = deque([], 300)
|
||||
self._temps = TemperatureHistory(cutoff=settings().getInt(["temperature", "cutoff"])*60)
|
||||
self._tempBacklog = []
|
||||
|
||||
self._latestMessage = None
|
||||
|
|
@ -937,3 +938,12 @@ class StateMonitor(object):
|
|||
}
|
||||
|
||||
|
||||
class TemperatureHistory(InvariantContainer):
|
||||
def __init__(self, cutoff=30 * 60):
|
||||
|
||||
def temperature_invariant(data):
|
||||
data.sort(key=lambda x: x["time"])
|
||||
now = int(time.time())
|
||||
return [item for item in data if item["time"] >= now - cutoff]
|
||||
|
||||
InvariantContainer.__init__(self, guarantee_invariant=temperature_invariant)
|
||||
|
|
|
|||
|
|
@ -89,7 +89,8 @@ def getSettings():
|
|||
"watched": s.getBaseFolder("watched")
|
||||
},
|
||||
"temperature": {
|
||||
"profiles": s.get(["temperature", "profiles"])
|
||||
"profiles": s.get(["temperature", "profiles"]),
|
||||
"cutoff": s.getInt(["temperature", "cutoff"])
|
||||
},
|
||||
"system": {
|
||||
"actions": s.get(["system", "actions"]),
|
||||
|
|
@ -210,6 +211,7 @@ def setSettings():
|
|||
|
||||
if "temperature" in data.keys():
|
||||
if "profiles" in data["temperature"].keys(): s.set(["temperature", "profiles"], data["temperature"]["profiles"])
|
||||
if "cutoff" in data["temperature"].keys(): s.setInt(["temperature", "cutoff"], data["temperature"]["cutoff"])
|
||||
|
||||
if "terminalFilters" in data.keys():
|
||||
s.set(["terminalFilters"], data["terminalFilters"])
|
||||
|
|
|
|||
|
|
@ -157,7 +157,8 @@ default_settings = {
|
|||
"profiles": [
|
||||
{"name": "ABS", "extruder" : 210, "bed" : 100 },
|
||||
{"name": "PLA", "extruder" : 180, "bed" : 60 }
|
||||
]
|
||||
],
|
||||
"cutoff": 30
|
||||
},
|
||||
"printerProfiles": {
|
||||
"default": None,
|
||||
|
|
|
|||
|
|
@ -109,8 +109,7 @@ $(function() {
|
|||
self.scripts_gcode_afterPrinterConnected = ko.observable(undefined);
|
||||
|
||||
self.temperature_profiles = ko.observableArray(undefined);
|
||||
|
||||
self.temperature_profiles = ko.observableArray(undefined);
|
||||
self.temperature_cutoff = ko.observable(undefined);
|
||||
|
||||
self.system_actions = ko.observableArray([]);
|
||||
|
||||
|
|
@ -260,6 +259,7 @@ $(function() {
|
|||
self.scripts_gcode_afterPrinterConnected(response.scripts.gcode.afterPrinterConnected);
|
||||
|
||||
self.temperature_profiles(response.temperature.profiles);
|
||||
self.temperature_cutoff(response.temperature.cutoff);
|
||||
|
||||
self.system_actions(response.system.actions);
|
||||
|
||||
|
|
@ -334,7 +334,8 @@ $(function() {
|
|||
"watched": self.folder_watched()
|
||||
},
|
||||
"temperature": {
|
||||
"profiles": self.temperature_profiles()
|
||||
"profiles": self.temperature_profiles(),
|
||||
"cutoff": self.temperature_cutoff()
|
||||
},
|
||||
"system": {
|
||||
"actions": self.system_actions()
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ $(function() {
|
|||
self.isLoading = ko.observable(undefined);
|
||||
|
||||
self.temperature_profiles = self.settingsViewModel.temperature_profiles;
|
||||
self.temperature_cutoff = self.settingsViewModel.temperature_cutoff;
|
||||
|
||||
self.heaterOptions = ko.observable({});
|
||||
|
||||
|
|
@ -161,11 +162,6 @@ $(function() {
|
|||
if (!CONFIG_TEMPERATURE_GRAPH) return;
|
||||
|
||||
self.temperatures = self._processTemperatureData(data, self.temperatures);
|
||||
_.each(_.keys(self.heaterOptions()), function(d) {
|
||||
self.temperatures[d].actual = self.temperatures[d].actual.slice(-300);
|
||||
self.temperatures[d].target = self.temperatures[d].target.slice(-300);
|
||||
});
|
||||
|
||||
self.updatePlot();
|
||||
};
|
||||
|
||||
|
|
@ -215,6 +211,16 @@ $(function() {
|
|||
})
|
||||
});
|
||||
|
||||
var now = Date.now();
|
||||
var filterOld = function(item) {
|
||||
return item[0] >= now - self.temperature_cutoff() * 60 * 1000;
|
||||
};
|
||||
|
||||
_.each(_.keys(self.heaterOptions()), function(d) {
|
||||
result[d].actual = _.filter(result[d].actual, filterOld);
|
||||
result[d].target = _.filter(result[d].target, filterOld);
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,16 @@
|
|||
<form class="form-horizontal">
|
||||
<h3>{{ _('Temperature Graph') }}</h3>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Graph cutoff') }}</label>
|
||||
<div class="controls">
|
||||
<div class="input-append">
|
||||
<input type="number" class="input-mini text-right" data-bind="value: temperature_cutoff">
|
||||
<span class="add-on">min</span>
|
||||
</div>
|
||||
<span class="help-block">{{ _('Needs a restart of OctoPrint to become active.') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<h3>{{ _('Temperature Presets') }}</h3>
|
||||
<div class="row-fluid">
|
||||
<div class="offset4 span3"><h4>{{ _('Extruder') }}</h4></div>
|
||||
<div class="span3"><h4>{{ _('Bed') }}</h4></div>
|
||||
|
|
|
|||
|
|
@ -649,3 +649,36 @@ class CountedEvent(object):
|
|||
self._counter = self._max
|
||||
self._event.set()
|
||||
self._logger.debug("Set event")
|
||||
|
||||
|
||||
class InvariantContainer(object):
|
||||
def __init__(self, initial_data=None, guarantee_invariant=None):
|
||||
from collections import Iterable
|
||||
from threading import RLock
|
||||
|
||||
if guarantee_invariant is None:
|
||||
guarantee_invariant = lambda data: data
|
||||
|
||||
self._data = []
|
||||
self._mutex = RLock()
|
||||
self._invariant = guarantee_invariant
|
||||
|
||||
if initial_data is not None and isinstance(initial_data, Iterable):
|
||||
for item in initial_data:
|
||||
self.append(item)
|
||||
|
||||
def append(self, item):
|
||||
with self._mutex:
|
||||
self._data.append(item)
|
||||
self._data = self._invariant(self._data)
|
||||
|
||||
def remove(self, item):
|
||||
with self._mutex:
|
||||
self._data.remove(item)
|
||||
self._data = self._invariant(self._data)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._data)
|
||||
|
||||
def __iter__(self):
|
||||
return self._data.__iter__()
|
||||
Loading…
Reference in a new issue