O SIGCOT é o sistema que um centro de operação da transmissão de energia usa para planejar, executar e registrar o que acontece na rede: cada intervenção num equipamento, cada plano de manobras, cada ocorrência do turno. É um programa desktop em Java que roda nas estações dos operadores e, desde setembro de 2026, guarda tudo num SQL Server corporativo. Antes disso, guardava num arquivo do Microsoft Access aberto pela rede.

Este caso técnico mostra como o sistema é construído, como ele trocou de banco de dados com a operação dependendo dele e quais falhas reais mudaram as regras do projeto. Os números foram conferidos no código, na versão de 29/10/2026.

Transparência: o SIGCOT foi projetado e é desenvolvido por Jeremias Cardoso, autor deste blog, que trabalha no centro de operação que usa o sistema. O texto não cita a empresa, pessoas, servidores nem equipamentos reais. Detalhes de segurança da infraestrutura também ficam de fora.

Nota de atualização (30/10/2026): este post foi reescrito como caso técnico. A versão de 18/09/2026 descrevia o sistema em termos gerais; esta traz a arquitetura, a migração do banco, os incidentes, os limites e os números conferidos no repositório.

O problema: uma rotina que não admite registro perdido

No centro da rotina está a Solicitação de Intervenção (SI). Ela descreve o que será feito num equipamento, quando, por quem e com quais cuidados de segurança. Depois passa por seis estágios: em elaboração, encaminhada, em análise, programação, execução e concluída. Em volta dela giram os outros documentos da operação: o Plano de Manobras, as autorizações para execução de serviço e de impedimento, as mensagens operativas, o relatório diário de ocorrências e o relatório de perturbação. Os tipos de intervenção seguem os procedimentos de rede do Operador Nacional do Sistema Elétrico (ONS).

Antes de o sistema reunir tudo, cada parte vivia num lugar. A escala de turno era outro programa, com login e banco próprios. Mesmo hoje, o informe para os grupos de mensagem sai pronto do sistema, mas o último passo ainda é copiar e colar. O acompanhamento da parcela variável, o desconto na receita da transmissora quando um equipamento fica indisponível, vinha de uma planilha com 35 abas e cerca de 18 mil fórmulas, e ganhou telas próprias no sistema em outubro.

O requisito que atravessa tudo é fácil de dizer e difícil de cumprir: nada pode se perder em silêncio. Uma SI que o operador viu salva e que não está no banco é pior do que um erro na tela, porque ninguém vai procurá-la. Esse requisito explica quase todas as decisões abaixo, e quase todos os incidentes também. É a mesma falha silenciosa que derruba automações comuns, num lugar onde ela custa mais caro — e a mesma insistência em registrar o fato antes de confiar na tela que aparece no GradUP quando a prova precisa sobreviver a auditoria.

O sistema em números

Todos os números são de outubro de 2026, com a data de cada medida entre parênteses.

O sistema em números
MedidaValor
Código da aplicação (Java, telas e estilos)cerca de 205 mil linhas (29/10)
Código de testescerca de 127 mil linhas (29/10)
Scripts da migração para o SQL Servercerca de 11 mil linhas (29/10)
Arquivos Java da aplicação e telas822 e 80 (29/10)
Testes automatizados5.580, nenhuma falha (27/10)
Versões no histórico174, da 2.0.05 à 2.13.1 (25/10)
Estações registradas na frota61 (26/10)
Banco de dados142 tabelas, 297.323 linhas (25/10)

A contagem de linhas considera só os arquivos versionados e exclui linhas em branco e comentários. Na aplicação são 180 mil linhas de Java, 14 mil de telas em FXML, o formato de tela do JavaFX, e 10 mil de estilos CSS. Com brancos e comentários, ela passa de 288 mil linhas físicas.

O banco, por outro lado, é pequeno: menos de 300 mil linhas e cerca de 5,5 MB no backup de 25/10. O que torna o sistema difícil não é volume de dados. É gente gravando ao mesmo tempo, de lugares diferentes, num processo em que um registro perdido tem consequência.

Arquitetura: um programa desktop em dezenas de estações

O SIGCOT é escrito em Java 21 com JavaFX, a biblioteca de interface gráfica do Java, e construído com Maven. Roda no Windows de cada operador, não no navegador. O centro de operação já tinha as estações, e o sistema fica aberto o turno inteiro, reagindo a eventos sem recarregar página. Desde setembro de 2026, cada estação tem a sua instalação local e se atualiza sozinha: o sistema avisa quando há versão nova e faz a troca. O pacote publicado é assinado, e o atualizador recusa o arquivo que não confere.

