Controlador, GlobalDatastore y archivos

Esta página describe lo que se sale del esquema « módulos → propiedades → elementos » de las páginas anteriores: los datos del controlador, el módulo central GlobalDatastore y el sistema de archivos del servidor. Un cliente que se limite a mostrar los módulos no los necesita; un cliente que quiera ofrecer lo que propone una página de parámetros globales (configuraciones, drivers INDI, carga del servidor, equipamiento, imágenes) debe tratarlos.

Los datos del controlador (controllerdata)

El controlador es el componente del servidor que carga los módulos y gestiona el servidor INDI embebido. Expone sus propios datos en el dump, bajo la clave controllerdata (ver d), y luego los mantiene al día con mensajes uc.

ClaveContenido
availableconfsConfiguraciones de módulos guardadas: objeto nombre de configuración → lista de {modulelabel, moduletype, profilename}
currentconfNombre de la configuración actual
bannerBanner del servidor (texto libre, opción --banner)
gitVersión del servidor: Gitdate, Githash, Gitmessage, Gittag
librariesBibliotecas de módulos disponibles, un objeto de metadatos por biblioteca — las claves están prefijadas con libost (libostfocus, libostdummy…); quitar el prefijo antes de pasarlo a ML. Releídas por MR
profilesPerfiles disponibles: objeto nombre de biblioteca transformado → lista de nombres de perfil — ver la convención de claves más abajo
indiserver"N" si el servidor INDI embebido está desactivado (--indiserver); en caso contrario "U" una vez confirmado en ejecución, "D" una vez que no lo está (fallo de arranque, caída o detención)
indidriversCatálogo de drivers INDI instalados: lista de {binary, family, label, mdpd}
indiactivedriversDrivers INDI actualmente en ejecución
indiconfigurationsConfiguraciones de drivers INDI guardadas: objeto nombre de configuración → lista de etiquetas de driver
systemwatcherCarga del servidor, ver más abajo
files, foldersCopia de d.files.files y d.files.folders, ver Sistema de archivos
Claves de profiles: ni el nombre de biblioteca tal cual, ni infos.template

profiles está indexado por el nombre de biblioteca del módulo (la misma cadena que se pasa como lib a ML, es decir, la clave en controllerdata.libraries sin el prefijo libost) — pero transformada: primera letra en mayúscula, el resto en minúsculas ("focus" → "Focus", no una simple capitalización — un nombre de biblioteca con otra mayúscula quedaría igualmente aplanado, por ejemplo un hipotético "myFocus" → "Myfocus"). Esta clave no es ni el nombre/etiqueta de la instancia del módulo, ni necesariamente idéntica a infos.template (suelen coincidir en la práctica, pero ambos se definen de forma independiente y el protocolo no garantiza nada al respecto).

Esta convención no está documentada en ningún otro sitio y el backend no la impone — se dedujo a partir de la propia lógica de búsqueda de Rooster. Dos clientes independientes construidos únicamente a partir de esta documentación tropezaron con ella (un selector de perfiles que se renderizaba vacío en silencio). Si necesitas buscar los perfiles disponibles de un módulo, reproduce exactamente esta transformación sobre el nombre de biblioteca en lugar de adivinar.

uc: una clave por mensaje, valor reemplazado por completo

Un mensaje uc lleva una sola clave de controllerdata, y su valor reemplaza por completo al anterior (sin fusión). Un cliente que solo lee la primera clave del payload (como hace Rooster) es por tanto correcto mientras el servidor envíe una sola clave por mensaje, que es lo que ocurre hoy.

systemwatcher: carga del servidor

El servidor mide su propia carga a intervalos regulares y la difunde a todos los clientes. El intervalo se ajusta con la opción --systemwatchinterval (segundos, 10 por defecto, 0 lo desactiva). El mensaje es un uc ordinario, y el último valor también está presente en controllerdata.systemwatcher del dump.

