SIGCOT es el sistema que un centro de operación de la transmisión de energía usa para planificar, ejecutar y registrar lo que ocurre en la red: cada intervención en un equipo, cada plan de maniobras, cada ocurrencia del turno. Es un programa de escritorio en Java que corre en las estaciones de los operadores y, desde septiembre de 2026, guarda todo en un SQL Server corporativo. Antes, lo guardaba en un archivo de Microsoft Access abierto por la red.

Este caso técnico muestra cómo está construido el sistema, cómo cambió de base de datos con la operación dependiendo de él y qué fallas reales cambiaron las reglas del proyecto. Los números se verificaron en el código, en la versión del 29/10/2026.

Transparencia: SIGCOT fue diseñado y lo desarrolla Jeremias Cardoso, autor de este blog, que trabaja en el centro de operación que usa el sistema. El texto no cita la empresa, personas, servidores ni equipos reales. Los detalles de seguridad de la infraestructura también quedan fuera.

Nota de actualización (30/10/2026): este post se reescribió como caso técnico. La versión del 18/09/2026 describía el sistema en términos generales; esta trae la arquitectura, la migración de la base, los incidentes, los límites y los números verificados en el repositorio.

El problema: una rutina que no admite un registro perdido

En el centro de la rutina está la Solicitud de Intervención (SI). Describe qué se hará en un equipo, cuándo, por quién y con qué cuidados de seguridad. Después pasa por seis etapas: en elaboración, remitida, en análisis, programación, ejecución y concluida. Alrededor de ella giran los otros documentos de la operación: el Plan de Maniobras, las autorizaciones para ejecución de servicio y de impedimento, los mensajes operativos, el informe diario de ocurrencias y el informe de perturbación. Los tipos de intervención siguen los procedimientos de red del Operador Nacional del Sistema Eléctrico (ONS).

Antes de que el sistema reuniera todo, cada parte vivía en un lugar. La escala de turno era otro programa, con inicio de sesión y base propios. Incluso hoy, el informe para los grupos de mensajes sale listo del sistema, pero el último paso sigue siendo copiar y pegar. El seguimiento de la parcela variable, el descuento en los ingresos de la transmisora cuando un equipo queda indisponible, venía de una hoja de cálculo con 35 pestañas y cerca de 18 mil fórmulas, y ganó pantallas propias en el sistema en octubre.

El requisito que atraviesa todo es fácil de decir y difícil de cumplir: nada puede perderse en silencio. Una SI que el operador vio guardada y que no está en la base es peor que un error en la pantalla, porque nadie va a buscarla. Ese requisito explica casi todas las decisiones de abajo, y casi todos los incidentes también. Es la misma falla silenciosa que derriba automatizaciones comunes, en un lugar donde cuesta más caro — y la misma insistencia en registrar el hecho antes de confiar en la pantalla que aparece en GradUP cuando el examen tiene que sobrevivir a una auditoría.

El sistema en números

Todos los números son de octubre de 2026, con la fecha de cada medida entre paréntesis.

El sistema en números
MedidaValor
Código de la aplicación (Java, pantallas y estilos)cerca de 205 mil líneas (29/10)
Código de pruebascerca de 127 mil líneas (29/10)
Scripts de la migración a SQL Servercerca de 11 mil líneas (29/10)
Archivos Java de la aplicación y pantallas822 y 80 (29/10)
Pruebas automatizadas5.580, ninguna falla (27/10)
Versiones en el historial174, de la 2.0.05 a la 2.13.1 (25/10)
Estaciones registradas en la flota61 (26/10)
Base de datos142 tablas, 297.323 líneas (25/10)

El conteo de líneas considera solo los archivos versionados y excluye líneas en blanco y comentarios. En la aplicación hay 180 mil líneas de Java, 14 mil de pantallas en FXML, el formato de pantalla de JavaFX, y 10 mil de estilos CSS. Con blancos y comentarios, pasa de 288 mil líneas físicas.

La base, por otro lado, es pequeña: menos de 300 mil líneas y cerca de 5,5 MB en el respaldo del 25/10. Lo que hace difícil al sistema no es el volumen de datos. Es gente grabando al mismo tiempo, desde lugares distintos, en un proceso en el que un registro perdido tiene consecuencia.

Arquitectura: un programa de escritorio en decenas de estaciones