Três decisões de arquitetura merecem registro.

Um só endereço de banco para todos. No começo, cada estação lia o endereço do banco num arquivo de configuração próprio. Duas estações chegaram a abrir bancos diferentes, e o resultado parecia sumiço de dados. Hoje o endereço vem de um ponteiro central, com uma cópia de reserva e um cache local como último recurso. Em emergência, uma estação pode apontar para outro banco, mas essa troca nunca grava no ponteiro central e se desfaz sozinha.

A concorrência é entre processos, não entre threads. Mais de quinze estações gravam no mesmo banco, e cada uma é um processo separado que não divide memória com as outras. A regra do projeto é direta: teste que roda tudo num processo só não cobre o caso real. No SQL Server há testes com estações concorrentes de verdade. Num deles, quatro estações cadastram a mesma ocorrência no mesmo instante, e uma trava do banco deixa passar só uma.

Nada de banco na thread da tela. No JavaFX, uma única thread desenha a tela. Se ela espera uma consulta pela VPN, a tela congela. Um inventário de 22/06/2026 achou 297 pontos que faziam esse tipo de espera na thread da tela. O padrão adotado manda o trabalho para uma thread de fundo e só devolve o resultado se a tela ainda mostra o mesmo registro. Um vigia grava no log, com a pilha de chamadas, toda vez que a thread da tela fica presa por mais de 200 milissegundos.

A arquitetura também tem dívida, e o próprio repositório a registra. Uma avaliação de código descreveu o padrão real como um MVC erodido, com controladores de tela de milhares de linhas. O maior tem perto de 7.900 linhas num só arquivo.

Do Access ao SQL Server com a operação em uso

Até 11/09/2026, o banco do SIGCOT era um arquivo do Access numa pasta de rede. Quem trabalhava de fora do escritório abria, pela VPN, um arquivo de 19 MB. A documentação do projeto tratava 15 clientes simultâneos como limite duro do arquivo compartilhado, e o arquivo pedia o ritual periódico de compactar e reparar. Em julho, o projeto já registrava como decisão fechada que a cura real era trocar o banco.

O plano que caiu em nove dias

O plano de migração foi escrito em 18/08/2026. Em 27/08, quando a TI enviou os manuais de conexão, a premissa caiu em quatro pontos. Os três que mais pesaram: o banco não seria dedicado ao SIGCOT, e sim compartilhado com outros sistemas; a versão era o SQL Server 2019; e a collation, a regra que o banco usa para comparar texto, já estava definida. A fronteira do sistema deixou de ser o banco inteiro e passou a ser um schema, a área do banco reservada para ele.

Duas medições feitas nessa fase mostram por que conferir vale mais do que supor:

  • Coluna sem collation declarada herda a do banco. Em dois bancos iguais exceto nesse ponto, a mesma busca por um nome com til, digitado sem o til, devolveu 0 linhas num e 1 no outro. Declarar a collation na coluna resolve.
  • O “verdadeiro” do Access não é o do SQL Server. Uma consulta que comparava um campo lógico com -1, o valor de verdadeiro no Access, compilava no servidor e não achava nada. A parcela variável sairia R$ 0,00. A correção entrou em 04/09, com um teste que varre o código inteiro atrás do padrão.

Traduzir na conexão em vez de reescrever

As consultas estavam espalhadas em 98 arquivos. Em vez de reescrever cada uma, o projeto pôs um tradutor central na conexão: ele acrescenta o schema aos nomes das tabelas e troca o dialeto do Access pelo do SQL Server, como Now() por GETDATE(). Para provar que funcionava, uma ferramenta extraiu cada frase SQL do próprio código Java e a compilou, sem executar, num SQL Server local com as 99 tabelas. De 727 frases, 716 compilaram, 8 ficaram inconclusivas e 3 falharam. Nenhuma das três falhas era do tradutor.

Pit stop, não troca de pneu com o carro andando

A tentação era manter os dois bancos vivos e sincronizar por baixo durante a transição. O plano rejeitou isso por escrito: uma SI salva no Access depois da última extração deixaria de existir, sem erro e sem log.

