diff --git a/ucepsa/edge-oee-demo/services/operator-stop-console/app.py b/ucepsa/edge-oee-demo/services/operator-stop-console/app.py
index beb4503..efbec26 100644
--- a/ucepsa/edge-oee-demo/services/operator-stop-console/app.py
+++ b/ucepsa/edge-oee-demo/services/operator-stop-console/app.py
@@ -22,10 +22,27 @@ PG_CONFIG = {
"connect_timeout": 5,
}
-APP_TITLE = getenv("APP_TITLE", "MESAVAULT Edge-OEE - Consola de paros")
+APP_TITLE = getenv("APP_TITLE", "MESAVAULT Edge-OEE - Consola de paros UCEPSA")
+
DEFAULT_MACHINE_ID = getenv("DEFAULT_MACHINE_ID", "CORT-01")
DEFAULT_ASSET = getenv("DEFAULT_ASSET", "revpi_oee_node_01")
+ALLOWED_MACHINES = [
+ item.strip()
+ for item in getenv("ALLOWED_MACHINES", DEFAULT_MACHINE_ID).split(",")
+ if item.strip()
+]
+
+
+def resolve_machine_id(raw_value):
+ machine_id = (raw_value or DEFAULT_MACHINE_ID).strip()
+
+ if machine_id not in ALLOWED_MACHINES:
+ return DEFAULT_MACHINE_ID
+
+ return machine_id
+
+
app = Flask(__name__)
@@ -33,7 +50,7 @@ def get_conn():
return psycopg2.connect(**PG_CONFIG)
-def fetch_pending_stops():
+def fetch_pending_stops(machine_id):
sql = """
SELECT
id,
@@ -54,13 +71,14 @@ def fetch_pending_stops():
cost_eur
FROM mv_hot.edge_oee_stops_recent
WHERE classification_status = 'PENDING'
+ AND machine_id = %(machine_id)s
ORDER BY started_at DESC
LIMIT 50;
"""
with get_conn() as conn:
with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
- cur.execute(sql)
+ cur.execute(sql, {"machine_id": machine_id})
return cur.fetchall()
@@ -81,7 +99,7 @@ def fetch_causes():
return cur.fetchall()
-def classify_stop(stop_id, cause_code, operator_note):
+def classify_stop(stop_id, machine_id, cause_code, operator_note):
sql = """
UPDATE mv_hot.edge_oee_machine_stops
SET
@@ -90,6 +108,7 @@ def classify_stop(stop_id, cause_code, operator_note):
classification_status = 'CLASSIFIED',
updated_at = now()
WHERE id = %(stop_id)s
+ AND machine_id = %(machine_id)s
AND classification_status = 'PENDING';
"""
@@ -99,300 +118,400 @@ def classify_stop(stop_id, cause_code, operator_note):
sql,
{
"stop_id": stop_id,
+ "machine_id": machine_id,
"cause_code": cause_code,
"operator_note": operator_note,
},
)
+ return cur.rowcount
-PAGE = """
+def fmt_dt(value):
+ if not value:
+ return ""
+ if isinstance(value, datetime):
+ return value.strftime("%Y-%m-%d %H:%M:%S")
+ return str(value)
+
+
+def fmt_num(value, decimals=2):
+ if value is None:
+ return "0"
+ try:
+ return f"{float(value):.{decimals}f}"
+ except Exception:
+ return str(value)
+
+
+HTML = """
{{ app_title }}
-
+
-
-
-
-
-
-
Paros pendientes
-
{{ stops|length }}
+
-
-
-
Máquina monitorizada
-
{{ default_machine }}
-
{{ default_asset }}
-
-
-
-
-
- {% if stops|length == 0 %}
-
- No hay paros pendientes de clasificar.
-
- {% else %}
-
-
-
- | ID |
- Máquina |
- Inicio |
- Fin |
- Estado |
- Duración |
- Coste |
- Causa |
- Nota |
- Guardar |
-
-
-
- {% for stop in stops %}
-
-
-
+
+
-
- {% endif %}
-
+
+
+
+
+
Paros pendientes
+
{{ pending_count }}
+
+
+
+
Máquina monitorizada
+
{{ selected_machine }}
+
{{ default_asset }}
+
+
+
+
+
+ {% if stops %}
+
+ {% else %}
+
+ No hay paros pendientes para {{ selected_machine }}.
+
+ {% endif %}
+
+
+
"""
@@ -400,30 +519,45 @@ PAGE = """
@app.route("/", methods=["GET"])
def index():
- stops = fetch_pending_stops()
+ selected_machine = resolve_machine_id(request.args.get("machine_id"))
+
+ stops = fetch_pending_stops(selected_machine)
causes = fetch_causes()
return render_template_string(
- PAGE,
+ HTML,
app_title=APP_TITLE,
stops=stops,
causes=causes,
- now=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
- default_machine=DEFAULT_MACHINE_ID,
+ pending_count=len(stops),
+ selected_machine=selected_machine,
+ allowed_machines=ALLOWED_MACHINES,
+ default_machine=selected_machine,
default_asset=DEFAULT_ASSET,
+ loaded_at=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
+ fmt_dt=fmt_dt,
+ fmt_num=fmt_num,
)
-@app.route("/classify", methods=["POST"])
-def classify():
- stop_id = request.form.get("stop_id")
- cause_code = request.form.get("cause_code")
- operator_note = request.form.get("operator_note", "").strip() or None
+@app.route("/classify/", methods=["POST"])
+def classify(stop_id):
+ selected_machine = resolve_machine_id(
+ request.form.get("machine_id") or request.args.get("machine_id")
+ )
- if stop_id and cause_code:
- classify_stop(stop_id, cause_code, operator_note)
+ cause_code = (request.form.get("cause_code") or "").strip()
+ operator_note = (request.form.get("operator_note") or "").strip()
- return redirect(url_for("index"))
+ if cause_code:
+ classify_stop(
+ stop_id=stop_id,
+ machine_id=selected_machine,
+ cause_code=cause_code,
+ operator_note=operator_note,
+ )
+
+ return redirect(url_for("index", machine_id=selected_machine))
@app.route("/health", methods=["GET"])
@@ -432,6 +566,14 @@ def health():
with get_conn() as conn:
with conn.cursor() as cur:
cur.execute("SELECT 1;")
+ cur.fetchone()
+
return {"status": "ok"}, 200
+
except Exception as exc:
return {"status": "error", "error": str(exc)}, 500
+
+
+if __name__ == "__main__":
+ port = int(getenv("APP_PORT", "8080"))
+ app.run(host="0.0.0.0", port=port)