Skip to content

YuriFreire/Hackuti

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 

Repository files navigation

Hackuti

Simuladores do curso de UTI do Dr Yuri

<title>Ver UTI - Simulação: Duplo Disparo em SDRA</title> <script src="https://cdn.tailwindcss.com"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/luxon@3.0.1/build/global/luxon.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@1.1.0/dist/chartjs-adapter-luxon.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-streaming@2.0.0/dist/chartjs-plugin-streaming.min.js"></script> <style> body { font-family: 'Inter', sans-serif; } .monitor-screen { background-color: #0d1a2e; border: 4px solid #374151; box-shadow: 0 0 20px rgba(0, 191, 255, 0.4); } .vital-sign { border-bottom: 1px solid #1f2937; } </style>
<div class="bg-gray-200 p-6 rounded-xl shadow-2xl w-full max-w-5xl">
    
    <div class="text-center border-b-2 border-gray-300 pb-4 mb-6">
        <h1 class="text-3xl font-bold text-blue-800">Simulação: Duplo Disparo (Double Triggering)</h1>
        <p id="case-status" class="text-gray-600 mt-1 font-semibold">Cenário Inicial</p>
    </div>

    <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">

        <div class="lg:col-span-1 bg-white p-4 rounded-lg shadow-md">
            <h2 class="text-xl font-bold text-gray-800 border-b pb-2 mb-3">Caso Clínico</h2>
            <div id="case-description" class="text-gray-700 space-y-2 text-sm">
                <p>Paciente feminina, 45 anos, admitida com Síndrome do Desconforto Respiratório Agudo (SDRA) secundária a pancreatite.</p>
                <p>Está em ventilação mecânica com estratégia protetora.</p>
                <p><strong>Configurações Iniciais:</strong> PCV, Pressão Controlada: 18 cmH₂O, PEEP: 10 cmH₂O, T-insp: 0.8s, FR: 18 rpm, FiO₂: 80%.</p>
                <p>O paciente apresenta alto drive respiratório, apesar da sedação leve.</p>
            </div>
            <hr class="my-4">
            <h2 class="text-xl font-bold text-gray-800 border-b pb-2 mb-3">Ações</h2>
            <div id="actions-area" class="space-y-3">
                <p class="text-center text-gray-500 italic">Aguardando evolução do caso...</p>
            </div>
        </div>

        <div class="lg:col-span-2 monitor-screen p-4 rounded-lg">
            <div class="grid grid-cols-4 gap-4 text-white text-center mb-4">
                <div class="vital-sign pb-1"><span class="text-xs text-green-400">FC</span><p id="hr-value" class="text-2xl font-bold">110</p></div>
                <div class="vital-sign pb-1"><span class="text-xs text-red-400">PA</span><p id="bp-value" class="text-2xl font-bold">110/65</p></div>
                <div class="vital-sign pb-1"><span class="text-xs text-blue-400">SpO₂</span><p id="spo2-value" class="text-2xl font-bold">92%</p></div>
                <div class="vital-sign pb-1"><span class="text-xs text-yellow-400">VT Expirado</span><p id="vt-value" class="text-2xl font-bold">~350</p></div>
            </div>

            <div>
                <div><span class="text-xs font-semibold text-white ml-2">Fluxo (L/min)</span><canvas id="flowChart"></canvas></div>
                <div class="mt-2"><span class="text-xs font-semibold text-white ml-2">Pressão (cmH₂O)</span><canvas id="pressureChart"></canvas></div>
                <div class="mt-2"><span class="text-xs font-semibold text-white ml-2">Volume (mL)</span><canvas id="volumeChart"></canvas></div>
            </div>
        </div>
    </div>

    <div id="feedback-area" class="mt-6 bg-white p-4 rounded-lg shadow-md min-h-[50px] text-center flex items-center justify-center">
        <p class="text-gray-700 italic">A simulação começará em breve. As curvas do ventilador aparecerão no monitor.</p>
    </div>
