Documentação Técnica - Parte 2
-
Fluxo 3: Atualização de ConfiguraçõesExplicação simplificada (Suporte):
O usuário altera as configurações (ex: intervalo de leitura para 500ms, prefixo "ID:"). Ao salvar, as mudanças são aplicadas imediatamente sem reiniciar o sistema.
Explicação técnica (Dev):
[Frontend] SettingsForm.onSubmit(data) │ ├─► PUT /settings/reader (axios) │ │ │ CardService.updateReaderSettings(payload) │ ├─ ReaderSettingsStore.update(payload) → normaliza → grava JSON em disco │ └─ DeviceManager.applyReaderSettings(updated) │ └─ DigiconDG710Reader.applySettings(settings) │ ├─ pollingMs = settings.intervalMs │ ├─ continuousRead = settings.continuousRead │ └─ reinicia loop de polling (clearTimeout + scheduleNextPoll) │ └─► window.readerApi.syncRuntimeSettings(updated) [IPC] └─ ipcMain: "reader-runtime-settings:update-cache" └─ runtimeSettingsCache = { ...cache, ...payload } (usado pelo auto-paste: appendNewLine)
Fluxo 4: Reconexão de LeitoresExplicação simplificada (Suporte):
Se o leitor parar de responder, o usuário pode clicar em "Reconectar leitores" no menu da bandeja. O sistema faz disconnect e connect novamente automaticamente.
Explicação técnica (Dev):
[Tray menu: "Reconectar leitores"] │ ou [Frontend: botão Reconectar] ▼ performReconnectReaders() │ ├─► GET /readers → lista todos os leitores │ └─► Para cada reader (em paralelo): POST /readers/disconnect { key } POST /readers/connect { key } POST /readers/start { key }
Fluxo 5: Manutenção Automática de LeitoresExplicação simplificada (Suporte):
A cada 1,5 segundos, o sistema verifica se o leitor está conectado e escutando. Se estiver desconectado, tenta reconectar sozinho.
Explicação técnica (Dev):
setInterval(service.maintainReaders, 1500ms) │ └─► CardService.maintainReaders() Para cada readerKey: reader.getStatus() ├─ !connected → reader.connect() └─ connected + !listening → reader.startListening() Erros → logger.debug (não interrompem)
️ 5. ROTINAS E MÉTODOS IMPORTANTES
bootstrap()—server.tsDescrição simplificada:
Inicializa toda a API: verifica instância única, cria componentes, monta rotas e inicia o servidor.
Descrição técnica:
- Localização:
insoft-readers-api/src/server.ts - Fluxo:
ensureSingleApiInstance()→ instância de todos os serviços → monta Express → escuta na porta - Parâmetros: nenhum (usa variáveis de ambiente)
- Retorno:
Promise<void> - Possíveis saídas de emergência:
process.exit(0)(duplicata),process.exit(1)(falha crítica)
runPollingTick()—DigiconDG710Reader.tsDescrição simplificada:
O loop que "pergunta" ao hardware a cada N milissegundos se há um cartão presente.
Descrição técnica:
- Localização:
DigiconDG710Reader(método privado) - Usa
setTimeoutrecursivo (nãosetInterval), garantindo que o próximo tick só ocorre após o atual terminar - Gerencia estado
wasCardPresentpara evitar eventos duplicados no modo não-contínuo - Em caso de erro: registra em
lastErrore continua o polling no próximo tick - Parada:
stopListening()definepollingActive = false; ofinallydo tick detecta isso e não agenda o próximo
readMifareCard()—DigiconFacade.tsDescrição simplificada:
Lê os bytes do cartão físico e os converte para um número decimal (o "código do cartão").
Descrição técnica:
- Localização:
DigiconFacade - Parâmetros:
handle: number→ identificador do canal conectado - Retorno:
{ cardId: string; rawHex: string } | null - Algoritmo: buffer de 4 bytes → leitura em ordem decrescente de índice (big-endian) → hex string →
BigInt(0x${rawHex}).toString(10) - Exemplo: bytes
[0x4E, 0x61, 0xBC, 0x00]→ rawHex"00BC614E"→ cardId"12345678" - Em mock mode: retorna número aleatório de 8 dígitos
applyRuntimeSettingsToEvent()—CardService.tsDescrição simplificada:
Adiciona o prefixo e o sufixo configurados ao código do cartão antes de distribuí-lo.
Descrição técnica:
- Localização:
CardService(método privado) - Parâmetros:
event: CardReadEvent - Retorno: novo
CardReadEventcomcardId = prefix + originalCardId + suffix - Não modifica o evento original (imutabilidade via spread
{...event}) - Exemplo: prefixo
"ID:", sufixo"", cardId"12345678"→"ID:12345678"
ensureApiRunning()—ApiProcessManager.tsDescrição simplificada:
Garante que a API está disponível antes de abrir a janela. Tenta todas as formas de iniciá-la.
Descrição técnica:
- Localização:
insoft-readers-app/electron/main/services/ApiProcessManager.ts - Retorno:
Promise<void> - Lança
Errorapenas se todas as tentativas falharem - Em dev: spawna
npm run dev:once; em prod: executa o.exeda pasta resources
handleCardRead()—main.tsDescrição simplificada:
Decide o que fazer quando um cartão é lido: mostrar na UI ou colar no aplicativo ativo.
Descrição técnica:
- Localização:
insoft-readers-app/electron/main/main.ts - Se janela em foco:
mainWindow.webContents.send("card:read", event) - Se janela em background:
pasteToExternalFocusedApp(cardId, appendNewLine) - Também envia
app:notificationquando em background (aparece como toast)
pasteToExternalFocusedApp()—main.tsDescrição simplificada:
Cola o código do cartão no campo de texto ativo usando a área de transferência do Windows.
Descrição técnica:
- Localização:
insoft-readers-app/electron/main/main.ts - Usa
clipboard.writeText(cardId)do Electron - Executa VBScript pré-criado em
%TEMP%(criado na inicialização para evitar latência)insoft_paste.vbs:SendKeys "^v"(Ctrl+V)insoft_paste_nl.vbs:SendKeys "^v{ENTER}"(Ctrl+V + Enter)
wscript.exe //Bsuprime diálogos; latência ~50ms vs ~1.500ms do PowerShell- Apenas funciona em
win32
6. INTEGRAÇÕES EXTERNASDLL Nativa —
DG710Facade.dllItem Detalhe Tipo DLL C/C++ do fabricante Digicon Arquitetura x64 (requerida; x86 causa erro detectável no log) Localização Configurável via DIGICON_DLL_PATH; buscada em ~9 caminhos candidatosIntegração koffi(FFI puro TypeScript, sem recompilar addons Node)Funções usadas registryStart/Stop/IsStarted/GetChannelList,channelConnect/Disconnect/IsConnected/GetSerialNumber,mifareIsCardPresent,mifareReadCardSerialNumberFallback Mock mode automático se a DLL não for encontrada Pontos de atenção:
- A DLL deve estar acessível no mesmo diretório do executável ou em caminho absoluto
- O processo deve ser x64 para carregar a DLL x64
- A DLL pode requerer drivers do fabricante instalados no Windows
NSSM — Non-Sucking Service Manager
Item Detalhe Tipo Ferramenta externa de linha de comando Uso Gerenciar a API como Serviço Windows Comandos sc query <name>(verificar),nssm start <name>(iniciar)Requisito nssm.exedeve estar no PATH do sistema
VBScript / wscript.exe
Item Detalhe Tipo Runtime nativo do Windows Uso Simular Ctrl+V no aplicativo ativo (auto-paste) Mecanismo SendKeys "^v"/SendKeys "^v{ENTER}"Limitação Funciona apenas em Windows; requer que o aplicativo de destino suporte Ctrl+V
7. TRATAMENTO DE ERROS E EXCEÇÕESEstratégia Geral
Camada Estratégia Backend API Middleware global de erros; ZodError→ 400; erros Digicon conhecidos → 503; outros → 500DeviceManager Erros de inicialização/discover logados, não propagados (sistema continua) CardService.maintainReaders Erros de auto-reconexão logados como debug, sem interrupçãoDigiconFacade Mock mode ativado silenciosamente se DLL não carregar Electron main Erros de startup da API encerram o app via app.quit()ApiProcessManager Lança Errorse health check final falhar; Electron encerraFrontend useReaderscaptura erros axios e os expõe via estadoerrorSSE (Electron) scheduleCardEventsReconnect(1500ms)em caso de falha ou disconnectLogs Importantes
O backend usa pino com logs estruturados JSON. Nível padrão:
info.Mensagem de Log Significado "Reader backend started"API iniciada com sucesso na porta "API já está em execução. Encerrando instância duplicada."Segunda instância detectada, saiu normalmente "Porta em uso por outro processo."Porta 3791 ocupada por processo externo "Reader initialization failed; continuing backend startup"Leitor não inicializou (hardware ausente?) "Digicon native DLL not loaded. Running in mock mode."DLL não encontrada; simulando hardware "Failed to load Digicon native DLL"Detalhe do erro ao carregar DLL (inclui arquitetura) "Reader discovery failed"Leitor não detectado no discover "Failed to connect Digicon reader"Falha ao conectar; lastErrorcontém detalhes"Error while reading Digicon card"Erro durante polling de leitura "Card read"Cartão lido com sucesso (inclui o evento completo) "Shutting down backend"Shutdown iniciado (SIGTERM/SIGINT) Variáveis de Ambiente para Diagnóstico
Variável Padrão Uso LOG_LEVELinfoNível de log ( debug,info,warn,error)BACKEND_PORT3791Porta do servidor HTTP da API BACKEND_BASE_URLhttp://127.0.0.1:3791URL base da API APP_CONTROL_URLhttp://127.0.0.1:3792URL do AppControlServer DIGICON_DLL_PATHDG710Facade.dllCaminho explícito da DLL nativa READER_SETTINGS_FILEreader-runtime-settings.json(cwd)Arquivo de settings API_SERVICE_NAMEInsoftReadersApiServiceNome do serviço Windows API_EXE_NAMEinsoft-reader-api.exeNome do executável da API API_EXE_PATH— Caminho absoluto do executável (override) APP_EXECUTABLE_PATH— Caminho do .exeda interface (override)
🧪 8. CENÁRIOS COMUNS DE SUPORTE
Cenário 1: O cartão não está sendo lidoSintoma Aproxima o cartão e nada acontece Possíveis causas (a) Leitor desconectado; (b) API não está rodando; (c) Leitor em estado "stopped"; (d) DLL não encontrada (modo mock) Como investigar 1. Abrir a interface → Dashboard → verificar se "Active Readers" > 0; 2. Verificar se status é "Connected" e "Listening" na página Readers; 3. Verificar logs do backend por "mock mode"ou"Reader not found"Como resolver 1. Página Readers → selecionar leitor → clicar "Conectar" e depois "Iniciar leitura"; 2. Menu da bandeja → "Reconectar leitores"; 3. Se DLL: verificar se DG710Facade.dllestá no diretório da API
Cenário 2: O código do cartão está errado (com caracteres extras)Sintoma O cartão é lido mas o código aparece com prefixo/sufixo estranho (ex: "AAA12345678BBBB")Possíveis causas Configurações de prefixo/sufixo estão definidas Como investigar Abrir interface → Settings → verificar campos "Prefix" e "Suffix" Como resolver Settings → limpar campos Prefix e Suffix → salvar
Cenário 3: O cartão está sendo lido mas não é colado no aplicativoSintoma O toast de notificação aparece mas o texto não é inserido no campo Possíveis causas (a) Janela do Insoft Readers está em foco (comportamento esperado); (b) Aplicativo de destino não suporta Ctrl+V Como investigar 1. Verificar se a janela do Insoft Readers está na frente — se sim, o modo é exibição na UI, não auto-paste; 2. Testar colar manualmente no campo do aplicativo (Ctrl+V) Como resolver Minimizar ou mover a janela do Insoft Readers para segundo plano antes de aproximar o cartão
Cenário 4: A interface abre mas mostra erro de conexãoSintoma Frontend exibe mensagem de erro ao carregar leitores Possíveis causas API não está rodando na porta 3791 Como investigar Abrir navegador → acessar http://127.0.0.1:3791/health→ se retornar{"status":"ok"}, API está OKComo resolver 1. Aguardar alguns segundos (API pode estar iniciando); 2. Menu da bandeja → Reconectar leitores; 3. Fechar e reabrir o aplicativo
Cenário 5: Erro "Arquitetura incompatível" no logSintoma Log contém "Arquitetura incompatível: a DLL é x86 (32-bit)"Possíveis causas DG710Facade.dllé versão 32-bit mas o processo é 64-bitComo investigar Verificar qual versão da DLL está instalada Como resolver Substituir a DLL pela versão x64 fornecida pelo fabricante Digicon
Cenário 6: Porta 3791 já está em usoSintoma Log contém "Porta em uso por outro processo. Abortando inicialização da API."e a API não iniciaPossíveis causas Outro processo ocupou a porta 3791 Como investigar Executar no terminal: netstat -ano | findstr 3791para ver qual processo usa a portaComo resolver Encerrar o processo conflitante ou configurar BACKEND_PORTpara outra porta
Cenário 7: Leitor conecta mas para de responder após um tempoSintoma Leituras param sem erro aparente; leitor aparece como conectado mas não lê Possíveis causas (a) Leitor fisicamente desconectado (USB); (b) Timeout de hardware Como investigar Dashboard → verificar se "Listening" mudou para "Stopped" ou se há lastErrorna página ReadersComo resolver O timer de manutenção (1,5s) tenta reconexão automática. Se persistir: desconectar fisicamente e reconectar o USB; usar "Reconectar leitores"
9. BOAS PRÁTICAS E OBSERVAÇÕESAdicionando Suporte a Novo Modelo de Leitor
Para adicionar um novo leitor (ex: fabricante X, modelo Y):
- Criar pasta:
insoft-readers-api/src/devices/fabricante-x/ - Implementar a interface
ICardReaderem uma nova classe - Registrar em
server.ts:registry.register("fabricante-x-modelY", () => new FabricanteXReaderY()); - Se usar DLL nativa: criar uma Facade similar ao
DigiconFacade.ts
Pontos Sensíveis ao Modificar
Área Cuidado normalizeSettings()Limites de intervalMs(50ms–10s) estão validados aqui e também no Zod do settings.routes. Alterar um sem o outro cria inconsistênciareadMifareCard()A ordem de bytes (little-endian → big-endian) é específica do hardware Digicon. Alterar sem hardware para testar pode causar IDs incorretos pasteToExternalFocusedApp()Os arquivos .vbssão criados uma vez na inicialização. Setmpdir()não for gravável, o auto-paste falha silenciosamenteensureSingleApiInstance()Depende do endpoint /healthretornar exatamente{ "status": "ok" }. Mudanças no formato quebram a detecção de instância duplicadaPorta 3792 (AppControlServer) É usada bidirecionalmente: Electron escuta notificações vindas da API. Conflitos de porta impedem notificações no tray recentReads.splice(50)O histórico é armazenado apenas em memória. Reiniciar a API limpa o histórico Configuração de Runtime em Produção
O arquivo
reader-runtime-settings.jsoné persistido no diretório de trabalho (cwd) da API. Em produção, isso é o diretório do executável. Ao atualizar a API, preserve esse arquivo para não perder configurações.Modo de Desenvolvimento
Para rodar o projeto em modo desenvolvimento:
# Na raiz do monorepo — inicia todos os serviços simultaneamente: npm run dev # Separado: npm run dev:frontend # Vite na porta 5173 npm run dev:electron # Electron + TypeScript watch # A API é iniciada automaticamente pelo Electron via ApiProcessManagerCompilação para Produção
# Gera instalador NSIS para Windows x64: npm run dist # Resultado em: # insoft-readers-app/electron/release/ # Insoft-Readers-1.0.0-Setup-x64.exeO instalador empacota:
resources/api/insoft-reader-api.exe— API compilada compkgresources/frontend/— Build estático do Reactresources/— Preload, assets
Diagrama de Dependências dos Pacotes
@insoft/backend (API) ├── express ← servidor HTTP ├── koffi ← FFI para DLL nativa ├── pino ← logging ├── zod ← validação └── dotenv ← variáveis de ambiente @insoft/electron └── electron ← framework desktop @insoft/frontend ├── react ← UI ├── zustand ← estado global ├── axios ← cliente HTTP ├── react-hook-form ← formulários ├── zod ← validação de forms ├── flowbite-react ← componentes UI └── tailwindcss ← estilos
Documentação gerada a partir do código-fonte do projeto. Para atualizações, reanalise os arquivos correspondentes após mudanças significativas na arquitetura e atualize essa documentação.
- Localização: