feat(ucepsa): add Shop Floor context health shadow
This commit is contained in:
parent
d1f1b9a6c8
commit
e5cb98e349
@ -0,0 +1,76 @@
|
|||||||
|
# Consultas Grafana — Shop Floor Context Health v0.3.5
|
||||||
|
|
||||||
|
## Estado por máquina
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
machine_id AS "Máquina",
|
||||||
|
live_context_status AS "Estado",
|
||||||
|
live_severity AS "Severidad",
|
||||||
|
published_production_order AS "Orden publicada",
|
||||||
|
odoo_workorder_id AS "Workorder sesión",
|
||||||
|
COALESCE(
|
||||||
|
operator_name,
|
||||||
|
operator_candidate_name
|
||||||
|
) AS "Operario",
|
||||||
|
sync_health_status AS "Sync",
|
||||||
|
recommended_action AS "Acción"
|
||||||
|
FROM
|
||||||
|
mv_reports_ucepsa_prod
|
||||||
|
.li_shopfloor_machine_live_context_v1
|
||||||
|
ORDER BY machine_id;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Incidencias
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
observed_at AS "Hora",
|
||||||
|
severity AS "Severidad",
|
||||||
|
machine_id AS "Máquina",
|
||||||
|
incident_code AS "Código",
|
||||||
|
production_order AS "Orden",
|
||||||
|
odoo_workorder_id AS "Workorder",
|
||||||
|
operator_name AS "Operario",
|
||||||
|
detail AS "Situación",
|
||||||
|
recommended_action AS "Acción"
|
||||||
|
FROM
|
||||||
|
mv_reports_ucepsa_prod
|
||||||
|
.li_shopfloor_context_incidents_v1
|
||||||
|
ORDER BY
|
||||||
|
CASE severity
|
||||||
|
WHEN 'CRITICAL' THEN 1
|
||||||
|
WHEN 'WARNING' THEN 2
|
||||||
|
ELSE 3
|
||||||
|
END,
|
||||||
|
machine_id;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sesiones abiertas
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
machine_id AS "Máquina",
|
||||||
|
production_order AS "Orden",
|
||||||
|
odoo_workorder_id AS "Workorder",
|
||||||
|
operator_name AS "Operario",
|
||||||
|
session_started_at AS "Inicio",
|
||||||
|
session_age_min AS "Minutos abierta",
|
||||||
|
machine_running AS "Máquina en marcha",
|
||||||
|
open_stop_duration_min AS "Paro abierto min",
|
||||||
|
watch_status AS "Estado",
|
||||||
|
watch_severity AS "Severidad"
|
||||||
|
FROM
|
||||||
|
mv_reports_ucepsa_prod
|
||||||
|
.li_shopfloor_open_session_watch_v1
|
||||||
|
ORDER BY machine_id;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Resumen
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT *
|
||||||
|
FROM
|
||||||
|
mv_reports_ucepsa_prod
|
||||||
|
.li_shopfloor_context_health_summary_v1;
|
||||||
|
```
|
||||||
@ -0,0 +1,211 @@
|
|||||||
|
# UCEPSA — Shop Floor Context Health v0.3.5
|
||||||
|
|
||||||
|
## Objetivo
|
||||||
|
|
||||||
|
Supervisar en vivo cinco capas:
|
||||||
|
|
||||||
|
```text
|
||||||
|
máquina física
|
||||||
|
orden Odoo publicada
|
||||||
|
sesión Odoo Shop Floor
|
||||||
|
sincronizador
|
||||||
|
calidad temporal
|
||||||
|
```
|
||||||
|
|
||||||
|
Esta versión no cambia la atribución de paros v0.3.4, no modifica Odoo y no
|
||||||
|
alimenta el Ledger oficial.
|
||||||
|
|
||||||
|
## Nuevos objetos
|
||||||
|
|
||||||
|
### Política
|
||||||
|
|
||||||
|
```text
|
||||||
|
mv_loss_intelligence.odoo_shopfloor_health_policies
|
||||||
|
```
|
||||||
|
|
||||||
|
Umbrales iniciales:
|
||||||
|
|
||||||
|
```text
|
||||||
|
intervalo esperado 15 s
|
||||||
|
DELAYED a partir de 45 s
|
||||||
|
STALE a partir de 120 s
|
||||||
|
sesión OPEN obsoleta 12 h
|
||||||
|
tolerancia fecha futura 5 min
|
||||||
|
```
|
||||||
|
|
||||||
|
### Vistas
|
||||||
|
|
||||||
|
```text
|
||||||
|
li_shopfloor_sync_health_v1
|
||||||
|
li_shopfloor_session_data_quality_v1
|
||||||
|
li_context_identity_registry_v1
|
||||||
|
li_shopfloor_machine_live_context_v1
|
||||||
|
li_shopfloor_open_session_watch_v1
|
||||||
|
li_shopfloor_context_incidents_v1
|
||||||
|
li_shopfloor_context_health_summary_v1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Estados vivos principales
|
||||||
|
|
||||||
|
### Producción coherente
|
||||||
|
|
||||||
|
```text
|
||||||
|
máquina en marcha
|
||||||
|
+ orden publicada
|
||||||
|
+ una sesión OPEN del mismo workorder
|
||||||
|
→ HEALTHY_OPEN_SESSION
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parada dentro de una orden
|
||||||
|
|
||||||
|
```text
|
||||||
|
máquina parada
|
||||||
|
+ orden publicada
|
||||||
|
+ sesión OPEN
|
||||||
|
→ TEMPORARY_STOP_INSIDE_SESSION
|
||||||
|
```
|
||||||
|
|
||||||
|
La parada física no cierra la sesión.
|
||||||
|
|
||||||
|
### Producción sin Shop Floor
|
||||||
|
|
||||||
|
```text
|
||||||
|
máquina en marcha
|
||||||
|
+ sin orden
|
||||||
|
+ sin sesión
|
||||||
|
→ RUNNING_WITHOUT_SHOPFLOOR_SESSION
|
||||||
|
```
|
||||||
|
|
||||||
|
Acción: confirmar sistema anterior, prueba, material residual o inicio omitido.
|
||||||
|
|
||||||
|
### Orden sin sesión
|
||||||
|
|
||||||
|
```text
|
||||||
|
orden publicada
|
||||||
|
+ ninguna sesión OPEN
|
||||||
|
→ RUNNING_WITH_ORDER_NO_SHOPFLOOR_SESSION
|
||||||
|
u ORDER_WITHOUT_SHOPFLOOR_SESSION
|
||||||
|
```
|
||||||
|
|
||||||
|
### Conflicto
|
||||||
|
|
||||||
|
```text
|
||||||
|
más de una sesión OPEN
|
||||||
|
o workorder publicado distinto del workorder de la sesión
|
||||||
|
→ CRITICAL
|
||||||
|
```
|
||||||
|
|
||||||
|
## Identidades con namespace
|
||||||
|
|
||||||
|
Nunca se cruza un identificador numérico desnudo.
|
||||||
|
|
||||||
|
Ejemplos:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ODOO_SHOPFLOOR:WORKORDER:340
|
||||||
|
PRODUCTION_CONTEXT:LEGACY_ERP:1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Incidencias
|
||||||
|
|
||||||
|
La vista de incidencias expone:
|
||||||
|
|
||||||
|
```text
|
||||||
|
severidad
|
||||||
|
código
|
||||||
|
máquina
|
||||||
|
context_key
|
||||||
|
orden
|
||||||
|
workorder
|
||||||
|
operario
|
||||||
|
detalle
|
||||||
|
acción recomendada
|
||||||
|
```
|
||||||
|
|
||||||
|
No crea tickets ni escribe en Odoo.
|
||||||
|
|
||||||
|
## Despliegue
|
||||||
|
|
||||||
|
No es necesario detener servicios.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
FILE=/srv/mesavault/40-clients/ucepsa/edge-oee-demo/sql/versions/108_ucepsa_shopfloor_context_health_v035.sql
|
||||||
|
|
||||||
|
docker exec -i mv_ucepsa_postgres_hot sh -lc \
|
||||||
|
'psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB"' \
|
||||||
|
< "$FILE"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Validación
|
||||||
|
|
||||||
|
```bash
|
||||||
|
VALIDATION=/srv/mesavault/40-clients/ucepsa/edge-oee-demo/ops/validate_shopfloor_context_health_v035.sql
|
||||||
|
|
||||||
|
docker exec -i mv_ucepsa_postgres_hot sh -lc \
|
||||||
|
'psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB"' \
|
||||||
|
< "$VALIDATION"
|
||||||
|
```
|
||||||
|
|
||||||
|
Valores estructurales obligatorios:
|
||||||
|
|
||||||
|
```text
|
||||||
|
live_machine_rows = 3
|
||||||
|
distinct_machine_rows = 3
|
||||||
|
pending_has_time_count = 0
|
||||||
|
open_missing_start_count = 0
|
||||||
|
open_has_end_count = 0
|
||||||
|
closed_missing_start_count = 0
|
||||||
|
closed_missing_end_count = 0
|
||||||
|
invalid_interval_count = 0
|
||||||
|
context_key_collision_count = 0
|
||||||
|
temporary_stop_wrong_severity = 0
|
||||||
|
healthy_session_wrong_severity = 0
|
||||||
|
idle_wrong_severity = 0
|
||||||
|
official_live_rows = 0
|
||||||
|
official_incident_rows = 0
|
||||||
|
official_operator_queue_rows = 0
|
||||||
|
```
|
||||||
|
|
||||||
|
`multiple_open_session_rows` debe ser cero en operación normal. Si no lo es, la
|
||||||
|
vista lo elevará como incidencia crítica.
|
||||||
|
|
||||||
|
## Lectura esperada
|
||||||
|
|
||||||
|
Mientras el workorder 340 siga abierto:
|
||||||
|
|
||||||
|
```text
|
||||||
|
CORT-02
|
||||||
|
context_key = ODOO_SHOPFLOOR:WORKORDER:340
|
||||||
|
operario = Esteban
|
||||||
|
estado = HEALTHY_OPEN_SESSION
|
||||||
|
```
|
||||||
|
|
||||||
|
Durante una parada física:
|
||||||
|
|
||||||
|
```text
|
||||||
|
estado = TEMPORARY_STOP_INSIDE_SESSION
|
||||||
|
severidad = INFO
|
||||||
|
```
|
||||||
|
|
||||||
|
CORT-00 solo aparecerá como advertencia cuando realmente esté en marcha sin
|
||||||
|
orden ni sesión.
|
||||||
|
|
||||||
|
## Grafana
|
||||||
|
|
||||||
|
La vista recomendada para una tabla operativa es:
|
||||||
|
|
||||||
|
```text
|
||||||
|
li_shopfloor_machine_live_context_v1
|
||||||
|
```
|
||||||
|
|
||||||
|
Para una lista de excepciones:
|
||||||
|
|
||||||
|
```text
|
||||||
|
li_shopfloor_context_incidents_v1
|
||||||
|
```
|
||||||
|
|
||||||
|
Para un indicador único:
|
||||||
|
|
||||||
|
```text
|
||||||
|
li_shopfloor_context_health_summary_v1
|
||||||
|
```
|
||||||
@ -0,0 +1,222 @@
|
|||||||
|
\pset pager off
|
||||||
|
|
||||||
|
\echo '=== 1. Objetos v0.3.5 ==='
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
to_regclass(
|
||||||
|
'mv_loss_intelligence.odoo_shopfloor_health_policies'
|
||||||
|
) AS policy_table,
|
||||||
|
to_regclass(
|
||||||
|
'mv_reports_ucepsa_prod.li_shopfloor_sync_health_v1'
|
||||||
|
) AS sync_health_view,
|
||||||
|
to_regclass(
|
||||||
|
'mv_reports_ucepsa_prod.li_shopfloor_session_data_quality_v1'
|
||||||
|
) AS session_dq_view,
|
||||||
|
to_regclass(
|
||||||
|
'mv_reports_ucepsa_prod.li_context_identity_registry_v1'
|
||||||
|
) AS identity_registry_view,
|
||||||
|
to_regclass(
|
||||||
|
'mv_reports_ucepsa_prod.li_shopfloor_machine_live_context_v1'
|
||||||
|
) AS live_context_view,
|
||||||
|
to_regclass(
|
||||||
|
'mv_reports_ucepsa_prod.li_shopfloor_open_session_watch_v1'
|
||||||
|
) AS open_watch_view,
|
||||||
|
to_regclass(
|
||||||
|
'mv_reports_ucepsa_prod.li_shopfloor_context_incidents_v1'
|
||||||
|
) AS incidents_view,
|
||||||
|
to_regclass(
|
||||||
|
'mv_reports_ucepsa_prod.li_shopfloor_context_health_summary_v1'
|
||||||
|
) AS summary_view;
|
||||||
|
|
||||||
|
\echo '=== 2. Política activa ==='
|
||||||
|
|
||||||
|
SELECT *
|
||||||
|
FROM
|
||||||
|
mv_loss_intelligence
|
||||||
|
.odoo_shopfloor_health_policies
|
||||||
|
WHERE tenant = 'ucepsa'
|
||||||
|
AND site = 'ucepsa_onpremise'
|
||||||
|
AND policy_code =
|
||||||
|
'CUTTER_SHOPFLOOR_HEALTH_V1';
|
||||||
|
|
||||||
|
\echo '=== 3. Salud del sincronizador ==='
|
||||||
|
|
||||||
|
SELECT *
|
||||||
|
FROM
|
||||||
|
mv_reports_ucepsa_prod
|
||||||
|
.li_shopfloor_sync_health_v1;
|
||||||
|
|
||||||
|
\echo '=== 4. Tres máquinas y una fila por máquina ==='
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
count(*) AS live_machine_rows,
|
||||||
|
count(
|
||||||
|
DISTINCT machine_id
|
||||||
|
) AS distinct_machine_rows
|
||||||
|
FROM
|
||||||
|
mv_reports_ucepsa_prod
|
||||||
|
.li_shopfloor_machine_live_context_v1;
|
||||||
|
|
||||||
|
\echo '=== 5. Estado vivo por máquina ==='
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
machine_id,
|
||||||
|
telemetry_fresh,
|
||||||
|
machine_running,
|
||||||
|
published_order_present,
|
||||||
|
published_order_coherent,
|
||||||
|
published_production_order,
|
||||||
|
published_workorder_id,
|
||||||
|
open_session_count,
|
||||||
|
context_key,
|
||||||
|
session_production_order,
|
||||||
|
odoo_workorder_id,
|
||||||
|
operator_name,
|
||||||
|
operator_candidate_name,
|
||||||
|
sync_health_status,
|
||||||
|
live_context_status,
|
||||||
|
live_severity,
|
||||||
|
recommended_action,
|
||||||
|
official_eligible
|
||||||
|
FROM
|
||||||
|
mv_reports_ucepsa_prod
|
||||||
|
.li_shopfloor_machine_live_context_v1
|
||||||
|
ORDER BY machine_id;
|
||||||
|
|
||||||
|
\echo '=== 6. Invariantes de sesiones ==='
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
count(*) FILTER (
|
||||||
|
WHERE pending_has_time
|
||||||
|
) AS pending_has_time_count,
|
||||||
|
count(*) FILTER (
|
||||||
|
WHERE open_missing_start
|
||||||
|
) AS open_missing_start_count,
|
||||||
|
count(*) FILTER (
|
||||||
|
WHERE open_has_end
|
||||||
|
) AS open_has_end_count,
|
||||||
|
count(*) FILTER (
|
||||||
|
WHERE closed_missing_start
|
||||||
|
) AS closed_missing_start_count,
|
||||||
|
count(*) FILTER (
|
||||||
|
WHERE closed_missing_end
|
||||||
|
) AS closed_missing_end_count,
|
||||||
|
count(*) FILTER (
|
||||||
|
WHERE invalid_interval
|
||||||
|
) AS invalid_interval_count,
|
||||||
|
count(*) FILTER (
|
||||||
|
WHERE multiple_open_sessions
|
||||||
|
) AS multiple_open_session_rows
|
||||||
|
FROM
|
||||||
|
mv_reports_ucepsa_prod
|
||||||
|
.li_shopfloor_session_data_quality_v1;
|
||||||
|
|
||||||
|
\echo '=== 7. Identidades con namespace sin colisiones ==='
|
||||||
|
|
||||||
|
WITH duplicated AS (
|
||||||
|
SELECT
|
||||||
|
tenant,
|
||||||
|
site,
|
||||||
|
context_key,
|
||||||
|
count(*) AS n
|
||||||
|
FROM
|
||||||
|
mv_reports_ucepsa_prod
|
||||||
|
.li_context_identity_registry_v1
|
||||||
|
GROUP BY
|
||||||
|
tenant,
|
||||||
|
site,
|
||||||
|
context_key
|
||||||
|
HAVING count(*) > 1
|
||||||
|
)
|
||||||
|
SELECT count(*) AS context_key_collision_count
|
||||||
|
FROM duplicated;
|
||||||
|
|
||||||
|
\echo '=== 8. Sesiones OPEN bajo vigilancia ==='
|
||||||
|
|
||||||
|
SELECT *
|
||||||
|
FROM
|
||||||
|
mv_reports_ucepsa_prod
|
||||||
|
.li_shopfloor_open_session_watch_v1
|
||||||
|
ORDER BY machine_id, session_started_at;
|
||||||
|
|
||||||
|
\echo '=== 9. Incidencias vivas ==='
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
severity,
|
||||||
|
incident_scope,
|
||||||
|
incident_code,
|
||||||
|
machine_id,
|
||||||
|
context_key,
|
||||||
|
production_order,
|
||||||
|
odoo_workorder_id,
|
||||||
|
operator_name,
|
||||||
|
detail,
|
||||||
|
recommended_action,
|
||||||
|
official_eligible
|
||||||
|
FROM
|
||||||
|
mv_reports_ucepsa_prod
|
||||||
|
.li_shopfloor_context_incidents_v1
|
||||||
|
ORDER BY
|
||||||
|
CASE severity
|
||||||
|
WHEN 'CRITICAL' THEN 1
|
||||||
|
WHEN 'WARNING' THEN 2
|
||||||
|
ELSE 3
|
||||||
|
END,
|
||||||
|
machine_id NULLS FIRST,
|
||||||
|
incident_code;
|
||||||
|
|
||||||
|
\echo '=== 10. Coherencia de severidades ==='
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
count(*) FILTER (
|
||||||
|
WHERE live_context_status =
|
||||||
|
'TEMPORARY_STOP_INSIDE_SESSION'
|
||||||
|
AND live_severity <> 'INFO'
|
||||||
|
) AS temporary_stop_wrong_severity,
|
||||||
|
count(*) FILTER (
|
||||||
|
WHERE live_context_status =
|
||||||
|
'HEALTHY_OPEN_SESSION'
|
||||||
|
AND live_severity <> 'OK'
|
||||||
|
) AS healthy_session_wrong_severity,
|
||||||
|
count(*) FILTER (
|
||||||
|
WHERE live_context_status =
|
||||||
|
'IDLE_NO_ACTIVE_CONTEXT'
|
||||||
|
AND live_severity <> 'OK'
|
||||||
|
) AS idle_wrong_severity,
|
||||||
|
count(*) FILTER (
|
||||||
|
WHERE official_eligible
|
||||||
|
) AS official_live_rows
|
||||||
|
FROM
|
||||||
|
mv_reports_ucepsa_prod
|
||||||
|
.li_shopfloor_machine_live_context_v1;
|
||||||
|
|
||||||
|
\echo '=== 11. Resumen ejecutivo ==='
|
||||||
|
|
||||||
|
SELECT *
|
||||||
|
FROM
|
||||||
|
mv_reports_ucepsa_prod
|
||||||
|
.li_shopfloor_context_health_summary_v1;
|
||||||
|
|
||||||
|
\echo '=== 12. Nada oficial ==='
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
(
|
||||||
|
SELECT count(*)
|
||||||
|
FROM
|
||||||
|
mv_reports_ucepsa_prod
|
||||||
|
.li_shopfloor_context_incidents_v1
|
||||||
|
WHERE official_eligible
|
||||||
|
) AS official_incident_rows,
|
||||||
|
(
|
||||||
|
SELECT count(*)
|
||||||
|
FROM
|
||||||
|
mv_reports_ucepsa_prod
|
||||||
|
.li_context_identity_registry_v1
|
||||||
|
WHERE official_eligible
|
||||||
|
) AS official_identity_rows,
|
||||||
|
(
|
||||||
|
SELECT count(*)
|
||||||
|
FROM
|
||||||
|
mv_reports_ucepsa_prod
|
||||||
|
.li_operator_stop_queue_v2
|
||||||
|
) AS official_operator_queue_rows;
|
||||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user