fix(ucepsa): auto-refresh operator console safely
This commit is contained in:
parent
d525443de9
commit
69f8416808
@ -1,10 +1,10 @@
|
|||||||
|
import json
|
||||||
import os
|
import os
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import psycopg2
|
import psycopg2
|
||||||
import psycopg2.extras
|
import psycopg2.extras
|
||||||
from flask import Flask, redirect, render_template_string, request, url_for
|
from flask import Flask, Response, redirect, render_template_string, request, url_for
|
||||||
|
|
||||||
|
|
||||||
def getenv(name, default=None, required=False):
|
def getenv(name, default=None, required=False):
|
||||||
value = os.getenv(name, default)
|
value = os.getenv(name, default)
|
||||||
@ -26,6 +26,7 @@ APP_TITLE = getenv("APP_TITLE", "MESAVAULT Edge-OEE - Consola de paros UCEPSA")
|
|||||||
|
|
||||||
DEFAULT_MACHINE_ID = getenv("DEFAULT_MACHINE_ID", "CORT-01")
|
DEFAULT_MACHINE_ID = getenv("DEFAULT_MACHINE_ID", "CORT-01")
|
||||||
DEFAULT_ASSET = getenv("DEFAULT_ASSET", "revpi_oee_node_01")
|
DEFAULT_ASSET = getenv("DEFAULT_ASSET", "revpi_oee_node_01")
|
||||||
|
AUTO_REFRESH_SECONDS = int(getenv("AUTO_REFRESH_SECONDS", "5"))
|
||||||
|
|
||||||
ALLOWED_MACHINES = [
|
ALLOWED_MACHINES = [
|
||||||
item.strip()
|
item.strip()
|
||||||
@ -150,7 +151,12 @@ HTML = """
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>{{ app_title }}</title>
|
<title>{{ app_title }}</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="manifest" href="{{ url_for('manifest', machine_id=selected_machine) }}">
|
||||||
|
<meta name="theme-color" content="#0f172a">
|
||||||
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-title" content="MESAVAULT {{ selected_machine }}">
|
||||||
|
<link rel="icon" href="{{ url_for('pwa_icon') }}" type="image/svg+xml">
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
--bg: #0f172a;
|
--bg: #0f172a;
|
||||||
@ -512,6 +518,54 @@ HTML = """
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
<script>
|
||||||
|
if ("serviceWorker" in navigator) {
|
||||||
|
window.addEventListener("load", function () {
|
||||||
|
navigator.serviceWorker.register("/sw.js").catch(function (err) {
|
||||||
|
console.log("Service worker no registrado:", err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
const refreshSeconds = Number("{{ auto_refresh_seconds }}");
|
||||||
|
const refreshMs = Math.max(refreshSeconds, 2) * 1000;
|
||||||
|
let formDirty = false;
|
||||||
|
|
||||||
|
document.addEventListener("input", function (event) {
|
||||||
|
if (event.target.closest("form")) {
|
||||||
|
formDirty = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("change", function (event) {
|
||||||
|
if (event.target.closest("form")) {
|
||||||
|
formDirty = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function userIsEditing() {
|
||||||
|
const active = document.activeElement;
|
||||||
|
if (!active) return false;
|
||||||
|
|
||||||
|
const tag = active.tagName;
|
||||||
|
return tag === "INPUT" ||
|
||||||
|
tag === "SELECT" ||
|
||||||
|
tag === "TEXTAREA" ||
|
||||||
|
active.isContentEditable;
|
||||||
|
}
|
||||||
|
|
||||||
|
setInterval(function () {
|
||||||
|
if (document.hidden) return;
|
||||||
|
if (formDirty || userIsEditing()) return;
|
||||||
|
|
||||||
|
window.location.reload();
|
||||||
|
}, refreshMs);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
"""
|
"""
|
||||||
@ -534,6 +588,7 @@ def index():
|
|||||||
allowed_machines=ALLOWED_MACHINES,
|
allowed_machines=ALLOWED_MACHINES,
|
||||||
default_machine=selected_machine,
|
default_machine=selected_machine,
|
||||||
default_asset=DEFAULT_ASSET,
|
default_asset=DEFAULT_ASSET,
|
||||||
|
auto_refresh_seconds=AUTO_REFRESH_SECONDS,
|
||||||
loaded_at=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
loaded_at=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
fmt_dt=fmt_dt,
|
fmt_dt=fmt_dt,
|
||||||
fmt_num=fmt_num,
|
fmt_num=fmt_num,
|
||||||
@ -574,6 +629,73 @@ def health():
|
|||||||
return {"status": "error", "error": str(exc)}, 500
|
return {"status": "error", "error": str(exc)}, 500
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/manifest.webmanifest", methods=["GET"])
|
||||||
|
def manifest():
|
||||||
|
selected_machine = resolve_machine_id(request.args.get("machine_id"))
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"name": f"MESAVAULT Edge-OEE {selected_machine}",
|
||||||
|
"short_name": selected_machine,
|
||||||
|
"description": "Consola de paros MESAVAULT Edge-OEE para UCEPSA",
|
||||||
|
"start_url": url_for("index", machine_id=selected_machine),
|
||||||
|
"scope": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"orientation": "landscape",
|
||||||
|
"background_color": "#0f172a",
|
||||||
|
"theme_color": "#0f172a",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": url_for("pwa_icon"),
|
||||||
|
"sizes": "any",
|
||||||
|
"type": "image/svg+xml",
|
||||||
|
"purpose": "any maskable",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response(
|
||||||
|
json.dumps(payload, ensure_ascii=False),
|
||||||
|
mimetype="application/manifest+json",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/sw.js", methods=["GET"])
|
||||||
|
def service_worker():
|
||||||
|
js = """
|
||||||
|
self.addEventListener("install", function (event) {
|
||||||
|
self.skipWaiting();
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener("activate", function (event) {
|
||||||
|
event.waitUntil(self.clients.claim());
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener("fetch", function (event) {
|
||||||
|
event.respondWith(fetch(event.request));
|
||||||
|
});
|
||||||
|
"""
|
||||||
|
return Response(
|
||||||
|
js,
|
||||||
|
mimetype="application/javascript",
|
||||||
|
headers={"Cache-Control": "no-cache"},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/pwa-icon.svg", methods=["GET"])
|
||||||
|
def pwa_icon():
|
||||||
|
svg = """
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||||
|
<rect width="512" height="512" rx="96" fill="#0f172a"/>
|
||||||
|
<path d="M96 328h320v48H96z" fill="#60a5fa"/>
|
||||||
|
<path d="M136 136h240v128H136z" fill="#2563eb"/>
|
||||||
|
<path d="M168 176h176v48H168z" fill="#dbeafe"/>
|
||||||
|
<circle cx="160" cy="392" r="28" fill="#22c55e"/>
|
||||||
|
<circle cx="352" cy="392" r="28" fill="#22c55e"/>
|
||||||
|
<text x="256" y="304" text-anchor="middle" font-size="64" font-family="Arial" font-weight="700" fill="#ffffff">OEE</text>
|
||||||
|
</svg>
|
||||||
|
"""
|
||||||
|
return Response(svg, mimetype="image/svg+xml")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
port = int(getenv("APP_PORT", "8080"))
|
port = int(getenv("APP_PORT", "8080"))
|
||||||
app.run(host="0.0.0.0", port=port)
|
app.run(host="0.0.0.0", port=port)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user