Saltar a contenido

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:

aasPath = aas_submodel + "/" + aas_collection + "/" + aas_property

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 aasPath correcto 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 aasPath que 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.

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

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 abierto
  • SYS-ADR-009 — la forma canónica del aasPath cuando no hay colección