{
  "uc-update controller data": {
    "systemwatcher": {
      "cpu": { "load_1m": 10.42, "load_5m": 9.79, "load_15m": 4.51 },
      "ram": { "used_mb": 12194, "total_mb": 31941 },
      "disks": [
        { "device": "/dev/nvme0n1p2", "mountpoint": "/", "used_gb": 789.4, "total_gb": 915.3 }
      ],
      "network": [
        { "iface": "enp4s0", "rx_kbps": 16.27, "tx_kbps": 448.8 }
      ],
      "temperatures": [
        { "type": "Tctl", "temp_c": 75.375 }
      ]
    }
  }
}
CampoSignificado
cpu.load_1m / load_5m / load_15mCarga media del sistema (como /proc/loadavg), no un porcentaje de uso: 1.0 = un núcleo saturado de media
ram.used_mb / total_mbMemoria utilizada (total − disponible) y total, en MB
disks[]Un elemento por dispositivo de almacenamiento montado (/dev/*, excepto loop): device, mountpoint, used_gb, total_gb
network[]Un elemento por interfaz (excepto lo): caudales rx_kbps / tx_kbps medidos entre dos mensajes; la primera medida de una interfaz vale 0
temperatures[]Sensores de hardware del procesador: type, temp_c. La lista puede estar vacía según la máquina (solo se leen los sensores de tipo Intel coretemp y AMD k10temp)
No suponer que los arrays están rellenos

disks, network y sobre todo temperatures pueden estar vacíos. Un cliente debe ocultar la sección correspondiente en lugar de mostrar un array vacío. El protocolo no da el número de núcleos del servidor: un indicador de CPU no puede por tanto ser un verdadero porcentaje, hay que mostrar el valor como una carga.

Configuraciones y ciclo de vida de los módulos

Una configuración es un conjunto con nombre de módulos (biblioteca, etiqueta, perfil) que el servidor puede recargar de una vez. La lista está en controllerdata.availableconfs, la configuración activa en controllerdata.currentconf.

AcciónComandoDetalle
Cargar una configuraciónCLCarga todos los módulos de la configuración
Guardar la configuración actualCSCon el nombre indicado
Cargar un móduloMLBiblioteca, etiqueta, perfil opcional
Descargar un móduloMKPor nombre de módulo

Los módulos efectivamente cargados son los del dump (d.m); aparecen y desaparecen mediante aa y dm. La configuración nunca contiene GlobalDatastore (ver más abajo), que siempre está presente.

Una configuración incluye silenciosamente los drivers INDI activos

CS también guarda los drivers INDI actualmente en ejecución además de la lista de módulos, y CL los reinicia — pero esto es invisible en controllerdata.availableconfs, cuya forma solo describe módulos ({modulelabel, moduletype, profilename}), nunca drivers. Un cliente que construye un editor de configuraciones limitado a la lista de módulos no muestra el panorama completo de lo que ocurrirá realmente al hacer CL.

Sin operación de borrado

No existe ningún comando para eliminar una configuración guardada — CL y CS son las dos únicas operaciones. Una configuración que un cliente ya no quiere permanece indefinidamente en controllerdata.availableconfs (solo puede sobrescribirse con el mismo nombre mediante otro CS).

Servidor INDI y drivers

A menos que controllerdata.indiserver valga "N", el servidor gestiona una instancia INDI embebida. Los comandos YA/YZ/YL/YR/YS la inician, la detienen y gestionan los drivers; controllerdata.indidrivers da el catálogo disponible y controllerdata.indiactivedrivers lo que está en marcha. binary es el valor que se pasa como driver en YL, YR e YS.

controllerdata.indidrivers y controllerdata.indiactivedrivers son ambas listas de objetos {binary, family, label} (indidrivers tiene además mdpd, ver la tabla de arriba).

Un conjunto completo de drivers guardado puede iniciarse de una sola vez con YK, por nombre, desde controllerdata.indiconfigurations — guardado por CS junto con una configuración de módulos (ver la advertencia anterior), pero que puede cargarse de forma independiente, sin pasar por CL.

YA/YZ/YL/YR/YS/YK no tienen confirmación dedicada

Ninguno de estos comandos genera un mensaje de confirmación propio. El éxito o fallo debe deducirse indirectamente: YL/YR/YS/YK van seguidos de una actualización uc de indiactivedrivers que refleja el cambio solicitado (pero esto solo confirma que el servidor lo intentó, no que el driver INDI subyacente realmente arrancó), mientras que YA/YZ no producen ningún uc — solo mensajes l (log), pensados para un humano, no para ser analizados programáticamente por un cliente. controllerdata.indiserver sí sigue en vivo el propio estado de arriba/abajo del servidor embebido (ver la tabla anterior), pero eso es un efecto secundario de YA/YZ/una caída, no una confirmación directa del comando que lo provocó.

El módulo GlobalDatastore

GlobalDatastore es un módulo como los demás en el dump (d.m.GlobalDatastore, con propiedades, elementos, cuadrículas, perfiles), pero su papel es central:

  • lo crea el servidor antes que todos los demás módulos y está siempre presente: MK sobre él se rechaza, y nunca se incluye en una configuración;
  • lleva el referencial compartido del equipamiento, que consultan todos los demás módulos: ópticas, ubicaciones, equipamientos (conjuntos de dispositivos) y servidores INDI.

Sus cuadrículas sirven de fuente a listas de opciones (LOV) del mismo nombre, presentes en d.lovs (optics, locations, equipments, servers) y reconstruidas con cada modificación de la cuadrícula. Los demás módulos las usan así: elegir una óptica rellena la focal y el diámetro, elegir una ubicación rellena latitud, longitud y altitud, elegir un conjunto de equipamiento resuelve los dispositivos (cámara, montura, focuser, rueda de filtros, cámara de guiado, GPS, cúpula, meteo) y el servidor INDI al que conectarse.

Propiedades expuestas:

PropiedadFunción
devicesConjunto de equipamiento y GPS seleccionados (equipments, gps)
devicesactionsBotones: conectar/desconectar los dispositivos, cargar las configuraciones (condevs, discondevs, loadconfs)
serverHost y puerto del servidor INDI (host, port)
serveractionsBotones: conectar/desconectar el servidor INDI (conserv, disconserv)
startupCasillas de inicio automático (indiatstart, devatstart, confsatstart)
equipmentsCuadrícula de los conjuntos de equipamiento
locationsCuadrícula de las ubicaciones (iconos refresh = leer el GPS, add = añadir la fila actual)
opticsCuadrícula de las ópticas
serversCuadrícula de los servidores INDI

Todas sus propiedades se manipulan con los mensajes estándar (SV, SA, GC…, I3/I4 sobre locations); un cliente genérico las muestra como cualquier otro módulo. Rooster las presenta en una pestaña dedicada de su página de parámetros, reteniendo las propiedades que tienen perfil (hasprofile) y agrupándolas por level1/level2.

Sistema de archivos

El servidor publica la lista de los archivos producidos (imágenes, secuencias, archivos) en la carpeta --webroot, sin que el cliente tenga que consultarla. Este mecanismo es independiente de los módulos.

En el dump: d.files

"files": {
  "folders": ["", "/Allsky", "/Sequencer/gam Cyg/LIGHT/Luminance"],
  "files": ["/image1.jpeg", "/rapport.txt"],
  "selectedfolder": "/home/gilles/ostmedia"
}
CampoContenido
foldersTodas las subcarpetas del webroot, recursivamente, rutas relativas al webroot que empiezan por /. La raíz es la cadena vacía "". Sin ordenar.
filesLos archivos de la sola carpeta seleccionada (no recursivo), rutas relativas al webroot que empiezan por /. Todos los archivos, sin filtro de extensión.
selectedfolderLa carpeta seleccionada, como ruta absoluta del sistema de archivos del servidor (y no relativa).

No hay paginación ni límite de tamaño: en un webroot muy lleno, la lista puede ser larga.

Elegir la carpeta: FS

FS designa la carpeta a vigilar y de la que listar los archivos. El servidor solo vigila el contenido de una carpeta a la vez; el árbol de carpetas (folders) se sigue de forma independiente, ver más abajo.

La carpeta seleccionada es global en el servidor

No hay carpeta por cliente: un FS cambia la lista de archivos que ven todos los clientes conectados. Un cliente no debe usarlo para una simple navegación « local » si es posible que haya otros clientes conectados.

La ruta se valida: el destino debe existir y quedar dentro del webroot (sin escapar vía ../). Una solicitud inválida se rechaza en el servidor (se registra, sin cambio de estado) — no devuelve silenciosamente una lista vacía.

Un FS correcto reemplaza la lista de archivos en bloque: el cliente recibe un único mensaje uc files con la lista completa de la nueva carpeta, no un flujo de af/df — seleccionar una carpeta no es una mutación del sistema de archivos, no se añadió ni eliminó realmente nada. No hay ningún mensaje que anuncie el nuevo selectedfolder en sí, que solo existe en el dump.

Árbol de carpetas frente a archivos de la carpeta seleccionada

folders (el árbol recursivo completo) y files (el contenido de la carpeta seleccionada) no tienen el mismo alcance:

AlcanceActualizaciones en vivo
folders / ad / ddTodo el webroot, recursivamente — independiente de lo seleccionadoSiempre, sea cual sea la selección FS actual
files / af / dfSolo la carpeta seleccionadaSolo para cambios en la carpeta que seleccionó el último FS

Una carpeta creada o eliminada en cualquier parte del webroot se notifica en vivo vía ad/dd aunque ningún cliente la tenga seleccionada. Un archivo creado o eliminado solo se notifica vía af/df si ocurre en la carpeta que el FS de un cliente seleccionó por última vez — files/af/df no dicen nada sobre el contenido de otras carpetas, un cliente tiene que hacer un FS a ellas para saberlo.

Actualizaciones: af / df / ad / dd

Cuando el árbol de carpetas o el contenido de la carpeta seleccionada cambia realmente, el servidor envía un mensaje por cada ruta afectada — el valor es la ruta en sí, sin envolver en un objeto ni en un array:

{ "af": "/Sequencer/gam Cyg/LIGHT/Luminance/img_001.FITS" }
{ "df": "/rapport.txt" }
{ "ad": "/Sequencer/nuevo objetivo" }
ClaveSignificado
afArchivo aparecido en la carpeta seleccionada
dfArchivo desaparecido de la carpeta seleccionada
adSubcarpeta aparecida en cualquier parte del webroot
ddSubcarpeta desaparecida de cualquier parte del webroot

Cada mensaje lleva exactamente una ruta (N archivos = N mensajes) — ver af/df/ad/dd para la referencia completa. Un renombrado se notifica como una eliminación seguida de una creación: el servidor compara una lista antes/después, no detecta los renombrados como tales.

Tras los mensajes por ruta, el servidor envía uc files y/o folders con la lista completa actualizada — pero solo para el que realmente cambió en esta pasada, nunca incondicionalmente. Un cliente puede por tanto aplicar los eventos por ruta de forma incremental, o simplemente reemplazar su(s) lista(s) con el/los mensaje(s) uc correspondiente(s).

Construir la URL de un archivo

Las rutas de files son relativas al webroot y empiezan por /. La URL de un archivo es la URL base de los medios del servidor (http(s)://hostname/ostmedia/, ver la nota « URLs media » del Modelo de datos) seguida de la ruta. Cuidado con el / inicial: concatenarlo a una base que ya termina en / da un doble //.

Lo que no hace el evento

El servidor detecta el alta, la baja y el cambio de nombre de archivos, no la modificación del contenido de un archivo existente: una imagen reescrita con el mismo nombre no produce ningún evento. Es el caso de las imágenes de previsualización de los módulos, cuya URL permanece idéntica en cada toma: un cliente debe añadir un parámetro de cache-busting (?t= + marca de tiempo) al recargar una imagen tras una actualización de su propiedad.