</div>

<script>
window.onload = function() {
    
    let simulationState = 'INITIAL';
    let baseFR = 18;
    let baseTinsp = 0.8;

    const caseStatus = document.getElementById('case-status');
    const actionsArea = document.getElementById('actions-area');
    const feedbackArea = document.getElementById('feedback-area');
    const vtValue = document.getElementById('vt-value');

    // Funções para gerar dados das curvas
    function getNormalWaveData(x, type) {
        const period = 60 / baseFR;
        const t = (x / 1000) % period;
        const inspTime = baseTinsp;
        
        if (t < inspTime) { // Inspiração
            if (type === 'flow') return 60 * (1 - Math.pow(t / inspTime, 2));
            if (type === 'pressure') return 10 + 18; // PEEP + Pressão Controlada
            if (type === 'volume') return 350 * (t / inspTime);
        } else { // Expiração
            const expTime = t - inspTime;
            if (type === 'flow') return -50 * Math.exp(-expTime * 2.5);
            if (type === 'pressure') return 10; // PEEP
            if (type === 'volume') return 350 * Math.exp(-expTime * 2.5);
        }
    }

    function getDoubleTriggerData(x, type) {
        const period = 60 / baseFR;
        const t = (x / 1000) % period;
        const inspTime1 = 0.8; // T-insp do ventilador
        const inspTime2 = 0.6; // Duração do segundo disparo
        const shortExpTime = 0.1; // Pequeno intervalo entre disparos
        
        // Primeiro ciclo
        if (t < inspTime1) {
            if (type === 'flow') return 60 * (1 - Math.pow(t / inspTime1, 2));
            if (type === 'pressure') return 10 + 18;
            if (type === 'volume') return 350 * (t / inspTime1);
        } 
        // Breve expiração
        else if (t < inspTime1 + shortExpTime) {
            const expTime = t - inspTime1;
             if (type === 'flow') return -20 * Math.exp(-expTime * 5);
             if (type === 'pressure') return 10 + 18 - (10 * expTime / shortExpTime);
             if (type === 'volume') return 350 - (50 * expTime / shortExpTime);
        }
        // Segundo ciclo (Duplo Disparo)
        else if (t < inspTime1 + shortExpTime + inspTime2) {
            const inspTime = t - (inspTime1 + shortExpTime);
            if (type === 'flow') return 50 * (1 - Math.pow(inspTime / inspTime2, 2));
            if (type === 'pressure') return 10 + 18 + 12; // Pressão aumenta (stacking)
            if (type === 'volume') return 300 + (300 * (inspTime / inspTime2)); // Volume aumenta (stacking)
        }
        // Expiração final
        else {
            const expTime = t - (inspTime1 + shortExpTime + inspTime2);
            if (type === 'flow') return -80 * Math.exp(-expTime * 2.5);
            if (type === 'pressure') return 10;
            if (type === 'volume') return 600 * Math.exp(-expTime * 2.5);
        }
    }


    function createCharts() {
        const commonOptions = {
            plugins: { legend: { display: false }, streaming: { duration: 10000, refresh: 80, delay: 200, onRefresh: (chart) => {
                let dataFunction = simulationState === 'ASYNCHRONY' ? getDoubleTriggerData : getNormalWaveData;
                chart.data.datasets.forEach(dataset => {
                    dataset.data.push({ x: Date.now(), y: dataFunction(Date.now(), dataset.label.toLowerCase()) });
                });
            }}},
            scales: { x: { type: 'realtime', display: false }, y: { beginAtZero: false, ticks: { color: 'white' }, grid: { color: 'rgba(255, 255, 255, 0.2)' } } }
        };
        
        const ctxFlow = document.getElementById('flowChart').getContext('2d');
        new Chart(ctxFlow, { type: 'line', data: { datasets: [{ label: 'Flow', data: [], borderColor: 'rgb(75, 192, 192)', borderWidth: 2, pointRadius: 0 }] }, options: { ...commonOptions, scales: { ...commonOptions.scales, y: { ...commonOptions.scales.y, min: -100, max: 80 } } } });
        const ctxPressure = document.getElementById('pressureChart').getContext('2d');
        new Chart(ctxPressure, { type: 'line', data: { datasets: [{ label: 'Pressure', data: [], borderColor: 'rgb(255, 205, 86)', borderWidth: 2, pointRadius: 0 }] }, options: { ...commonOptions, scales: { ...commonOptions.scales, y: { ...commonOptions.scales.y, min: 0, max: 45 } } } });
        const ctxVolume = document.getElementById('volumeChart').getContext('2d');
        new Chart(ctxVolume, { type: 'line', data: { datasets: [{ label: 'Volume', data: [], borderColor: 'rgb(153, 102, 255)', borderWidth: 2, pointRadius: 0 }] }, options: { ...commonOptions, scales: { ...commonOptions.scales, y: { ...commonOptions.scales.y, min: 0, max: 700 } } } });
    }
    
    function startSimulation() {
        createCharts();
        feedbackArea.innerHTML = '<p class="text-gray-700 italic">Monitor a estabilizar. A aguardar evolução do paciente...</p>';
        setTimeout(developAsynchrony, 10000); 
    }

    function developAsynchrony() {
        if (simulationState === 'ASYNCHRONY') return;
        simulationState = 'ASYNCHRONY';
        caseStatus.textContent = 'Evolução: Assincronia Detectada';
        vtValue.textContent = '~600'; // VT aumenta devido ao duplo disparo
        feedbackArea.innerHTML = `<p class="text-red-600 font-bold">ALERTA: O monitor agora mostra Duplo Disparo, com empilhamento de volume e aumento da pressão. Qual a sua intervenção?</p>`;
        showActions();
    }

    function showActions() {
        actionsArea.innerHTML = `
            <button onclick="window.handleAction('increase_tinsp')" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded transition duration-200">Aumentar Tempo Inspiratório</button>
            <button onclick="window.handleAction('decrease_peep')" class="w-full bg-gray-500 hover:bg-gray-600 text-white font-bold py-2 px-4 rounded transition duration-200">Diminuir PEEP</button>
            <button onclick="window.handleAction('change_mode')" class="w-full bg-gray-500 hover:bg-gray-600 text-white font-bold py-2 px-4 rounded transition duration-200">Mudar para modo VCV</button>
        `;
    }

    window.handleAction = function(action) {
        if (simulationState !== 'ASYNCHRONY') return;
        simulationState = 'ACTION_TAKEN';
        actionsArea.innerHTML = '<p class="text-center text-gray-500 italic">Ação realizada. A observar...</p>';

        if (action === 'increase_tinsp') {
            feedbackArea.innerHTML = `<p class="text-green-700 font-bold">AÇÃO CORRETA: Ao aumentar o T-insp, você alinhou o tempo do ventilador com o tempo neural do paciente. A assincronia de Duplo Disparo foi resolvida.</p>`;
            simulationState = 'CORRECTED';
            baseTinsp = 1.2; // T-insp corrigido
            vtValue.textContent = '~350';
            caseStatus.textContent = 'Resolvido: Paciente Sincronizado';
        } else {
            let feedbackText = (action === 'decrease_peep')
                ? `AÇÃO INCORRETA: Diminuir a PEEP num paciente com SDRA pode levar a desrecrutamento alveolar e piorar a hipoxemia, sem corrigir a causa do Duplo Disparo.`
                : `AÇÃO INCORRETA: Mudar para VCV sem ajustar o T-insp ou o fluxo não resolveria o descompasso temporal e poderia até piorar o conforto do paciente.`;
            feedbackArea.innerHTML = `<p class="text-red-700 font-bold">${feedbackText}</p>`;
            setTimeout(developAsynchrony, 5000);
        }
    }

    startSimulation();
};
</script>

About

Simuladores do curso de UTI do Dr Yuri

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published