SIGCOT está escrito en Java 21 con JavaFX, la biblioteca de interfaz gráfica de Java, y construido con Maven. Corre en el Windows de cada operador, no en el navegador. El centro de operación ya tenía las estaciones, y el sistema permanece abierto todo el turno, reaccionando a eventos sin recargar la página. Desde septiembre de 2026, cada estación tiene su instalación local y se actualiza sola: el sistema avisa cuando hay una versión nueva y hace el cambio. El paquete publicado está firmado, y el actualizador rechaza el archivo que no coincide.

Tres decisiones de arquitectura merecen registro.

Una sola dirección de base para todos. Al principio, cada estación leía la dirección de la base en un archivo de configuración propio. Dos estaciones llegaron a abrir bases distintas, y el resultado parecía desaparición de datos. Hoy la dirección viene de un puntero central, con una copia de reserva y un caché local como último recurso. En una emergencia, una estación puede apuntar a otra base, pero ese cambio nunca se graba en el puntero central y se deshace solo.

La concurrencia es entre procesos, no entre hilos. Más de quince estaciones graban en la misma base, y cada una es un proceso separado que no comparte memoria con las otras. La regla del proyecto es directa: una prueba que corre todo en un solo proceso no cubre el caso real. En SQL Server hay pruebas con estaciones concurrentes de verdad. En una de ellas, cuatro estaciones registran la misma ocurrencia en el mismo instante, y un bloqueo de la base deja pasar solo una.

Nada de base de datos en el hilo de la pantalla. En JavaFX, un único hilo dibuja la pantalla. Si espera una consulta por la VPN, la pantalla se congela. Un inventario del 22/06/2026 encontró 297 puntos que hacían ese tipo de espera en el hilo de la pantalla. El patrón adoptado manda el trabajo a un hilo de fondo y solo devuelve el resultado si la pantalla todavía muestra el mismo registro. Un vigía anota en el log, con la pila de llamadas, cada vez que el hilo de la pantalla queda trabado por más de 200 milisegundos.

La arquitectura también tiene deuda, y el propio repositorio la registra. Una evaluación de código describió el patrón real como un MVC erosionado, con controladores de pantalla de miles de líneas. El mayor tiene cerca de 7.900 líneas en un solo archivo.

De Access a SQL Server con la operación en uso

Hasta el 11/09/2026, la base de SIGCOT era un archivo de Access en una carpeta de red. Quien trabajaba fuera de la oficina abría, por la VPN, un archivo de 19 MB. La documentación del proyecto trataba 15 clientes simultáneos como límite duro del archivo compartido, y el archivo pedía el ritual periódico de compactar y reparar. En julio, el proyecto ya registraba como decisión cerrada que la cura real era cambiar la base.

El plan que cayó en nueve días

El plan de migración se escribió el 18/08/2026. El 27/08, cuando TI envió los manuales de conexión, la premisa cayó en cuatro puntos. Los tres que más pesaron: la base no sería dedicada a SIGCOT, sino compartida con otros sistemas; la versión era SQL Server 2019; y la collation, la regla que la base usa para comparar texto, ya estaba definida. La frontera del sistema dejó de ser la base entera y pasó a ser un esquema, el área de la base reservada para él.

Dos mediciones hechas en esa fase muestran por qué verificar vale más que suponer:

  • Una columna sin collation declarada hereda la de la base. En dos bases iguales salvo en ese punto, la misma búsqueda de un nombre con tilde, escrito sin la tilde, devolvió 0 líneas en una y 1 en la otra. Declarar la collation en la columna lo resuelve.
  • El “verdadero” de Access no es el de SQL Server. Una consulta que comparaba un campo lógico con -1, el valor de verdadero en Access, compilaba en el servidor y no encontraba nada. La parcela variable saldría en R$ 0,00. La corrección entró el 04/09, con una prueba que recorre el código entero en busca del patrón.

Traducir en la conexión en lugar de reescribir

Las consultas estaban repartidas en 98 archivos. En lugar de reescribir cada una, el proyecto puso un traductor central en la conexión: añade el esquema a los nombres de las tablas y cambia el dialecto de Access por el de SQL Server, como Now() por GETDATE(). Para probar que funcionaba, una herramienta extrajo cada frase SQL del propio código Java y la compiló, sin ejecutar, en un SQL Server local con las 99 tablas. De 727 frases, 716 compilaron, 8 quedaron inconclusas y 3 fallaron. Ninguna de las tres fallas era del traductor.

Pit stop, no un cambio de neumático con el auto en marcha

La tentación era mantener las dos bases vivas y sincronizar por debajo durante la transición. El plan lo rechazó por escrito: una SI guardada en Access después de la última extracción dejaría de existir, sin error y sin log.