O que se fez foi ensaiar sobre cópia, com a produção seguindo no Access. Em 06/09, uma carga de ensaio levou 47.619 linhas em 87 tabelas para o servidor. A virada foi um pit stop curto, na noite de 11/09: todos fora do sistema, retrato final do arquivo, recarga, conferência por contagem e liberação. Cada passo do roteiro terminava com um código, e só se seguia com código zero.

Na mesma noite, o centro de operação passou a trabalhar na versão 2.0.146, já sobre o SQL Server. Em 14/09, o pessoal de campo começou a entrar pelo instalador. Em 16/09 veio a decisão de aposentar o Access: novas versões em Access não voltariam, e o arquivo congelado virou histórico. Em 22/09, a escala de turno, que ainda vivia num banco Access à parte, foi carregada: 21.989 linhas numa única transação, conferidas origem contra destino.

O rito para mudar a estrutura do banco

Com o banco no servidor, mudar uma tabela virou o momento mais arriscado do projeto. As regras que ficaram:

  • A aplicação não muda a estrutura do banco. Em modo servidor, o programa não emite DDL, os comandos que criam ou alteram tabelas. A decisão está num registro de arquitetura, o ADR-009. A primeira versão dele foi reprovada na revisão do próprio projeto, com notas entre 7,5 e 8,7 contra um corte de 9,5, e foi reescrita.
  • Toda mudança é um script numerado, que pode rodar duas vezes sem estragar nada, validado antes num SQL Server 2019 local. Quem aplica é uma pessoa, com uma ferramenta que pede confirmação digitada e recusa, antes de conectar, qualquer comando fora da área do SIGCOT. Logo depois, uma segunda ferramenta confere o resultado objeto a objeto.
  • O campo nasce no banco antes da versão que o usa. Código que espera uma coluna ainda inexistente quebra em produção.
  • Largura de texto se decide generosa na criação. Texto que o usuário digita livremente vai como NVARCHAR(MAX). Alargar depois custa um script e uma janela; nascer largo não custa nada.
  • A aplicação confere o banco ao abrir, contra um contrato do que espera encontrar. Se falta algo que ela exige, ela não abre.

A última regra parece só prudência. Em 18/09, ela derrubou o sistema inteiro, como mostra a próxima seção.

Incidentes que viraram regra

Quase toda regra acima tem uma história. Seis delas:

31/07/2026: a SI parou de salvar. O operador via um erro de permissão numa pasta de licenças do Java, sem relação nenhuma com a SI. Eram dois erros empilhados. O de cima vinha do driver do banco, que, ao montar a mensagem de erro, varria o disco e estourava. O de baixo era o real: o nome do equipamento numa coluna limitada a 100 caracteres. A correção tornou a mensagem de erro segura em 17 pontos do código. A lição foi para o rito: largura se decide na criação. Quando o banco novo foi criado, o texto livre já nasceu sem limite.

03/08/2026: três horas sem entrar no sistema. O próprio autor tentou 27 vezes reapontar o banco, passando por 5 arquivos diferentes. A causa era tripla. O pacote saía sem a versão gravada, o que reduziu 459 ocorrências de erro a um único “Erro de rede inesperado”. A checagem do arquivo só perguntava se ele existia e podia ser lido. E uma mensagem única cobria quatro causas diferentes. A versão 2.0.130 passou a testar o arquivo de verdade e a dizer qual causa aconteceu.

07/08/2026: uma SI salva, lida de volta e depois perdida. A tela confirmou, o informe saiu no canal da equipe, e mesmo assim os quatro comandos de atualização seguintes atingiram zero linhas. A investigação mediu o tamanho do problema: uma SI não recuperada em 671, em dois meses. A resposta foi proporcional. A atualização passou a exigir exatamente uma linha afetada, dentro de uma transação, e a tela ganhou três desfechos distintos em vez de um “salvo” genérico.

19/08/2026: o relatório diário “parou”. O envio automático do relatório deixou de chegar depois que a central de automações entrou no ar, e o registro continuava dizendo que tinha enviado. A causa estava no envio: o script começava ignorando todos os erros, e o programa devolvia sucesso por ter conseguido abrir o processo, não porque o e-mail saiu. A correção foi conferir o código de saída de cada etapa.

02/09/2026: quedas duas a três vezes por dia. Um sinal de presença, gravado a cada minuto por cada estação, obrigava as outras a recarregar o banco inteiro. A versão 2.0.142 tirou o sinal.

