Voltar ao console

MeshVigil · arquitetura

Um simulador que não cai

O MeshVigil modela uma rede AMI RF-mesh — medidores roteando por coletores até uma central (head-end) — com telemetria sintética, injeção de caos e um parser DLMS/COSEM real no coração. O foco de engenharia é menos a simulação em si e mais torná-la determinística, testável e de hospedagem gratuita, sem timeout de serverless.

Visão geral

BROWSER
Thread da UI
React · Canvas · Zustand
snapshots
Web Worker
engine determinística por tick
encode / decode
Codec DLMS/COSEM
HDLC · APDU · A-XDR · OBIS
▲ HTTPS (assets estáticos · fetch ocasional) ▼
VERCEL (servidor fino)
Next.js App Router
estático + RSC
/api/health
liveness + self-test do codec
/api/snapshot
Upstash KV opcional
OpenTelemetry
@vercel/otel

A simulação inteira roda no cliente, dentro de um Web Worker. O worker é o dono do estado autoritativo e o avança por um timer; a thread da UI renderiza uma projeção de cada snapshot. O servidor é deliberadamente fino — um health check e um armazenamento opcional de snapshots — então não há processo persistente para pagar, escalar ou ver cair.

O codec DLMS/COSEM

Cada leitura que a simulação emite é serializada em um frame DLMS/COSEM DataNotificationgenuíno — framing HDLC com CRC-16/X.25 no cabeçalho e no frame, uma camada LLC, uma APDU xDLMS e dados COSEM em A-XDR carregando registradores identificados por OBIS. O inspetor então decodifica exatamente esses bytes de volta para objetos legíveis. Não há caminho de dados falso: bytes entram, objetos saem — as duas direções cobertas por testes, incluindo um round-trip completo encoder ↔ parser e um frame deliberadamente corrompido que precisa falhar na verificação de FCS.

Por que determinismo

A engine é uma função pura de (seed, tick, eventos de caos). Nenhum Math.random() é chamado em lugar nenhum; todo sorteio vem de um PRNG seedado misturado com o tick atual. A mesma seed e o mesmo log de comandos produzem uma saída byte a byte idêntica — é isso que torna a simulação testável, que permite reproduzir um snapshot compartilhado com exatidão e que deixaria um browser e um servidor concordarem sobre o estado sem uma conexão ativa.

Alternativas consideradas

Um desenho server-driven era uma opção. O raciocínio por trás das escolhas feitas:

OpçãoVereditoPor quê
Redis + Vercel Cron + SSERejeitadoO cron do Hobby roda cerca de uma vez por dia e handlers SSE batem no timeout de função — o oposto exato de um sistema que fica de pé. Ainda força uma dependência externa numa demo que deveria simplesmente funcionar.
Engine em Rust → WASMAdiadoAdiciona um toolchain e um risco real de quebrar o deploy por um ganho de performance que não existe nesta escala. Um Web Worker em TypeScript já entrega execução client-side de custo zero e escala infinita.
Transporte por WebSocketRejeitadoFunções serverless não seguram sockets de longa duração. Com a engine no browser não há a que se conectar — o problema de transporte desaparece.
TypeScript 7 / ESLint 10 gume-vivoAdiadoFixei as duas ferramentas com maior chance de quebrar o type-checker embutido do build nas suas majors comprovadas. Latest onde é seguro; conservador no caminho crítico de deploy.

Stack

  • Next.js 16
  • React 19
  • TypeScript 5.9
  • Tailwind v4
  • Zustand 5
  • Web Workers
  • Canvas 2D
  • Vitest 4
  • Playwright
  • @vercel/otel
  • Upstash Redis
  • GitHub Actions