Lo que se hizo fue ensayar sobre una copia, con la producción siguiendo en Access. El 06/09, una carga de ensayo llevó 47.619 líneas en 87 tablas al servidor. El cambio fue un pit stop corto, la noche del 11/09: todos fuera del sistema, retrato final del archivo, recarga, verificación por conteo y liberación. Cada paso del guion terminaba con un código, y solo se seguía con código cero.

Esa misma noche, el centro de operación pasó a trabajar en la versión 2.0.146, ya sobre SQL Server. El 14/09, el personal de campo empezó a entrar por el instalador. El 16/09 vino la decisión de retirar Access: las versiones nuevas en Access no volverían, y el archivo congelado pasó a ser historial. El 22/09, la escala de turno, que todavía vivía en una base Access aparte, se cargó: 21.989 líneas en una sola transacción, verificadas origen contra destino.

El rito para cambiar la estructura de la base

Con la base en el servidor, cambiar una tabla se volvió el momento más riesgoso del proyecto. Las reglas que quedaron:

  • La aplicación no cambia la estructura de la base. En modo servidor, el programa no emite DDL, los comandos que crean o alteran tablas. La decisión está en un registro de arquitectura, el ADR-009. La primera versión se reprobó en la revisión del propio proyecto, con notas entre 7,5 y 8,7 contra un corte de 9,5, y se reescribió.
  • Todo cambio es un script numerado, que puede correr dos veces sin estropear nada, validado antes en un SQL Server 2019 local. Quien lo aplica es una persona, con una herramienta que pide confirmación tecleada y rechaza, antes de conectar, cualquier comando fuera del área de SIGCOT. Justo después, una segunda herramienta verifica el resultado objeto por objeto.
  • El campo nace en la base antes de la versión que lo usa. El código que espera una columna todavía inexistente se rompe en producción.
  • El ancho del texto se decide generoso en la creación. El texto que el usuario escribe libremente va como NVARCHAR(MAX). Ensanchar después cuesta un script y una ventana; nacer ancho no cuesta nada.
  • La aplicación verifica la base al abrir, contra un contrato de lo que espera encontrar. Si falta algo que exige, no abre.

La última regla parece solo prudencia. El 18/09, derribó el sistema entero, como muestra la sección siguiente.

Incidentes que se volvieron regla

Casi toda regla de arriba tiene una historia. Seis de ellas:

31/07/2026: la SI dejó de guardarse. El operador veía un error de permiso en una carpeta de licencias de Java, sin relación ninguna con la SI. Eran dos errores apilados. El de arriba venía del driver de la base, que, al armar el mensaje de error, recorría el disco y reventaba. El de abajo era el real: el nombre del equipo en una columna limitada a 100 caracteres. La corrección hizo seguro el mensaje de error en 17 puntos del código. La lección pasó al rito: el ancho se decide en la creación. Cuando se creó la base nueva, el texto libre ya nació sin límite.

03/08/2026: tres horas sin entrar al sistema. El propio autor intentó 27 veces redirigir la base, pasando por 5 archivos distintos. La causa era triple. El paquete salía sin la versión grabada, lo que redujo 459 ocurrencias de error a un único “Error de red inesperado”. La revisión del archivo solo preguntaba si existía y se podía leer. Y un mensaje único cubría cuatro causas distintas. La versión 2.0.130 pasó a probar el archivo de verdad y a decir cuál causa ocurrió.

07/08/2026: una SI guardada, leída de vuelta y después perdida. La pantalla confirmó, el informe salió en el canal del equipo, y aun así los cuatro comandos de actualización siguientes alcanzaron cero líneas. La investigación midió el tamaño del problema: una SI no recuperada en 671, en dos meses. La respuesta fue proporcional. La actualización pasó a exigir exactamente una línea afectada, dentro de una transacción, y la pantalla ganó tres desenlaces distintos en lugar de un “guardado” genérico.

19/08/2026: el informe diario “se detuvo”. El envío automático del informe dejó de llegar después de que la central de automatizaciones entró en funcionamiento, y el registro seguía diciendo que lo había enviado. La causa estaba en el envío: el script empezaba ignorando todos los errores, y el programa devolvía éxito por haber conseguido abrir el proceso, no porque el correo hubiera salido. La corrección fue verificar el código de salida de cada etapa.

02/09/2026: caídas dos a tres veces por día. Una señal de presencia, grabada cada minuto por cada estación, obligaba a las otras a recargar la base entera. La versión 2.0.142 quitó la señal.

