refacto: Exporting is now in CSV the values + auto gestion of perfusion

This commit is contained in:
gauvainboiche
2026-08-02 14:44:03 +02:00
parent c0843ed083
commit abe44eca1d
9 changed files with 646 additions and 217 deletions
+67 -13
View File
@@ -68,6 +68,31 @@ export const CLINICAL_THRESHOLDS = {
}
};
// Unités de perfusion supportées
export const INFUSION_UNITS = {
MG_KG_H: 'mg_kg_h', // mg/kg/h
MCG_KG_MIN: 'mcg_kg_min',// µg/kg/min
MG_H: 'mg_h' // mg/h
};
/**
* Calcule la dose en mg administrée sur un intervalle de 5 minutes
*/
export function calculate5MinInfusionDose(rate, unit, weight) {
if (!rate || rate <= 0) return 0;
switch (unit) {
case INFUSION_UNITS.MG_KG_H:
return (rate * weight) / 12;
case INFUSION_UNITS.MCG_KG_MIN:
return (rate * weight * 5) / 1000;
case INFUSION_UNITS.MG_H:
return rate / 12;
default:
return (rate * weight) / 12;
}
}
/**
* Calcul d'un bolus unique à l'instant t
*/
@@ -85,30 +110,59 @@ export function calculateBolusCp(model, doseMg, weightKg, timeElapsedMin) {
}
/**
* Superposition Linéaire des Bolus et Perfusions sur 300 min
* Propage la perfusion active sur la timeline (pas de 5 min)
* Indique si chaque pas de temps possède une consigne explicite (isExplicit) ou héritée.
*/
export function generateSimulationData({ model, weightKg, timelineInputs }) {
const timeGrid = Array.from({ length: 61 }, (_, i) => i * 5);
const cpResults = new Array(61).fill(0);
export function processTimelineEvents(timelineInputs, weightKg, infusionUnit) {
let currentRate = 0;
timeGrid.forEach((targetTime, targetIndex) => {
return timelineInputs.map((item) => {
const isExplicit = item.perfRate !== undefined && item.perfRate !== null && item.perfRate !== '';
if (isExplicit) {
const parsed = parseFloat(item.perfRate);
currentRate = isNaN(parsed) ? 0 : parsed;
}
const bolusMg = parseFloat(item.bolusMg) || 0;
const infusionMg5Min = calculate5MinInfusionDose(currentRate, infusionUnit, weightKg);
return {
time: item.time,
bolusMg: bolusMg,
infusionRate: currentRate,
isExplicit: isExplicit,
infusionMg5Min: infusionMg5Min
};
});
}
/**
* Superposition Linéaire des Bolus et Perfusions sur 300 min (ng/mL arrondis à l'entier)
*/
export function generateSimulationData({ model, weightKg, timelineInputs, infusionUnit = INFUSION_UNITS.MG_KG_H }) {
const processedTimeline = processTimelineEvents(timelineInputs, weightKg, infusionUnit);
const cpResults = [];
processedTimeline.forEach((targetEvent, targetIndex) => {
let totalCp = 0;
for (let i = 0; i <= targetIndex; i++) {
const deltaTime = targetTime - timeGrid[i];
const input = timelineInputs[i] || { bolusMg: 0, perfRateMgKgH: 0 };
const sourceEvent = processedTimeline[i];
const deltaTime = targetEvent.time - sourceEvent.time;
if (input.bolusMg > 0) {
totalCp += calculateBolusCp(model, input.bolusMg, weightKg, deltaTime);
if (sourceEvent.bolusMg > 0) {
totalCp += calculateBolusCp(model, sourceEvent.bolusMg, weightKg, deltaTime);
}
if (input.perfRateMgKgH > 0) {
const perfDoseMg = (input.perfRateMgKgH * weightKg) * (5 / 60);
totalCp += calculateBolusCp(model, perfDoseMg, weightKg, deltaTime);
if (sourceEvent.infusionMg5Min > 0) {
totalCp += calculateBolusCp(model, sourceEvent.infusionMg5Min, weightKg, deltaTime);
}
}
cpResults[targetIndex] = Math.round(totalCp * 100) / 100;
cpResults.push({
time: targetEvent.time,
cp: Math.round(totalCp)
});
});
return cpResults;