"use client"; import { useState, useMemo, useRef } from "react"; import { usePathname } from "next/navigation"; import Header from "@/components/Header"; import Footer from "@/components/Footer"; import PageHero from "@/components/PageHero"; import * as diagnosticoPt from "@/lib/diagnosticoData"; import * as diagnosticoEn from "@/lib/data/en/diagnostico"; import { Activity, AlertTriangle, Check, CheckCircle2, ChevronDown, Copy, Cpu, ExternalLink, HelpCircle, Layers, LayoutGrid, RotateCcw, Search, ShieldAlert, Terminal, Wifi, Wrench, X, XCircle, Zap, } from "lucide-react"; /** Os rótulos dos sintomas vêm com emoji nos dados; a interface usa só texto e ícones. */ const semEmoji = (texto) => texto.replace(/^[\p{Extended_Pictographic}\uFE0F\s]+/u, ""); const ICONES = { Zap, Cpu, Layers, Activity, Terminal, Wifi }; const EXEMPLOS_LOG_PT = [ ["Brownout", "Brownout detector was triggered\n\nrst:0xc (SW_CPU_RESET),boot:0x13"], ["LoadProhibited", "Guru Meditation Error: Core 0 panic'ed (LoadProhibited).\nEXCVADDR: 0x00000000"], ["Task watchdog", "Task watchdog got triggered. The following tasks/users did not reset the watchdog: - IDLE0"], ["Handshake TLS", "mbedtls_ssl_handshake returned -0x2700\nThe certificate is not yet valid"], ]; const EXEMPLOS_LOG_EN = [ ["Brownout", "Brownout detector was triggered\n\nrst:0xc (SW_CPU_RESET),boot:0x13"], ["LoadProhibited", "Guru Meditation Error: Core 0 panic'ed (LoadProhibited).\nEXCVADDR: 0x00000000"], ["Task watchdog", "Task watchdog got triggered. The following tasks/users did not reset the watchdog: - IDLE0"], ["TLS Handshake", "mbedtls_ssl_handshake returned -0x2700\nThe certificate is not yet valid"], ]; const GRAVIDADE = { "Crítica": "text-danger", "Critical": "text-danger", Alta: "text-gold", High: "text-gold", "Média": "text-accent", Medium: "text-accent", }; const CHECKLISTS_PT = [ { icon: Zap, titulo: "Hardware e alimentação", itens: [ "Capacitor de 10 µF a 100 µF de baixo ESR no VCC, mais um cerâmico de 100 nF junto ao chip.", "LDO dimensionado para picos de 600 mA a 1 A, com folga térmica.", "Nenhum periférico forçando nível lógico nos pinos de strapping durante o boot.", "Capacitor de 10 µF ou 1 µF no pino EN (RST) para auto-reset estável na gravação.", ], }, { icon: Layers, titulo: "Firmware e FreeRTOS", itens: [ "Toda tarefa FreeRTOS cede a CPU com vTaskDelay, filas ou semáforos.", "Toda ISR marcada com IRAM_ATTR e sem chamadas bloqueantes.", "Sem uso contínuo de String do Arduino em laços, para evitar fragmentação do heap.", "Horário sincronizado por SNTP antes de abrir conexões TLS/HTTPS.", ], }, ]; const CHECKLISTS_EN = [ { icon: Zap, titulo: "Hardware and Power Supply", itens: [ "10 µF to 100 µF low-ESR capacitor on VCC, plus a 100 nF ceramic capacitor close to the chip.", "LDO regulator sized for 600 mA to 1 A current spikes, with adequate thermal margin.", "No external peripherals forcing logic levels on strapping pins during reset and boot.", "10 µF or 1 µF capacitor on the EN (RST) pin for stable auto-reset flashing.", ], }, { icon: Layers, titulo: "Firmware and FreeRTOS", itens: [ "Every FreeRTOS task yields CPU time with vTaskDelay, queues, or semaphores.", "Every ISR marked with IRAM_ATTR with zero blocking calls.", "Avoid continuous heap reallocations using Arduino Strings in tight loops.", "Clock synchronized via SNTP before attempting TLS/HTTPS handshakes.", ], }, ]; export default function DiagnosticoPage() { const pathname = usePathname(); const isEn = pathname?.startsWith("/en"); const { categoriasDiagnostico, sintomasRapidos, errosCatalogo, regrasDecodificadorLog } = isEn ? diagnosticoEn : diagnosticoPt; const exemplosLog = isEn ? EXEMPLOS_LOG_EN : EXEMPLOS_LOG_PT; const checklists = isEn ? CHECKLISTS_EN : CHECKLISTS_PT; const [termoBusca, setTermoBusca] = useState(""); const [categoriaAtiva, setCategoriaAtiva] = useState("todos"); const [sintomaAtivo, setSintomaAtivo] = useState(null); const [cardAberto, setCardAberto] = useState(null); const [linguagemCodigo, setLinguagemCodigo] = useState({}); // { [id]: 'arduino' | 'espidf' } const [copiadoId, setCopiadoId] = useState(null); const searchInputRef = useRef(null); // Estado do Analisador de Logs const [logInput, setLogInput] = useState(""); const [resultadoLog, setResultadoLog] = useState(null); // Função de Análise do Log const analisarLog = (texto) => { setLogInput(texto); if (!texto || texto.trim() === "") { setResultadoLog(null); return; } for (const regra of regrasDecodificadorLog) { if (regra.padrao.test(texto)) { setResultadoLog(regra); return; } } setResultadoLog( isEn ? { titulo: "Error pattern not automatically classified", tipo: "Unknown", explicacao: "We could not identify a classic Guru Meditation or Brownout signature in this snippet. Verify that the baud rate (e.g. 115200) is correct and whether the 'rst:' or 'panic' header is present.", acaoRecomendada: "Look for keywords such as 'Guru Meditation', 'rst:', 'LoadProhibited', 'watchdog', or 'panic' in the serial output.", } : { titulo: "Padrão de erro não categorizado automaticamente", tipo: "Desconhecido", explicacao: "Não identificamos uma assinatura clássica de Guru Meditation ou Brownout neste trecho. Verifique se a taxa de baud rate (ex: 115200) está correta e se o cabeçalho 'rst:' ou 'panic' está presente.", acaoRecomendada: "Procure por palavras-chave como 'Guru Meditation', 'rst:', 'LoadProhibited', 'watchdog' ou 'panic' no console serial.", } ); }; const copiarParaClipboard = (texto, id) => { navigator.clipboard.writeText(texto); setCopiadoId(id); setTimeout(() => setCopiadoId(null), 2000); }; // Filtragem dos erros const errosFiltrados = useMemo(() => { const termo = termoBusca.toLowerCase().trim(); return errosCatalogo.filter((erro) => { const matchCategoria = categoriaAtiva === "todos" || erro.categoria === categoriaAtiva; const matchSintoma = !sintomaAtivo || erro.sintomas.includes(sintomaAtivo); const matchBusca = !termo || erro.titulo.toLowerCase().includes(termo) || erro.resumo.toLowerCase().includes(termo) || erro.causaRaiz.toLowerCase().includes(termo) || erro.logSerial.toLowerCase().includes(termo); return matchCategoria && matchSintoma && matchBusca; }); }, [categoriaAtiva, sintomaAtivo, termoBusca, errosCatalogo]); const temFiltroAtivo = termoBusca.trim() !== "" || categoriaAtiva !== "todos" || sintomaAtivo !== null; const limparTodosFiltros = () => { setTermoBusca(""); setCategoriaAtiva("todos"); setSintomaAtivo(null); searchInputRef.current?.focus(); }; const toggleCard = (id) => { setCardAberto((prev) => (prev === id ? null : id)); }; const setLang = (id, lang) => { setLinguagemCodigo((prev) => ({ ...prev, [id]: lang })); }; const totalFiltros = (termoBusca.trim() ? 1 : 0) + (categoriaAtiva !== "todos" ? 1 : 0) + (sintomaAtivo ? 1 : 0); return (
{isEn ? "Based on article by Ciência Embarcada" : "Baseado no artigo do Ciência Embarcada"}

{isEn ? "Paste the log to see the cause" : "Cole o log e veja a causa"}

{isEn ? "Copy the serial monitor output snippet. Diagnostics appear instantly." : "Copie o trecho do monitor serial. O diagnóstico aparece na hora."}

{isEn ? "Test with" : "Testar com"} {exemplosLog.map(([rotulo, texto]) => ( ))}