commit
fcbd5ba042
|
@ -46,7 +46,10 @@ class EventManager(object):
|
|||
eventListeners = self._registeredListeners[event]
|
||||
for listener in eventListeners:
|
||||
self._logger.debug("Sending action to %r" % listener)
|
||||
listener(event, payload)
|
||||
try:
|
||||
listener(event, payload)
|
||||
except:
|
||||
self._logger.exception("Got an exception while sending event %s (Payload: %r) to %s" % (event, payload, listener))
|
||||
|
||||
|
||||
def subscribe(self, event, callback):
|
||||
|
|
|
@ -3,7 +3,7 @@ __author__ = "Gina Häußge <osd@foosel.net>"
|
|||
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
|
||||
|
||||
from werkzeug.utils import secure_filename
|
||||
import tornadio2
|
||||
from sockjs.tornado import SockJSRouter, SockJSConnection
|
||||
from flask import Flask, request, render_template, jsonify, send_from_directory, url_for, current_app, session, abort, make_response
|
||||
from flask.ext.login import LoginManager, login_user, logout_user, login_required, current_user
|
||||
from flask.ext.principal import Principal, Permission, RoleNeed, Identity, identity_changed, AnonymousIdentity, identity_loaded, UserNeed
|
||||
|
@ -34,6 +34,7 @@ app = Flask("octoprint")
|
|||
# In order that threads don't start too early when running as a Daemon
|
||||
printer = None
|
||||
timelapse = None
|
||||
debug = False
|
||||
|
||||
gcodeManager = None
|
||||
userManager = None
|
||||
|
@ -46,9 +47,9 @@ user_permission = Permission(RoleNeed("user"))
|
|||
|
||||
#~~ Printer state
|
||||
|
||||
class PrinterStateConnection(tornadio2.SocketConnection):
|
||||
def __init__(self, printer, gcodeManager, userManager, eventManager, session, endpoint=None):
|
||||
tornadio2.SocketConnection.__init__(self, session, endpoint)
|
||||
class PrinterStateConnection(SockJSConnection):
|
||||
def __init__(self, printer, gcodeManager, userManager, eventManager, session):
|
||||
SockJSConnection.__init__(self, session)
|
||||
|
||||
self._logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -64,8 +65,14 @@ class PrinterStateConnection(tornadio2.SocketConnection):
|
|||
self._userManager = userManager
|
||||
self._eventManager = eventManager
|
||||
|
||||
def _getRemoteAddress(self, info):
|
||||
forwardedFor = info.headers.get("X-Forwarded-For")
|
||||
if forwardedFor is not None:
|
||||
return forwardedFor.split(",")[0]
|
||||
return info.ip
|
||||
|
||||
def on_open(self, info):
|
||||
self._logger.info("New connection from client: %s" % info.ip)
|
||||
self._logger.info("New connection from client: %s" % self._getRemoteAddress(info))
|
||||
self._printer.registerCallback(self)
|
||||
self._gcodeManager.registerCallback(self)
|
||||
|
||||
|
@ -102,16 +109,16 @@ class PrinterStateConnection(tornadio2.SocketConnection):
|
|||
"logs": logs,
|
||||
"messages": messages
|
||||
})
|
||||
self.emit("current", data)
|
||||
self._emit("current", data)
|
||||
|
||||
def sendHistoryData(self, data):
|
||||
self.emit("history", data)
|
||||
self._emit("history", data)
|
||||
|
||||
def sendUpdateTrigger(self, type):
|
||||
self.emit("updateTrigger", type)
|
||||
self._emit("updateTrigger", type)
|
||||
|
||||
def sendFeedbackCommandOutput(self, name, output):
|
||||
self.emit("feedbackCommandOutput", {"name": name, "output": output})
|
||||
self._emit("feedbackCommandOutput", {"name": name, "output": output})
|
||||
|
||||
def addLog(self, data):
|
||||
with self._logBacklogMutex:
|
||||
|
@ -128,6 +135,9 @@ class PrinterStateConnection(tornadio2.SocketConnection):
|
|||
def _onMovieDone(self, event, payload):
|
||||
self.sendUpdateTrigger("timelapseFiles")
|
||||
|
||||
def _emit(self, type, payload):
|
||||
self.send({type: payload})
|
||||
|
||||
def restricted_access(func):
|
||||
"""
|
||||
If you decorate a view with this, it will ensure that first setup has been
|
||||
|
@ -159,6 +169,8 @@ def index():
|
|||
except:
|
||||
pass
|
||||
|
||||
global debug
|
||||
|
||||
return render_template(
|
||||
"index.jinja2",
|
||||
ajaxBaseUrl=BASEURL,
|
||||
|
@ -169,6 +181,7 @@ def index():
|
|||
enableAccessControl=userManager is not None,
|
||||
enableSdSupport=settings().get(["feature", "sdSupport"]),
|
||||
firstRun=settings().getBoolean(["server", "firstRun"]) and (userManager is None or not userManager.hasBeenCustomized()),
|
||||
debug=debug,
|
||||
gitBranch=branch,
|
||||
gitCommit=commit
|
||||
)
|
||||
|
@ -602,7 +615,8 @@ def getSettings():
|
|||
"system": {
|
||||
"actions": s.get(["system", "actions"]),
|
||||
"events": s.get(["system", "events"])
|
||||
}
|
||||
},
|
||||
"terminalFilters": s.get(["terminalFilters"])
|
||||
})
|
||||
|
||||
@app.route(BASEURL + "settings", methods=["POST"])
|
||||
|
@ -671,6 +685,9 @@ def setSettings():
|
|||
if "temperature" in data.keys():
|
||||
if "profiles" in data["temperature"].keys(): s.set(["temperature", "profiles"], data["temperature"]["profiles"])
|
||||
|
||||
if "terminalFilters" in data.keys():
|
||||
s.set(["terminalFilters"], data["terminalFilters"])
|
||||
|
||||
if "system" in data.keys():
|
||||
if "actions" in data["system"].keys(): s.set(["system", "actions"], data["system"]["actions"])
|
||||
if "events" in data["system"].keys(): s.set(["system", "events"], data["system"]["events"])
|
||||
|
@ -930,12 +947,15 @@ class Server():
|
|||
global userManager
|
||||
global eventManager
|
||||
global loginManager
|
||||
global debug
|
||||
|
||||
from tornado.wsgi import WSGIContainer
|
||||
from tornado.httpserver import HTTPServer
|
||||
from tornado.ioloop import IOLoop
|
||||
from tornado.web import Application, FallbackHandler
|
||||
|
||||
debug = self._debug
|
||||
|
||||
# first initialize the settings singleton and make sure it uses given configfile and basedir if available
|
||||
self._initSettings(self._configfile, self._basedir)
|
||||
|
||||
|
@ -978,7 +998,7 @@ class Server():
|
|||
logger.info("Listening on http://%s:%d" % (self._host, self._port))
|
||||
app.debug = self._debug
|
||||
|
||||
self._router = tornadio2.TornadioRouter(self._createSocketConnection)
|
||||
self._router = SockJSRouter(self._createSocketConnection, "/sockjs")
|
||||
|
||||
self._tornado_app = Application(self._router.urls + [
|
||||
(".*", FallbackHandler, {"fallback": WSGIContainer(app)})
|
||||
|
@ -998,9 +1018,9 @@ class Server():
|
|||
logger.fatal("Now that is embarrassing... Something really really went wrong here. Please report this including the stacktrace below in OctoPrint's bugtracker. Thanks!")
|
||||
logger.exception("Stacktrace follows:")
|
||||
|
||||
def _createSocketConnection(self, session, endpoint=None):
|
||||
def _createSocketConnection(self, session):
|
||||
global printer, gcodeManager, userManager, eventManager
|
||||
return PrinterStateConnection(printer, gcodeManager, userManager, eventManager, session, endpoint)
|
||||
return PrinterStateConnection(printer, gcodeManager, userManager, eventManager, session)
|
||||
|
||||
def _checkForRoot(self):
|
||||
if "geteuid" in dir(os) and os.geteuid() == 0:
|
||||
|
|
|
@ -106,6 +106,11 @@ default_settings = {
|
|||
"enabled": False,
|
||||
"key": ''.join('%02X' % ord(z) for z in uuid.uuid4().bytes)
|
||||
},
|
||||
"terminalFilters": [
|
||||
{ "name": "Suppress M105 requests/responses", "regex": "(Send: M105)|(Recv: ok T:)" },
|
||||
{ "name": "Suppress M27 requests/responses", "regex": "(Send: M27)|(Recv: SD printing byte)" }
|
||||
]
|
||||
},
|
||||
"devel": {
|
||||
"virtualPrinter": {
|
||||
"enabled": False,
|
||||
|
|
|
@ -11,8 +11,32 @@ function DataUpdater(loginStateViewModel, connectionViewModel, printerStateViewM
|
|||
self.timelapseViewModel = timelapseViewModel;
|
||||
self.gcodeViewModel = gcodeViewModel;
|
||||
|
||||
self._socket = io.connect();
|
||||
self._socket.on("connect", function() {
|
||||
self._socket = undefined;
|
||||
self._autoReconnecting = false;
|
||||
self._autoReconnectTrial = 0;
|
||||
self._autoReconnectTimeouts = [1, 1, 2, 3, 5, 8, 13, 20, 40, 100];
|
||||
|
||||
self.connect = function() {
|
||||
var options = {};
|
||||
if (SOCKJS_DEBUG) {
|
||||
options["debug"] = true;
|
||||
}
|
||||
|
||||
self._socket = new SockJS(SOCKJS_URI, undefined, options);
|
||||
self._socket.onopen = self._onconnect;
|
||||
self._socket.onclose = self._onclose;
|
||||
self._socket.onmessage = self._onmessage;
|
||||
}
|
||||
|
||||
self.reconnect = function() {
|
||||
delete self._socket;
|
||||
self.connect();
|
||||
}
|
||||
|
||||
self._onconnect = function() {
|
||||
self._autoReconnecting = false;
|
||||
self._autoReconnectTrial = 0;
|
||||
|
||||
if ($("#offline_overlay").is(":visible")) {
|
||||
$("#offline_overlay").hide();
|
||||
self.timelapseViewModel.requestData();
|
||||
|
@ -20,8 +44,9 @@ function DataUpdater(loginStateViewModel, connectionViewModel, printerStateViewM
|
|||
self.loginStateViewModel.requestData();
|
||||
self.gcodeFilesViewModel.requestData();
|
||||
}
|
||||
});
|
||||
self._socket.on("disconnect", function() {
|
||||
}
|
||||
|
||||
self._onclose = function() {
|
||||
$("#offline_overlay_message").html(
|
||||
"The server appears to be offline, at least I'm not getting any response from it. I'll try to reconnect " +
|
||||
"automatically <strong>over the next couple of minutes</strong>, however you are welcome to try a manual reconnect " +
|
||||
|
@ -29,45 +54,66 @@ function DataUpdater(loginStateViewModel, connectionViewModel, printerStateViewM
|
|||
);
|
||||
if (!$("#offline_overlay").is(":visible"))
|
||||
$("#offline_overlay").show();
|
||||
});
|
||||
self._socket.on("reconnect_failed", function() {
|
||||
|
||||
if (self._autoReconnectTrial < self._autoReconnectTimeouts.length) {
|
||||
var timeout = self._autoReconnectTimeouts[self._autoReconnectTrial];
|
||||
console.log("Reconnect trial #" + self._autoReconnectTrial + ", waiting " + timeout + "s");
|
||||
setTimeout(self.reconnect, timeout * 1000);
|
||||
self._autoReconnectTrial++;
|
||||
} else {
|
||||
self._onreconnectfailed();
|
||||
}
|
||||
}
|
||||
|
||||
self._onreconnectfailed = function() {
|
||||
$("#offline_overlay_message").html(
|
||||
"The server appears to be offline, at least I'm not getting any response from it. I <strong>could not reconnect automatically</strong>, " +
|
||||
"but you may try a manual reconnect using the button below."
|
||||
);
|
||||
});
|
||||
self._socket.on("history", function(data) {
|
||||
self.connectionViewModel.fromHistoryData(data);
|
||||
self.printerStateViewModel.fromHistoryData(data);
|
||||
self.temperatureViewModel.fromHistoryData(data);
|
||||
self.controlViewModel.fromHistoryData(data);
|
||||
self.terminalViewModel.fromHistoryData(data);
|
||||
self.timelapseViewModel.fromHistoryData(data);
|
||||
self.gcodeViewModel.fromHistoryData(data);
|
||||
self.gcodeFilesViewModel.fromCurrentData(data);
|
||||
});
|
||||
self._socket.on("current", function(data) {
|
||||
self.connectionViewModel.fromCurrentData(data);
|
||||
self.printerStateViewModel.fromCurrentData(data);
|
||||
self.temperatureViewModel.fromCurrentData(data);
|
||||
self.controlViewModel.fromCurrentData(data);
|
||||
self.terminalViewModel.fromCurrentData(data);
|
||||
self.timelapseViewModel.fromCurrentData(data);
|
||||
self.gcodeViewModel.fromCurrentData(data);
|
||||
self.gcodeFilesViewModel.fromCurrentData(data);
|
||||
});
|
||||
self._socket.on("updateTrigger", function(type) {
|
||||
if (type == "gcodeFiles") {
|
||||
gcodeFilesViewModel.requestData();
|
||||
} else if (type == "timelapseFiles") {
|
||||
timelapseViewModel.requestData();
|
||||
}
|
||||
});
|
||||
self._socket.on("feedbackCommandOutput", function(data) {
|
||||
self.controlViewModel.fromFeedbackCommandData(data);
|
||||
});
|
||||
|
||||
self.reconnect = function() {
|
||||
self._socket.socket.connect();
|
||||
}
|
||||
|
||||
self._onmessage = function(e) {
|
||||
for (var prop in e.data) {
|
||||
var payload = e.data[prop];
|
||||
|
||||
switch (prop) {
|
||||
case "history": {
|
||||
self.connectionViewModel.fromHistoryData(payload);
|
||||
self.printerStateViewModel.fromHistoryData(payload);
|
||||
self.temperatureViewModel.fromHistoryData(payload);
|
||||
self.controlViewModel.fromHistoryData(payload);
|
||||
self.terminalViewModel.fromHistoryData(payload);
|
||||
self.timelapseViewModel.fromHistoryData(payload);
|
||||
self.gcodeViewModel.fromHistoryData(payload);
|
||||
self.gcodeFilesViewModel.fromCurrentData(payload);
|
||||
break;
|
||||
}
|
||||
case "current": {
|
||||
self.connectionViewModel.fromCurrentData(payload);
|
||||
self.printerStateViewModel.fromCurrentData(payload);
|
||||
self.temperatureViewModel.fromCurrentData(payload);
|
||||
self.controlViewModel.fromCurrentData(payload);
|
||||
self.terminalViewModel.fromCurrentData(payload);
|
||||
self.timelapseViewModel.fromCurrentData(payload);
|
||||
self.gcodeViewModel.fromCurrentData(payload);
|
||||
self.gcodeFilesViewModel.fromCurrentData(payload);
|
||||
break;
|
||||
}
|
||||
case "updateTrigger": {
|
||||
if (payload == "gcodeFiles") {
|
||||
gcodeFilesViewModel.requestData();
|
||||
} else if (payload == "timelapseFiles") {
|
||||
timelapseViewModel.requestData();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "feedbackCommandOutput": {
|
||||
self.controlViewModel.fromFeedbackCommandData(payload);
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.connect();
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ $(function() {
|
|||
var appearanceViewModel = new AppearanceViewModel(settingsViewModel);
|
||||
var temperatureViewModel = new TemperatureViewModel(loginStateViewModel, settingsViewModel);
|
||||
var controlViewModel = new ControlViewModel(loginStateViewModel, settingsViewModel);
|
||||
var terminalViewModel = new TerminalViewModel(loginStateViewModel);
|
||||
var terminalViewModel = new TerminalViewModel(loginStateViewModel, settingsViewModel);
|
||||
var gcodeFilesViewModel = new GcodeFilesViewModel(printerStateViewModel, loginStateViewModel);
|
||||
var timelapseViewModel = new TimelapseViewModel(loginStateViewModel);
|
||||
var gcodeViewModel = new GcodeViewModel(loginStateViewModel);
|
||||
|
@ -198,6 +198,7 @@ $(function() {
|
|||
ko.applyBindings(settingsViewModel, document.getElementById("settings_dialog"));
|
||||
ko.applyBindings(navigationViewModel, document.getElementById("navbar"));
|
||||
ko.applyBindings(appearanceViewModel, document.getElementsByTagName("head")[0]);
|
||||
ko.applyBindings(loginStateViewModel, document.getElementById("drop_overlay"));
|
||||
|
||||
var timelapseElement = document.getElementById("timelapse");
|
||||
if (timelapseElement) {
|
||||
|
|
|
@ -51,6 +51,8 @@ function SettingsViewModel(loginStateViewModel, usersViewModel) {
|
|||
|
||||
self.system_actions = ko.observableArray([]);
|
||||
|
||||
self.terminalFilters = ko.observableArray([]);
|
||||
|
||||
self.addTemperatureProfile = function() {
|
||||
self.temperature_profiles.push({name: "New", extruder:0, bed:0});
|
||||
};
|
||||
|
@ -59,6 +61,14 @@ function SettingsViewModel(loginStateViewModel, usersViewModel) {
|
|||
self.temperature_profiles.remove(profile);
|
||||
};
|
||||
|
||||
self.addTerminalFilter = function() {
|
||||
self.terminalFilters.push({name: "New", regex: "(Send: M105)|(Recv: ok T:)"})
|
||||
};
|
||||
|
||||
self.removeTerminalFilter = function(filter) {
|
||||
self.terminalFilters.remove(filter);
|
||||
};
|
||||
|
||||
self.requestData = function() {
|
||||
$.ajax({
|
||||
url: AJAX_BASEURL + "settings",
|
||||
|
@ -112,6 +122,8 @@ function SettingsViewModel(loginStateViewModel, usersViewModel) {
|
|||
self.temperature_profiles(response.temperature.profiles);
|
||||
|
||||
self.system_actions(response.system.actions);
|
||||
|
||||
self.terminalFilters(response.terminalFilters);
|
||||
}
|
||||
|
||||
self.saveData = function() {
|
||||
|
@ -166,7 +178,8 @@ function SettingsViewModel(loginStateViewModel, usersViewModel) {
|
|||
},
|
||||
"system": {
|
||||
"actions": self.system_actions()
|
||||
}
|
||||
},
|
||||
"terminalFilters": self.terminalFilters()
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
function TerminalViewModel(loginStateViewModel) {
|
||||
function TerminalViewModel(loginStateViewModel, settingsViewModel) {
|
||||
var self = this;
|
||||
|
||||
self.loginState = loginStateViewModel;
|
||||
self.settings = settingsViewModel;
|
||||
|
||||
self.log = [];
|
||||
|
||||
|
@ -16,17 +17,13 @@ function TerminalViewModel(loginStateViewModel) {
|
|||
self.isLoading = ko.observable(undefined);
|
||||
|
||||
self.autoscrollEnabled = ko.observable(true);
|
||||
self.filterM105 = ko.observable(false);
|
||||
self.filterM27 = ko.observable(false);
|
||||
|
||||
self.regexM105 = /(Send: M105)|(Recv: ok T:)/;
|
||||
self.regexM27 = /(Send: M27)|(Recv: SD printing byte)/;
|
||||
self.filters = self.settings.terminalFilters;
|
||||
self.filterRegex = undefined;
|
||||
|
||||
self.filterM105.subscribe(function(newValue) {
|
||||
self.updateOutput();
|
||||
});
|
||||
|
||||
self.filterM27.subscribe(function(newValue) {
|
||||
self.activeFilters = ko.observableArray([]);
|
||||
self.activeFilters.subscribe(function(e) {
|
||||
self.updateFilterRegex();
|
||||
self.updateOutput();
|
||||
});
|
||||
|
||||
|
@ -63,15 +60,23 @@ function TerminalViewModel(loginStateViewModel) {
|
|||
self.isLoading(data.flags.loading);
|
||||
}
|
||||
|
||||
self.updateFilterRegex = function() {
|
||||
var filterRegexStr = self.activeFilters().join("|").trim();
|
||||
if (filterRegexStr == "") {
|
||||
self.filterRegex = undefined;
|
||||
} else {
|
||||
self.filterRegex = new RegExp(filterRegexStr);
|
||||
}
|
||||
console.log("Terminal filter regex: " + filterRegexStr);
|
||||
}
|
||||
|
||||
self.updateOutput = function() {
|
||||
if (!self.log)
|
||||
return;
|
||||
|
||||
var output = "";
|
||||
for (var i = 0; i < self.log.length; i++) {
|
||||
if (self.filterM105() && self.log[i].match(self.regexM105)) continue;
|
||||
if (self.filterM27() && self.log[i].match(self.regexM27)) continue;
|
||||
|
||||
if (self.filterRegex !== undefined && self.log[i].match(self.filterRegex)) continue;
|
||||
output += self.log[i] + "\n";
|
||||
}
|
||||
|
||||
|
@ -110,4 +115,5 @@ function TerminalViewModel(loginStateViewModel) {
|
|||
self.sendCommand();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -17,7 +17,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div id="drop_overlay">
|
||||
<div id="drop_overlay" data-bind="visible: isUser()">
|
||||
<div id="drop_overlay_background"></div>
|
||||
<div id="drop_overlay_wrapper">
|
||||
{% if enableSdSupport %}
|
||||
|
|
|
@ -27,8 +27,8 @@
|
|||
var CONFIG_SD_SUPPORT = {% if enableSdSupport -%} true; {% else %} false; {%- endif %}
|
||||
var CONFIG_FIRST_RUN = {% if firstRun -%} true; {% else %} false; {%- endif %}
|
||||
|
||||
var WEB_SOCKET_SWF_LOCATION = "{{ url_for('static', filename='js/socket.io/WebSocketMain.swf') }}";
|
||||
var WEB_SOCKET_DEBUG = true;
|
||||
var SOCKJS_URI = window.location.protocol.slice(0, -1) + "://" + (window.document ? window.document.domain : window.location.hostname) + ":" + window.location.port + "/sockjs";
|
||||
var SOCKJS_DEBUG = {% if debug -%} true; {% else %} false; {%- endif %}
|
||||
</script>
|
||||
<script src="{{ url_for('static', filename='js/lib/less-1.3.3.min.js') }}" type="text/javascript"></script>
|
||||
</head>
|
||||
|
@ -511,12 +511,11 @@
|
|||
<label class="checkbox">
|
||||
<input type="checkbox" id="terminal-autoscroll" data-bind="checked: autoscrollEnabled"> Autoscroll
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="terminal-filterM105" data-bind="checked: filterM105"> Suppress M105 requests/responses
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="terminal-filterM27" data-bind="checked: filterM27"> Suppress M27 requests/responses
|
||||
</label>
|
||||
<div data-bind="foreach: filters">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="attr: { value: regex }, checked: $parent.activeFilters"> <span data-bind="text: name"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="input-append" style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<input type="text" id="terminal-command" data-bind="value: command, event: { keyup: function(d,e) { handleEnter(e); } }, enable: isOperational() && loginState.isUser()">
|
||||
|
@ -619,7 +618,7 @@
|
|||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery/jquery.flot.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery/jquery.iframe-transport.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery/jquery.fileupload.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/socket.io/socket.io.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/sockjs-0.3.4.min.js') }}"></script>
|
||||
|
||||
<!-- Include OctoPrint files -->
|
||||
<!-- TODO: merge/minimize in the future -->
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<li class="active"><a href="#settings_serialConnection" data-toggle="tab">Serial Connection</a></li>
|
||||
<li><a href="#settings_printerParameters" data-toggle="tab">Printer Parameters</a></li>
|
||||
<li><a href="#settings_temperature" data-toggle="tab">Temperatures</a></li>
|
||||
<li><a href="#settings_terminalFilters" data-toggle="tab">Terminal filters</a></li>
|
||||
<li class="nav-header">Features</li>
|
||||
<li><a href="#settings_features" data-toggle="tab">Features</a></li>
|
||||
<li><a href="#settings_webcam" data-toggle="tab">Webcam</a></li>
|
||||
|
@ -263,6 +264,32 @@
|
|||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="tab-pane" id="settings_terminalFilters">
|
||||
<form class="form-horizontal">
|
||||
<div class="row-fluid">
|
||||
<div class="span4"><h4>Name</h4></div>
|
||||
<div class="span6"><h4>RegExp <small><a href="https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions">?</a></small></h4></div>
|
||||
</div>
|
||||
<div data-bind="foreach: terminalFilters">
|
||||
<div class="row-fluid" style="margin-bottom: 5px">
|
||||
<div class="span4">
|
||||
<input type="text" class="span12" data-bind="value: name">
|
||||
</div>
|
||||
<div class="span6">
|
||||
<input type="text" class="span12" data-bind="value: regex">
|
||||
</div>
|
||||
<div class="span2">
|
||||
<button title="Remove Filter" class="btn btn-danger" data-bind="click: $parent.removeTerminalFilter"><i class="icon-trash"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="offset10 span2">
|
||||
<button title="Add Filter" class="btn btn-primary" data-bind="click: addTerminalFilter"><i class="icon-plus"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="tab-pane" id="settings_appearance">
|
||||
<form class="form-horizontal">
|
||||
<div class="control-group">
|
||||
|
|
|
@ -51,6 +51,7 @@ class Timelapse(object):
|
|||
eventManager().subscribe("PrintStarted", self.onPrintStarted)
|
||||
eventManager().subscribe("PrintFailed", self.onPrintDone)
|
||||
eventManager().subscribe("PrintDone", self.onPrintDone)
|
||||
eventManager().subscribe("PrintResumed", self.onPrintResumed)
|
||||
for (event, callback) in self.eventSubscriptions():
|
||||
eventManager().subscribe(event, callback)
|
||||
|
||||
|
@ -62,6 +63,7 @@ class Timelapse(object):
|
|||
eventManager().unsubscribe("PrintStarted", self.onPrintStarted)
|
||||
eventManager().unsubscribe("PrintFailed", self.onPrintDone)
|
||||
eventManager().unsubscribe("PrintDone", self.onPrintDone)
|
||||
eventManager().unsubscribe("PrintResumed", self.onPrintResumed)
|
||||
for (event, callback) in self.eventSubscriptions():
|
||||
eventManager().unsubscribe(event, callback)
|
||||
|
||||
|
@ -77,12 +79,20 @@ class Timelapse(object):
|
|||
"""
|
||||
self.stopTimelapse()
|
||||
|
||||
def onPrintResumed(self, event, payload):
|
||||
"""
|
||||
Override this to perform additional actions upon the pausing of a print job.
|
||||
"""
|
||||
if not self._inTimelapse:
|
||||
self.startTimelapse(payload)
|
||||
|
||||
def eventSubscriptions(self):
|
||||
"""
|
||||
Override this method to subscribe to additional events by returning an array of (event, callback) tuples.
|
||||
|
||||
Events that are already subscribed:
|
||||
* PrintStarted - self.onPrintStarted
|
||||
* PrintResumed - self.onPrintResumed
|
||||
* PrintFailed - self.onPrintDone
|
||||
* PrintDone - self.onPrintDone
|
||||
"""
|
||||
|
|
|
@ -386,6 +386,7 @@ class MachineCom(object):
|
|||
self.sendCommand("M24")
|
||||
else:
|
||||
self._sendNext()
|
||||
eventManager().fire("PrintResumed", self._currentFile.getFilename())
|
||||
if pause and self.isPrinting():
|
||||
self._changeState(self.STATE_PAUSED)
|
||||
if self.isSdFileSelected():
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
flask==0.9
|
||||
werkzeug==0.8.3
|
||||
tornado==3.0.2
|
||||
tornadio2==0.0.4
|
||||
sockjs-tornado>=1.0.0
|
||||
PyYAML==3.10
|
||||
Flask-Login==0.2.2
|
||||
Flask-Principal==0.3.5
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
flask==0.9
|
||||
werkzeug==0.8.3
|
||||
tornado==3.0.2
|
||||
tornadio2==0.0.4
|
||||
sockjs-tornado>=1.0.0
|
||||
PyYAML==3.10
|
||||
Flask-Login==0.2.2
|
||||
Flask-Principal==0.3.5
|
||||
|
|
Loading…
Reference in New Issue