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:
Gina Häußge 2015-05-05 18:18:55 +02:00
parent ede2e8c593
commit f19d0f0360
8 changed files with 79 additions and 11 deletions

View file

@ -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

View file

@ -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)

View file

@ -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"])

View file

@ -157,7 +157,8 @@ default_settings = {
"profiles": [
{"name": "ABS", "extruder" : 210, "bed" : 100 },
{"name": "PLA", "extruder" : 180, "bed" : 60 }
]
],
"cutoff": 30
},
"printerProfiles": {
"default": None,

View file

@ -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()

View file

@ -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;
};

View file

@ -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>

View file

@ -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__()