525 lines
13 KiB
Python
Executable File
525 lines
13 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import base64
|
|
import json
|
|
import sys
|
|
import urllib.error
|
|
import urllib.request
|
|
from pathlib import Path
|
|
|
|
|
|
BASE_URL = "http://127.0.0.1:3000"
|
|
DASHBOARD_UID = "ucepsa-edge-oee-machine"
|
|
|
|
|
|
def read_env(path=".env"):
|
|
env = {}
|
|
p = Path(path)
|
|
if not p.exists():
|
|
return env
|
|
|
|
for raw in p.read_text().splitlines():
|
|
line = raw.strip()
|
|
if not line or line.startswith("#") or "=" not in line:
|
|
continue
|
|
key, value = line.split("=", 1)
|
|
env[key.strip()] = value.strip().strip('"').strip("'")
|
|
return env
|
|
|
|
|
|
env = read_env()
|
|
user = env.get("GRAFANA_ADMIN_USER", "admin")
|
|
password = env.get("GRAFANA_ADMIN_PASSWORD")
|
|
|
|
if not password:
|
|
print("ERROR: no encuentro GRAFANA_ADMIN_PASSWORD en .env", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
token = base64.b64encode(f"{user}:{password}".encode()).decode()
|
|
|
|
|
|
def api(method, path, payload=None):
|
|
data = None
|
|
headers = {
|
|
"Authorization": f"Basic {token}",
|
|
"Content-Type": "application/json",
|
|
"Accept": "application/json",
|
|
}
|
|
|
|
if payload is not None:
|
|
data = json.dumps(payload).encode()
|
|
|
|
req = urllib.request.Request(
|
|
BASE_URL + path,
|
|
data=data,
|
|
headers=headers,
|
|
method=method,
|
|
)
|
|
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=20) as resp:
|
|
body = resp.read().decode()
|
|
return json.loads(body) if body else {}
|
|
except urllib.error.HTTPError as e:
|
|
print("HTTP ERROR:", e.code, e.read().decode(), file=sys.stderr)
|
|
raise
|
|
|
|
|
|
datasources = api("GET", "/api/datasources")
|
|
|
|
print("Datasources encontrados:")
|
|
for d in datasources:
|
|
print(f"- name={d.get('name')} type={d.get('type')} uid={d.get('uid')} url={d.get('url')} database={d.get('database')}")
|
|
|
|
pg = next(
|
|
(
|
|
d for d in datasources
|
|
if d.get("type") in ("postgres", "grafana-postgresql-datasource")
|
|
or "postgres" in (d.get("type") or "").lower()
|
|
or "postgres" in (d.get("name") or "").lower()
|
|
or "mesavault" in (d.get("name") or "").lower()
|
|
),
|
|
None,
|
|
)
|
|
|
|
if not pg:
|
|
print("ERROR: no encuentro datasource PostgreSQL/MESAVAULT en Grafana", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
DS = {
|
|
"type": "postgres",
|
|
"uid": pg["uid"],
|
|
}
|
|
|
|
|
|
def target(sql, ref="A", fmt="table"):
|
|
return {
|
|
"refId": ref,
|
|
"datasource": DS,
|
|
"format": fmt,
|
|
"rawQuery": True,
|
|
"rawSql": sql,
|
|
"editorMode": "code",
|
|
}
|
|
|
|
|
|
def stat_panel(panel_id, title, x, y, w, h, sql, unit="none", mappings=None):
|
|
field_defaults = {
|
|
"unit": unit,
|
|
"thresholds": {
|
|
"mode": "absolute",
|
|
"steps": [
|
|
{"color": "green", "value": None},
|
|
],
|
|
},
|
|
}
|
|
|
|
if mappings:
|
|
field_defaults["mappings"] = mappings
|
|
|
|
return {
|
|
"id": panel_id,
|
|
"type": "stat",
|
|
"title": title,
|
|
"datasource": DS,
|
|
"gridPos": {"x": x, "y": y, "w": w, "h": h},
|
|
"targets": [target(sql)],
|
|
"options": {
|
|
"reduceOptions": {
|
|
"values": False,
|
|
"calcs": ["lastNotNull"],
|
|
"fields": "",
|
|
},
|
|
"orientation": "auto",
|
|
"textMode": "auto",
|
|
"colorMode": "value",
|
|
"graphMode": "none",
|
|
"justifyMode": "auto",
|
|
},
|
|
"fieldConfig": {
|
|
"defaults": field_defaults,
|
|
"overrides": [],
|
|
},
|
|
}
|
|
|
|
|
|
def timeseries_panel(panel_id, title, x, y, w, h, sql, unit="none"):
|
|
return {
|
|
"id": panel_id,
|
|
"type": "timeseries",
|
|
"title": title,
|
|
"datasource": DS,
|
|
"gridPos": {"x": x, "y": y, "w": w, "h": h},
|
|
"targets": [target(sql, fmt="time_series")],
|
|
"fieldConfig": {
|
|
"defaults": {
|
|
"unit": unit,
|
|
"custom": {
|
|
"drawStyle": "line",
|
|
"lineInterpolation": "linear",
|
|
"lineWidth": 1,
|
|
"fillOpacity": 5,
|
|
"showPoints": "never",
|
|
},
|
|
},
|
|
"overrides": [],
|
|
},
|
|
"options": {
|
|
"legend": {
|
|
"displayMode": "list",
|
|
"placement": "bottom",
|
|
"showLegend": True,
|
|
},
|
|
"tooltip": {
|
|
"mode": "multi",
|
|
"sort": "none",
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
def table_panel(panel_id, title, x, y, w, h, sql):
|
|
return {
|
|
"id": panel_id,
|
|
"type": "table",
|
|
"title": title,
|
|
"datasource": DS,
|
|
"gridPos": {"x": x, "y": y, "w": w, "h": h},
|
|
"targets": [target(sql)],
|
|
"options": {
|
|
"showHeader": True,
|
|
"cellHeight": "sm",
|
|
},
|
|
"fieldConfig": {
|
|
"defaults": {},
|
|
"overrides": [],
|
|
},
|
|
}
|
|
|
|
|
|
comm_mapping = [
|
|
{
|
|
"type": "value",
|
|
"options": {
|
|
"1": {"text": "OK", "color": "green"},
|
|
"0": {"text": "NO OK", "color": "red"},
|
|
},
|
|
}
|
|
]
|
|
|
|
run_mapping = [
|
|
{
|
|
"type": "value",
|
|
"options": {
|
|
"1": {"text": "EN MARCHA", "color": "green"},
|
|
"0": {"text": "PARADA", "color": "yellow"},
|
|
},
|
|
}
|
|
]
|
|
|
|
dashboard = {
|
|
"id": None,
|
|
"uid": DASHBOARD_UID,
|
|
"title": "MESAVAULT Edge-OEE — Máquina",
|
|
"tags": ["mesavault", "ucepsa", "edge-oee"],
|
|
"timezone": "browser",
|
|
"schemaVersion": 39,
|
|
"version": 1,
|
|
"refresh": "5s",
|
|
"time": {
|
|
"from": "now-30m",
|
|
"to": "now",
|
|
},
|
|
"templating": {
|
|
"list": [
|
|
{
|
|
"name": "machine_id",
|
|
"type": "query",
|
|
"label": "Máquina",
|
|
"datasource": DS,
|
|
"refresh": 1,
|
|
"sort": 1,
|
|
"multi": False,
|
|
"includeAll": False,
|
|
"query": "SELECT machine_id FROM mv_reports_ucepsa_demo.machine_state_latest_multi ORDER BY machine_id",
|
|
"current": {
|
|
"text": "CORT-00",
|
|
"value": "CORT-00",
|
|
"selected": True,
|
|
},
|
|
}
|
|
]
|
|
},
|
|
"panels": [
|
|
stat_panel(
|
|
1,
|
|
"Comunicación",
|
|
0,
|
|
0,
|
|
6,
|
|
4,
|
|
"""
|
|
SELECT
|
|
CASE
|
|
WHEN age_s < 30 AND comm_ok = true AND COALESCE(sample_valid, true) = true THEN 1
|
|
ELSE 0
|
|
END AS value
|
|
FROM mv_reports_ucepsa_demo.machine_state_latest_multi
|
|
WHERE machine_id = '$machine_id'
|
|
""",
|
|
mappings=comm_mapping,
|
|
),
|
|
stat_panel(
|
|
2,
|
|
"Edad último dato",
|
|
6,
|
|
0,
|
|
6,
|
|
4,
|
|
"""
|
|
SELECT age_s AS value
|
|
FROM mv_reports_ucepsa_demo.machine_state_latest_multi
|
|
WHERE machine_id = '$machine_id'
|
|
""",
|
|
unit="s",
|
|
),
|
|
stat_panel(
|
|
3,
|
|
"Estado máquina",
|
|
12,
|
|
0,
|
|
6,
|
|
4,
|
|
"""
|
|
SELECT
|
|
CASE WHEN machine_running = true THEN 1 ELSE 0 END AS value
|
|
FROM mv_reports_ucepsa_demo.machine_state_latest_multi
|
|
WHERE machine_id = '$machine_id'
|
|
""",
|
|
mappings=run_mapping,
|
|
),
|
|
stat_panel(
|
|
4,
|
|
"Ciclos/min",
|
|
18,
|
|
0,
|
|
6,
|
|
4,
|
|
"""
|
|
SELECT cycle_rate_ppm AS value
|
|
FROM mv_reports_ucepsa_demo.machine_state_latest_multi
|
|
WHERE machine_id = '$machine_id'
|
|
""",
|
|
unit="cpm",
|
|
),
|
|
stat_panel(
|
|
5,
|
|
"Potencia total",
|
|
0,
|
|
4,
|
|
6,
|
|
4,
|
|
"""
|
|
SELECT power_total_kw AS value
|
|
FROM mv_reports_ucepsa_demo.machine_state_latest_multi
|
|
WHERE machine_id = '$machine_id'
|
|
""",
|
|
unit="kwatt",
|
|
),
|
|
stat_panel(
|
|
6,
|
|
"Energía importada",
|
|
6,
|
|
4,
|
|
6,
|
|
4,
|
|
"""
|
|
SELECT import_kwh AS value
|
|
FROM mv_reports_ucepsa_demo.machine_state_latest_multi
|
|
WHERE machine_id = '$machine_id'
|
|
""",
|
|
unit="kwatth",
|
|
),
|
|
stat_panel(
|
|
7,
|
|
"Frecuencia",
|
|
12,
|
|
4,
|
|
6,
|
|
4,
|
|
"""
|
|
SELECT frequency_hz AS value
|
|
FROM mv_reports_ucepsa_demo.machine_state_latest_multi
|
|
WHERE machine_id = '$machine_id'
|
|
""",
|
|
unit="hertz",
|
|
),
|
|
stat_panel(
|
|
8,
|
|
"Pulsos acumulados",
|
|
18,
|
|
4,
|
|
6,
|
|
4,
|
|
"""
|
|
SELECT cycle_pulse_count AS value
|
|
FROM mv_reports_ucepsa_demo.machine_state_latest_multi
|
|
WHERE machine_id = '$machine_id'
|
|
""",
|
|
unit="none",
|
|
),
|
|
timeseries_panel(
|
|
9,
|
|
"Potencia por fase",
|
|
0,
|
|
8,
|
|
12,
|
|
8,
|
|
"""
|
|
SELECT
|
|
ts AS "time",
|
|
active_power_l1_w AS "L1 W",
|
|
active_power_l2_w AS "L2 W",
|
|
active_power_l3_w AS "L3 W",
|
|
active_power_w AS "Total W"
|
|
FROM mv_reports_ucepsa_demo.energy_3phase_timeseries
|
|
WHERE $__timeFilter(ts)
|
|
AND site = 'ucepsa_onpremise'
|
|
AND machine_id = '$machine_id'
|
|
ORDER BY ts
|
|
""",
|
|
unit="watt",
|
|
),
|
|
timeseries_panel(
|
|
10,
|
|
"Tensiones L1/L2/L3",
|
|
12,
|
|
8,
|
|
12,
|
|
8,
|
|
"""
|
|
SELECT
|
|
ts AS "time",
|
|
voltage_l1_v AS "L1 V",
|
|
voltage_l2_v AS "L2 V",
|
|
voltage_l3_v AS "L3 V"
|
|
FROM mv_reports_ucepsa_demo.energy_3phase_timeseries
|
|
WHERE $__timeFilter(ts)
|
|
AND site = 'ucepsa_onpremise'
|
|
AND machine_id = '$machine_id'
|
|
ORDER BY ts
|
|
""",
|
|
unit="volt",
|
|
),
|
|
timeseries_panel(
|
|
11,
|
|
"Corrientes L1/L2/L3",
|
|
0,
|
|
16,
|
|
12,
|
|
8,
|
|
"""
|
|
SELECT
|
|
ts AS "time",
|
|
current_l1_a AS "L1 A",
|
|
current_l2_a AS "L2 A",
|
|
current_l3_a AS "L3 A"
|
|
FROM mv_reports_ucepsa_demo.energy_3phase_timeseries
|
|
WHERE $__timeFilter(ts)
|
|
AND site = 'ucepsa_onpremise'
|
|
AND machine_id = '$machine_id'
|
|
ORDER BY ts
|
|
""",
|
|
unit="amp",
|
|
),
|
|
table_panel(
|
|
12,
|
|
"Última lectura trifásica",
|
|
12,
|
|
16,
|
|
12,
|
|
8,
|
|
"""
|
|
SELECT
|
|
machine_id,
|
|
round(age_s::numeric, 1) AS age_s,
|
|
comm_ok,
|
|
sample_valid,
|
|
energy_map,
|
|
machine_running,
|
|
cycle_input_state,
|
|
cycle_pulse_count,
|
|
wise_cycle_counter_raw,
|
|
round(voltage_l1_v::numeric, 2) AS v_l1,
|
|
round(voltage_l2_v::numeric, 2) AS v_l2,
|
|
round(voltage_l3_v::numeric, 2) AS v_l3,
|
|
round(current_l1_a::numeric, 3) AS i_l1,
|
|
round(current_l2_a::numeric, 3) AS i_l2,
|
|
round(current_l3_a::numeric, 3) AS i_l3,
|
|
round(power_total_kw::numeric, 4) AS kw_total,
|
|
digital_inputs
|
|
FROM mv_reports_ucepsa_demo.machine_state_latest_multi
|
|
WHERE machine_id = '$machine_id'
|
|
""",
|
|
),
|
|
table_panel(
|
|
13,
|
|
"Odoo vs máquina",
|
|
0,
|
|
24,
|
|
12,
|
|
7,
|
|
"""
|
|
SELECT
|
|
machine_id,
|
|
order_ref,
|
|
product_name,
|
|
odoo_state,
|
|
machine_running,
|
|
telemetry_age_s,
|
|
cycle_rate_ppm,
|
|
status_check,
|
|
diagnostic_message
|
|
FROM mv_reports_ucepsa_demo.odoo_vs_machine_demo
|
|
WHERE machine_id = '$machine_id'
|
|
""",
|
|
),
|
|
table_panel(
|
|
14,
|
|
"Loss Intelligence",
|
|
12,
|
|
24,
|
|
12,
|
|
7,
|
|
"""
|
|
SELECT
|
|
priority_rank,
|
|
priority_score,
|
|
priority_level,
|
|
machine_id,
|
|
order_ref,
|
|
loss_type,
|
|
severity,
|
|
duration_min,
|
|
cost_eur,
|
|
status,
|
|
description,
|
|
recommended_action
|
|
FROM mv_reports_ucepsa_demo.loss_priority_board
|
|
WHERE machine_id = '$machine_id'
|
|
ORDER BY priority_rank
|
|
""",
|
|
),
|
|
],
|
|
}
|
|
|
|
payload = {
|
|
"dashboard": dashboard,
|
|
"overwrite": True,
|
|
"message": "Create MESAVAULT Edge-OEE machine dashboard with machine selector",
|
|
}
|
|
|
|
result = api("POST", "/api/dashboards/db", payload)
|
|
|
|
print("Dashboard creado/actualizado:")
|
|
print(BASE_URL + result.get("url", f"/d/{DASHBOARD_UID}"))
|