18/09/2026: el script que derribó su propia versión. Un registro de perturbación con más de una función de transmisión no se guardaba, por un defecto de ancho de columna latente desde junio. Un script ensanchó cuatro columnas y corrió a las 20:30, con código cero en la aplicación y en la verificación. Justo después, ninguna estación abría la versión 2.0.147. El verificador de estructura de esa versión bloqueaba cualquier diferencia de tamaño de columna, incluida una columna mayor. Y la revisión de actualización solo corría después de él, así que nunca llegó a correr. El guion del script afirmaba que una columna más ancha no estorbaría, y nadie lo había verificado contra el verificador.

La salida fue instalar la 2.0.148 estación por estación. La 2.0.149 trajo un verificador que solo bloquea lo que de hecho hace errar al programa, y pasó a verificar la versión en la pantalla de inicio de sesión, antes de tocar la base. La regla que quedó: ningún cambio de columna mientras haya una estación en una versión antigua.

El hilo común es el mismo en casi todos los casos: el sistema creía que estaba en lo cierto. Cada corrección cambió una falla silenciosa por una falla visible, o por una regla escrita.

Pruebas que traban decisiones

La suite creció con el sistema: 980 pruebas el 22/07/2026, 1.863 el 17/08, 2.817 el 10/09, víspera del cambio, 4.380 en la versión 2.9.0 y 5.580 el 27/10, sin ninguna falla.

Cerca de 100 clases de prueba son guardas de contrato. En lugar de probar una función, traban una decisión ya tomada. Una verifica que toda pantalla carga de verdad. Otra verifica que los scripts SQL solo tocan el área de SIGCOT. Otra mide la cobertura de rótulos para lector de pantalla y reprueba si cae por debajo del piso. Una prueba solo puede saltarse si está en una lista con motivo, dueño y plazo de validez. El proyecto también registró una lección sobre las propias guardas: una guarda apagada por condición no es guarda, porque el informe sigue en verde afirmando una cobertura que nunca corrió.

La publicación sigue la misma lógica de compuertas que prueban. El script de release cierra por defecto: compila una vez, corre la suite, genera la lista de componentes, busca vulnerabilidades conocidas en las dependencias y sella el paquete con un hash. Si cualquier paso falla, no se genera nada. La liberación ocurre en dos etapas: la versión mínima exigida solo sube después de que otra estación probó que se actualizó sola. Antes y después de cada liberación, un kit de lecturas, solo con consultas, fotografía la base para comparar.

Lo que todavía no está resuelto

Un caso técnico honesto incluye lo que falta:

  • Access todavía está en el código. Salió de campo en septiembre, pero su driver sigue en el proyecto y parte de la suite todavía corre sobre él. El retiro del binario está planificado en fases.
  • Fechas guardadas como texto. En el relevamiento del plan de retiro de Access, 121 columnas de fecha y hora eran texto, contra 51 con tipo de fecha. La conversión está planificada y no empezó.
  • Controladores gigantes. La reorganización en módulos se propuso en un registro de arquitectura y no se ejecutó.
  • Accesibilidad parcial. La cobertura de rótulos para lector de pantalla subió de 4% el 01/08 a 47% el 26/09, con las seis pantallas más usadas en 100%. El trabajo pantalla por pantalla en las demás todavía falta, junto con la navegación completa por teclado.
  • La base no vuelve. No existe rollback de la base: restaurar un respaldo del servidor compartido afectaría a otros sistemas. Por eso todo lo que cambia la base pasa por un ensayo antes.
  • Sin medida de ganancia. El repositorio cuenta estaciones, sesiones y líneas, pero no mide tiempo ahorrado ni SI por día. Este texto no afirma ganancia de productividad porque ese número no existe.

Lecciones que valen fuera del sector eléctrico

  1. Mida la infraestructura antes de confiar en el plan. El plan del 18/08 estaba errado en cuatro puntos, y solo los manuales de TI y la verificación en la base real lo mostraron. Descubrirlo en el cambio costaría la operación.
  2. No sincronice dos sistemas vivos. Ensaye sobre una copia tantas veces como haga falta y haga el cambio con todos fuera, verificando por conteo.
  3. Cambie la falla silenciosa por una falla visible. Un mensaje que esconde la causa, un “guardado” genérico y un script que devuelve éxito sin verificar están en el origen de los peores incidentes.
  4. Verifique cada guarda contra la versión que está en campo. El verificador que protegía la base derribó el sistema porque nadie lo probó contra el cambio que iba a venir.
  5. Escriba la regla el día del incidente. “El ancho se decide en la creación” y “ningún cambio de columna con una estación antigua en campo” están escritos en el repositorio. Es la regla escrita la que impide la repetición, no el recuerdo de quien estaba ahí.

Fuentes