18/09/2026: o script que derrubou a própria versão. Um registro de perturbação com mais de uma função de transmissão não salvava, por um defeito de largura de coluna latente desde junho. Um script alargou quatro colunas e rodou às 20:30, com código zero na aplicação e na conferência. Logo depois, nenhuma estação abria a versão 2.0.147. O conferente de estrutura dessa versão bloqueava qualquer diferença de tamanho de coluna, inclusive coluna maior. E a checagem de atualização só rodava depois dele, então nunca chegou a rodar. O roteiro do script afirmava que coluna mais larga não atrapalharia, e ninguém tinha conferido isso contra o conferente.

A saída foi instalar a 2.0.148 estação por estação. A 2.0.149 trouxe um conferente que só bloqueia o que de fato faz o programa errar, e passou a verificar a versão na tela de login, antes de tocar no banco. A regra que ficou: nenhuma alteração de coluna enquanto houver estação numa versão antiga.

O fio comum é o mesmo em quase todos os casos: o sistema achava que estava certo. Cada correção trocou uma falha silenciosa por uma falha visível, ou por uma regra escrita.

Testes que travam decisões

A suíte cresceu com o sistema: 980 testes em 22/07/2026, 1.863 em 17/08, 2.817 em 10/09, véspera da virada, 4.380 na versão 2.9.0 e 5.580 em 27/10, sem nenhuma falha.

Cerca de 100 classes de teste são guardas de contrato. Em vez de testar uma função, elas travam uma decisão já tomada. Uma confere que toda tela carrega de verdade. Outra confere que os scripts SQL só mexem na área do SIGCOT. Outra mede a cobertura de rótulos para leitor de tela e reprova se ela cair abaixo do piso. Um teste só pode ser pulado se estiver numa lista com motivo, dono e prazo de validade. O projeto também registrou uma lição sobre as próprias guardas: guarda desligada por condição não é guarda, porque o relatório continua verde afirmando uma cobertura que nunca rodou.

A publicação segue a mesma lógica de portões que provam. O script de release fecha por padrão: compila uma vez, roda a suíte, gera a lista de componentes, procura vulnerabilidades conhecidas nas dependências e sela o pacote com um hash. Se qualquer passo falha, nada é gerado. A liberação acontece em duas etapas: a versão mínima exigida só sobe depois que outra estação provou que se atualizou sozinha. Antes e depois de cada liberação, um kit de leituras, só com consultas, fotografa o banco para comparar.

O que ainda não está resolvido

Um caso técnico honesto inclui o que falta:

  • O Access ainda está no código. Saiu do campo em setembro, mas o driver dele continua no projeto e parte da suíte ainda roda sobre ele. A retirada do binário está planejada em fases.
  • Datas guardadas como texto. No levantamento do plano de aposentadoria do Access, 121 colunas de data e hora eram texto, contra 51 com tipo de data. A conversão está planejada e não começou.
  • Controladores gigantes. A reorganização em módulos foi proposta num registro de arquitetura e não foi executada.
  • Acessibilidade parcial. A cobertura de rótulos para leitor de tela subiu de 4% em 01/08 para 47% em 26/09, com as seis telas mais usadas em 100%. O trabalho tela a tela nas demais ainda falta, junto com a navegação completa por teclado.
  • O banco não volta. Não existe rollback de banco: restaurar um backup do servidor compartilhado afetaria outros sistemas. Por isso tudo o que muda o banco passa por ensaio antes.
  • Sem medida de ganho. O repositório conta estações, sessões e linhas, mas não mede tempo economizado nem SIs por dia. Este texto não afirma ganho de produtividade porque esse número não existe.

Lições que valem fora do setor elétrico

  1. Meça a infraestrutura antes de confiar no plano. O plano de 18/08 estava errado em quatro pontos, e só os manuais da TI e a conferência no banco real mostraram isso. Descobrir na virada custaria a operação.
  2. Não sincronize dois sistemas vivos. Ensaie sobre cópia quantas vezes precisar e faça a troca com todos fora, conferindo por contagem.
  3. Troque falha silenciosa por falha visível. Mensagem que esconde a causa, “salvo” genérico e script que devolve sucesso sem conferir estão na origem dos piores incidentes.
  4. Confira cada guarda contra a versão que está em campo. O conferente que protegia o banco derrubou o sistema porque ninguém o testou contra a mudança que viria.
  5. Escreva a regra no dia do incidente. “Largura se decide na criação” e “nenhuma alteração de coluna com estação antiga em campo” estão escritas no repositório. É a regra escrita que impede a repetição, não a lembrança de quem estava lá.

Fontes