Catálogo de variables (aasPath)¶
Responde: ¿quién manda sobre el nombre de una variable, cómo se agrega una, y por qué renombrarla rompe cosas en silencio?
El aasPath es el contrato transversal más frágil del sistema: atraviesa los cuatro
repositorios, no tiene dueño declarado, no tiene versión y nadie lo valida. Es la decisión
SYS-ADR-004, y ésta es su página operativa.
El contrato en una línea¶
El aasPath identifica una variable en todo el sistema aguas arriba de InfluxDB. Lo compone el
Edge concatenando tres segmentos con barras:
Y el Cloud lo consulta con igualdad exacta. No normaliza, no tolera variantes, no hace prefijos ni distingue mayúsculas.
Un carácter de diferencia devuelve una serie vacía, sin error.
La fuente de verdad¶
Hay tres artefactos que parecen catálogos. Solo uno lo es.
| Artefacto | ¿Sirve de fuente de verdad? |
|---|---|
src/historian/aas_mapping.yaml |
Sí, es el canónico. El empaquetado lo monta de solo lectura dentro del contenedor del historian y del Edge, y el Edge lo parsea directamente |
runtime/catalog.json |
No. Es derivado del anterior, y ningún componente lo lee |
| El catálogo observado que devuelve el Cloud | No. Se calcula sobre lo que ya está almacenado, así que solo refleja lo que alguna vez fluyó |
Por qué el catálogo observado no sirve para validar¶
Es el único de los tres que se consulta por API, así que es el que parece autoritativo. El
Cloud lo calcula pidiéndole a la base los valores distintos de payload.aasPath para una
máquina, y eso tiene dos consecuencias:
- Una variable nueva no aparece hasta que fluye el primer dato. Validar contra él
rechazaría un
aasPathcorrecto recién agregado. - Una variable renombrada sigue apareciendo con el nombre viejo, para siempre, porque los
documentos históricos no se reescriben. Validar contra él aceptaría un
aasPathque ya no se escribe.
Sirve para lo que fue hecho —que un humano vea qué llegó de una máquina— y es peligroso como criterio de validación.
Estado actual del contrato¶
El catálogo canónico contrastado contra el registry de widgets:
aasPath que el Edge puede emitir |
18 |
| Widgets definidos en el registry | 32 |
| Widgets que consumen una métrica real | 14 |
Literales aasPath en el registry |
14, de los cuales 10 distintos |
| Literales que no coinciden con el catálogo | 0 |
aasPath del catálogo que ningún widget consume |
10 |
Los tres literales de alarma coinciden con el catálogo y aun así sus widgets no muestran datos: están deshabilitados a mano en el frontend, y las mediciones guardadas bajo la forma de tres segmentos son invisibles para una consulta por la canónica. Ver estado de aplicación.
Cómo agregar una variable¶
Cinco pasos, en tres repositorios. El orden importa.
1. Declarar el tag Modbus — repo del historian¶
En src/historian/tags.yaml: nombre, dirección de registro, tipo y orden de bytes.
2. Declarar el mapeo semántico — repo del historian¶
En src/historian/aas_mapping.yaml, con el mismo name del paso anterior. Acá se define el
aasPath que va a existir, más el tipo de valor, la unidad y la clase.
Si los dos nombres no coinciden, el dato se descarta sin error visible
El historian busca la entrada de mapeo por el nombre del tag. Si no la encuentra, registra
mapping.missing_for_tag en su log y no escribe el punto. El tag queda leído del PLC y
tirado a la basura.
Las dos listas tienen 18 entradas y coinciden, pero nada lo obliga: es otra validación
que falta, y como los dos archivos están en el mismo repo es más barata que la del
aasPath.
3. Reiniciar los dos contenedores de la Pi¶
No alcanza con reconstruir. El catálogo está montado como archivo, y ni el historian ni el Edge lo releen: los dos lo leen solo al arrancar.
Reconstruir con el compose no recrea un contenedor cuando lo único que cambió es un archivo montado, así que hay que reiniciarlos explícitamente. Como los dos montan el mismo archivo, el reinicio los deja a ambos en la misma versión del catálogo.
4. Esperar a que fluya el primer dato¶
Nada en la nube conoce la variable nueva hasta que llega su primera medición. No hay registro
previo ni declaración: el aasPath "existe" cuando aparece un documento con él.
5. Consumirla desde un widget — repo del frontend¶
Agregar el literal aasPath en src/lib/widget-registry.ts, con su agregación.
Las agregaciones que el Cloud acepta son ocho: avg, sum, min, max, last, count,
delta y raw. Hay restricciones de combinación —por ejemplo, last y delta no valen
cuando la consulta pide series por intervalo o agrupación— y la fuente de verdad de esas reglas
es el repositorio del Cloud, no esta página.
Por qué renombrar es un cambio incompatible¶
Renombrar un submodelo, una colección o una propiedad no es una operación de refactor. Rompe tres cosas a la vez, y ninguna avisa:
| Qué se rompe | Cómo se manifiesta |
|---|---|
| Los widgets que la consumen | Serie vacía, sin error. El widget se ve como una máquina detenida |
| La continuidad histórica | Los datos viejos quedan bajo el nombre anterior. Una consulta con el nombre nuevo no los ve, y no hay migración que los reescriba |
| La identidad de los eventos | El aasPath entra en el cálculo del eventId, así que el mismo dato reenviado después del renombre produce un identificador distinto y no se deduplica contra el anterior |
La consecuencia práctica: un renombre es una migración de datos, no un cambio de nombre. Si hay que hacerlo, la decisión merece su propia ADR y un plan explícito para lo ya almacenado.
La validación del catálogo¶
El contrato del aasPath atraviesa cuatro repositorios y nada lo obliga a coincidir, así que lo
que lo sostiene es un control en CI: comparar cada literal aasPath del registry de widgets
contra el catálogo canónico, y fallar si alguno no existe.
Ese control existe y corre en el CI del frontend. Deriva la regla de composición de un solo
lugar —scripts/lib/aas-path.mjs, con test propio— y la aplica sobre el YAML canónico, no
sobre el JSON derivado. Hoy pasa: 14 aasPath (10 distintos) existen en el catálogo canónico (18
propiedades).
El job todavía no bloquea un PR, así que un literal huérfano entra igual. Dejarlo
bloqueante es el paso 3 de SYS-ADR-009 y lo
único que falta de esa decisión: nada lo espera.
Por qué la regla se deriva del productor¶
Que la regla salga de un solo lugar no es prolijidad: es lo que hace que el control valga algo. Un validador que compone las rutas del catálogo por su cuenta compara al dashboard contra su propia convención, no contra la del productor — compara al dashboard consigo mismo, y pasa en verde sobre literales que no coinciden con nada.
Por eso la regla vive en scripts/lib/aas-path.mjs, con test, y de ahí la toma el validador.
Las dos validaciones que siguen faltando¶
| Qué | Por qué es barata |
|---|---|
Que todo tag de tags.yaml tenga entrada en aas_mapping.yaml |
Los dos archivos viven en el mismo repositorio. Sin esto, agregar una variable al PLC y olvidarse del catálogo la descarta en silencio |
Que el value_type declarado coincida con lo que el historian escribe |
Un script sobre las 18 entradas alcanza |
El validador del registry lee el catálogo fijado a un tag del repositorio del historian, que
es de un autor externo. Existe un solo tag, v0.3-historian-semantic, de abril: alcanza para
que el job corra, pero significa que el catálogo que valida puede quedar atrás del que corre en
la Pi.
Lo que falta decidir¶
| Pregunta | Por qué importa |
|---|---|
| ¿Quién es el dueño del catálogo? | Vive en el repo de una tesis de un autor externo al equipo. Nadie del equipo tiene autoridad declarada sobre él |
| ¿Cómo se versiona? | Hoy se identifica por el hash del archivo. Eso sirve para detectar que cambió, no para saber si un cambio es compatible. El validador lo fija a un tag del historian, y hoy ese repo tiene un solo tag |
¿Qué pasa con los 10 aasPath que nadie consume? |
Se están almacenando mediciones que ningún widget muestra. Puede ser correcto —datos para después— o desperdicio |
Dónde está implementado¶
- Catálogo canónico y lista de tags:
src/historian/aas_mapping.yamlysrc/historian/tags.yaml - Descarte silencioso de un tag sin mapeo:
src/historian/main.py - Composición del
aasPath, identificación del catálogo por hash y lectura solo al arrancar:internal/domain/aas/catalog.goyinternal/app/catalog/service.godel Edge - Igualdad exacta y catálogo observado calculado sobre lo almacenado:
internal/repo/mongo/metrics/repository.go - Agregaciones válidas y reglas de combinación:
internal/domain/metrics/metrics.goyinternal/domain/metrics/validate.go - Literales y definiciones de widgets, y la regla de composición del validador:
src/lib/widget-registry.tsyscripts/lib/aas-path.mjsdel frontend - Montaje compartido del catálogo y trampa del reinicio:
docker-compose.ymlydocs/pi.md
Relacionado¶
- Flujo de datos — el recorrido completo de un valor, y las cuatro identidades que tiene por el camino
SYS-ADR-004— la decisión y su defecto abiertoSYS-ADR-009— la forma canónica delaasPathcuando no hay colección