Creado apartir del commit b888de6c92056de294456f581a56e25e734d4ab7 de develop
This commit is contained in:
@@ -0,0 +1,610 @@
|
||||
using adas_core.Application.Exceptions;
|
||||
using adas_core.Application.Repositories.Interfaces;
|
||||
using adas_core.Application.Services.Interfaces;
|
||||
using adas_core.Application.Subscriptions;
|
||||
using adas_core.Domain.Enums;
|
||||
using adas_core.Domain.Models;
|
||||
using adas_core.Domain.Models.AppSettings;
|
||||
using adas_core.Domain.Models.Filter;
|
||||
using adas_core.Domain.Models.MongoModels;
|
||||
using adas_core.Domain.Models.Pumps;
|
||||
using adas_core.Domain.Models.Responses;
|
||||
using adas_core.Domain.Utils;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MongoDB.Bson;
|
||||
using Patient = adas_core.Domain.Models.MongoModels.Patient;
|
||||
|
||||
namespace adas_core.Application.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Servicio maestro de gestión de bombas
|
||||
/// - Procesa ApiRequest (HL7/Alaris transformado)
|
||||
/// - Histórico clínico (pump_observations)
|
||||
/// - Histórico de alarmas (pump_alarm_events)
|
||||
/// - Alarmas activas (pump_alarm_state)
|
||||
/// - Snapshot (pump_state)
|
||||
/// - Broadcast de snapshots (PumpState + PumpAlarmState)
|
||||
/// </summary>
|
||||
public class PumpService(
|
||||
IPumpObservationRepository pumpObservationRepository,
|
||||
IPumpStateRepository pumpStateRepo,
|
||||
IPumpAlarmEventRepository alarmEventRepo,
|
||||
IPumpAlarmStateRepository alarmStateRepo,
|
||||
IPumpArchiveRepository pumpArchiveRepo,
|
||||
IPatientService patientService,
|
||||
IConfigPumpsService configPumpsService,
|
||||
IOptions<ApiSettings> apiSettings,
|
||||
ILogger<PumpService> logger,
|
||||
ISubscribersService subscribersService,
|
||||
IClientMessageService clientMessageService,
|
||||
Lazy<ICalculatedObservationsService> calculatedObservationsService,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILocalAuditService auditService,
|
||||
IConfigUnitsService configUnitsService)
|
||||
: IPumpService
|
||||
{
|
||||
|
||||
// Settings
|
||||
private readonly int _pumpExpiresSeconds = apiSettings.Value.PumpExpiresSeconds;
|
||||
private readonly bool _sendPumpsZero = apiSettings.Value.SendPumpsZero;
|
||||
|
||||
// ======================================================================
|
||||
// ENTRYPOINT
|
||||
// ======================================================================
|
||||
public async Task SaveRequest(ApiRequest req)
|
||||
{
|
||||
// Normalizar single vs list (Alaris puede mandar 1 sola)
|
||||
if (req.PumpObservation != null && (req.PumpObservations == null || req.PumpObservations.Count == 0))
|
||||
req.PumpObservations = [req.PumpObservation];
|
||||
|
||||
if (req.PumpObservations == null || req.PumpObservations.Count == 0)
|
||||
{
|
||||
logger.LogWarning("ApiRequest contains 0 PumpObservations");
|
||||
return;
|
||||
}
|
||||
|
||||
// Busca paciente (lookup/create) a partir de PatientNumber / Patient / PatientId string
|
||||
var resolvedPatient = await patientService.FindPatientByApiRequest(req);
|
||||
var foundPatientId = resolvedPatient?.Id;
|
||||
|
||||
foreach (var pobs in req.PumpObservations)
|
||||
{
|
||||
UpdatePatientFromRequest(req, pobs, foundPatientId);
|
||||
|
||||
// Regla Alaris: si el origen es AlarisPump y no hay PatientId -> descartar
|
||||
if (string.Equals(req.Type, "AlarisPump", StringComparison.OrdinalIgnoreCase) &&
|
||||
pobs.PatientId == null)
|
||||
{
|
||||
logger.LogWarning("AlarisPump: Observation descartada por ausencia de PatientId. DeviceId={device}", pobs.DeviceId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pobs.Time == DateTime.MinValue)
|
||||
pobs.Time = DateTime.UtcNow;
|
||||
|
||||
try
|
||||
{
|
||||
// 1) Procesar por tipo HL7 o según ObservationType (Alaris)
|
||||
switch (req.Type)
|
||||
{
|
||||
case "ORU_R01": // PCD-01
|
||||
case "ORU_R42": // PCD-10
|
||||
pobs.MessageType = PumpEnum.PumpMessageType.Observation;
|
||||
await ProcessObservation(pobs);
|
||||
break;
|
||||
|
||||
case "ORU_R40": // PCD-04
|
||||
pobs.MessageType = PumpEnum.PumpMessageType.Alarm;
|
||||
await ProcessAlarm(pobs);
|
||||
break;
|
||||
|
||||
case "AlarisPump":
|
||||
if (pobs.MessageType == PumpEnum.PumpMessageType.Alarm)
|
||||
await ProcessAlarm(pobs);
|
||||
else
|
||||
await ProcessObservation(pobs);
|
||||
break;
|
||||
|
||||
default:
|
||||
logger.LogWarning("Tipo de request desconocido para PumpService: {type}", req.Type);
|
||||
break;
|
||||
}
|
||||
|
||||
// 2) Snapshot de bomba
|
||||
var state = await UpdatePumpState(pobs);
|
||||
|
||||
// 3) Alarmas activas del dispositivo
|
||||
var activeAlarms = await alarmStateRepo.FindAllActiveByDeviceAsync(pobs.DeviceId!);
|
||||
|
||||
// 4) Broadcast de snapshots (PumpState + todas las PumpAlarmState)
|
||||
await SendSnapshotsBroadcast(state, activeAlarms, pobs.PatientId ?? foundPatientId, req);
|
||||
|
||||
// 5) Retención (sobre histórico de observaciones) - reglas
|
||||
await DoRetentionActions(pobs);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Error procesando observation/alarm DeviceId={deviceId}", pobs.DeviceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// OBSERVACIONES (PCD-01 / PCD-10)
|
||||
// ======================================================================
|
||||
private async Task ProcessObservation(PumpObservation obs)
|
||||
{
|
||||
logger.LogDebug("Insertando OBSERVATION DeviceId={dev} Time={time}", obs.DeviceId, obs.Time);
|
||||
|
||||
obs.Id = ObjectId.GenerateNewId();
|
||||
obs.Expires = _pumpExpiresSeconds;
|
||||
|
||||
var mapped = await MapPumpObservation(obs);
|
||||
if (mapped != null)
|
||||
{
|
||||
await pumpObservationRepository.InsertAsync(mapped);
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, null, mapped);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// ALARMAS (PCD-04)
|
||||
// ======================================================================
|
||||
private async Task ProcessAlarm(PumpObservation obs)
|
||||
{
|
||||
logger.LogDebug("Insertando ALARM DeviceId={dev}, Phase={phase}, Type={type}",
|
||||
obs.DeviceId, obs.EventPhase, obs.AlarmType);
|
||||
|
||||
var alarmEvent = new PumpAlarmEvent
|
||||
{
|
||||
Id = ObjectId.GenerateNewId(),
|
||||
DeviceId = obs.DeviceId,
|
||||
RackId = obs.RackId,
|
||||
DeviceTypeMdc = obs.DeviceTypeMdc,
|
||||
DeviceIp = obs.DeviceIp,
|
||||
PillarAssembly = obs.PillarAssembly,
|
||||
PillarRackSlot = obs.PillarRackSlot,
|
||||
|
||||
Time = obs.Time,
|
||||
InfusionId = obs.InfusionId,
|
||||
|
||||
AlarmType = obs.AlarmType,
|
||||
AlarmTypeMdc = obs.AlarmTypeMdc,
|
||||
AlarmDescription = obs.AlarmDescription,
|
||||
AlarmPriority = obs.AlarmPriority,
|
||||
AlarmState = obs.AlarmState,
|
||||
AlarmInactivationState = obs.AlarmInactivationState,
|
||||
EventPhase = obs.EventPhase,
|
||||
|
||||
AlertSourceMdc = obs.AlertSourceMdc,
|
||||
|
||||
PatientId = obs.PatientId
|
||||
};
|
||||
|
||||
await alarmEventRepo.InsertAsync(alarmEvent);
|
||||
|
||||
await UpdateAlarmState(obs);
|
||||
}
|
||||
|
||||
private async Task UpdateAlarmState(PumpObservation obs)
|
||||
{
|
||||
if (obs.EventPhase == null) return;
|
||||
|
||||
var phaseLower = obs.EventPhase.Value.ToString().ToLowerInvariant();
|
||||
|
||||
// Cierre
|
||||
if (phaseLower == "end")
|
||||
{
|
||||
await alarmStateRepo.RemoveAsync(obs.DeviceId!, obs.AlarmType, obs.AlarmTypeMdc);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start/continue → upsert
|
||||
var state = new PumpAlarmState
|
||||
{
|
||||
Id = ObjectId.GenerateNewId(),
|
||||
DeviceId = obs.DeviceId,
|
||||
|
||||
AlarmType = obs.AlarmType,
|
||||
AlarmCodeMdc = obs.AlarmTypeMdc,
|
||||
AlarmDescription = obs.AlarmDescription,
|
||||
AlarmPriority = obs.AlarmPriority,
|
||||
AlarmState = obs.AlarmState,
|
||||
LastPhase = obs.EventPhase,
|
||||
|
||||
FirstSeen = DateTime.UtcNow,
|
||||
LastUpdated = DateTime.UtcNow,
|
||||
|
||||
AlertSourceMdc = obs.AlertSourceMdc,
|
||||
InfusionId = obs.InfusionId,
|
||||
|
||||
PatientId = obs.PatientId
|
||||
};
|
||||
|
||||
await alarmStateRepo.UpsertActiveAsync(state);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// SNAPSHOT (devuelve el PumpState actualizado)
|
||||
// ======================================================================
|
||||
private async Task<PumpState> UpdatePumpState(PumpObservation obs)
|
||||
{
|
||||
var current = await pumpStateRepo.FindByDeviceIdAsync(obs.DeviceId!)
|
||||
?? new PumpState
|
||||
{
|
||||
Id = ObjectId.GenerateNewId(),
|
||||
DeviceId = obs.DeviceId
|
||||
};
|
||||
|
||||
MergePumpState(current, obs);
|
||||
current.LastUpdated = DateTime.UtcNow;
|
||||
|
||||
await pumpStateRepo.UpsertAsync(current);
|
||||
return current;
|
||||
}
|
||||
|
||||
private static void MergePumpState(PumpState state, PumpObservation obs)
|
||||
{
|
||||
// Identidad / físico
|
||||
if (!string.IsNullOrWhiteSpace(obs.RackId)) state.RackId = obs.RackId;
|
||||
if (!string.IsNullOrWhiteSpace(obs.DeviceTypeMdc)) state.DeviceTypeMdc = obs.DeviceTypeMdc;
|
||||
if (!string.IsNullOrWhiteSpace(obs.DeviceIp)) state.DeviceIp = obs.DeviceIp;
|
||||
if (!string.IsNullOrWhiteSpace(obs.PillarAssembly)) state.PillarAssembly = obs.PillarAssembly;
|
||||
if (!string.IsNullOrWhiteSpace(obs.PillarRackSlot)) state.PillarRackSlot = obs.PillarRackSlot;
|
||||
|
||||
// Infusión
|
||||
if (!string.IsNullOrWhiteSpace(obs.InfusionId)) state.InfusionId = obs.InfusionId;
|
||||
|
||||
// Estado
|
||||
if (obs.InfusingStatus != null)
|
||||
{
|
||||
state.InfusingStatus = obs.InfusingStatus;
|
||||
state.IsInfusing = obs.IsInfusing ?? false;
|
||||
}
|
||||
if (obs.Status != null) state.Status = obs.Status;
|
||||
if (obs.PumpMode != null) state.PumpMode = obs.PumpMode;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(obs.ActiveSourceInfo)) state.ActiveSourceInfo = obs.ActiveSourceInfo;
|
||||
if (!string.IsNullOrWhiteSpace(obs.InfusionModeDetail)) state.InfusionModeDetail = obs.InfusionModeDetail;
|
||||
if (!string.IsNullOrWhiteSpace(obs.NotDeliveringReason)) state.NotDeliveringReason = obs.NotDeliveringReason;
|
||||
if (!string.IsNullOrWhiteSpace(obs.Source)) state.Source = obs.Source;
|
||||
|
||||
// Métricas
|
||||
if (HasValue(obs.FlowFluid)) state.FlowFluid = obs.FlowFluid;
|
||||
if (HasValue(obs.Rate)) state.Rate = obs.Rate;
|
||||
if (HasValue(obs.VolumeInfused)) state.VolumeInfused = obs.VolumeInfused;
|
||||
if (HasValue(obs.FluidDelivTotal)) state.FluidDelivTotal = obs.FluidDelivTotal;
|
||||
if (HasValue(obs.FluidDelivTotalSet)) state.FluidDelivTotalSet = obs.FluidDelivTotalSet;
|
||||
if (HasValue(obs.VolumeRemaining)) state.VolumeRemaining = obs.VolumeRemaining;
|
||||
if (HasValue(obs.Vtbi)) state.Vtbi = obs.Vtbi;
|
||||
if (HasValue(obs.TimeRemaining)) state.TimeRemaining = obs.TimeRemaining;
|
||||
if (HasValue(obs.TimeProgrammed)) state.TimeProgrammed = obs.TimeProgrammed;
|
||||
|
||||
// Medicación
|
||||
if (!string.IsNullOrWhiteSpace(obs.DrugName)) state.DrugName = obs.DrugName;
|
||||
if (!string.IsNullOrWhiteSpace(obs.DrugId)) state.DrugId = obs.DrugId;
|
||||
|
||||
if (HasValue(obs.Concentration)) state.Concentration = obs.Concentration;
|
||||
if (HasValue(obs.DoseRate)) state.DoseRate = obs.DoseRate;
|
||||
if (HasValue(obs.DrugAmount)) state.DrugAmount = obs.DrugAmount;
|
||||
if (HasValue(obs.DrugDoseDelivered)) state.DrugDoseDelivered = obs.DrugDoseDelivered;
|
||||
|
||||
if (HasValue(obs.PatientWeight)) state.PatientWeight = obs.PatientWeight;
|
||||
if (obs.Syringe != null) state.Syringe = obs.Syringe;
|
||||
|
||||
// Eventos / Alarmas resumen
|
||||
if (obs.Event != null) state.Event = obs.Event;
|
||||
if (obs.EventPhase != null) state.EventPhase = obs.EventPhase;
|
||||
|
||||
if (obs.AlarmType != null) state.AlarmType = obs.AlarmType;
|
||||
if (!string.IsNullOrWhiteSpace(obs.AlarmDescription)) state.AlarmDescription = obs.AlarmDescription;
|
||||
if (!string.IsNullOrWhiteSpace(obs.AlarmState)) state.AlarmState = obs.AlarmState;
|
||||
if (!string.IsNullOrWhiteSpace(obs.AlarmInactivationState)) state.AlarmInactivationState = obs.AlarmInactivationState;
|
||||
if (!string.IsNullOrWhiteSpace(obs.AlarmPriority)) state.AlarmPriority = obs.AlarmPriority;
|
||||
if (!string.IsNullOrWhiteSpace(obs.AlarmTypeMdc)) state.AlarmCodeMdc = obs.AlarmTypeMdc;
|
||||
|
||||
// Paciente
|
||||
state.PatientId = obs.PatientId;
|
||||
}
|
||||
|
||||
private static bool HasValue(CommonPumpTypes.PumpValue? v) => v is { Value: not null };
|
||||
|
||||
// MAP
|
||||
|
||||
public async Task<PumpObservation?> MapPumpObservation(PumpObservation obs)
|
||||
{
|
||||
var obs2 = await configPumpsService.Map(obs);
|
||||
var obs3 = await configUnitsService.Map(obs2);
|
||||
var obs4 = await calculatedObservationsService.Value.Map(obs3);
|
||||
|
||||
if (obs4 == null)
|
||||
logger.LogDebug("Mapping ignorado para obs");
|
||||
|
||||
return obs3;
|
||||
}
|
||||
|
||||
//Métodos
|
||||
|
||||
public Task SaveRequestAsync(ApiRequest req) => SaveRequest(req);
|
||||
|
||||
|
||||
// Últimas N observaciones por paciente
|
||||
public async Task<List<PumpObservation>> FindLastPumpObservations(ObjectId patientId, int num = 1)
|
||||
{
|
||||
var list = await pumpObservationRepository.FindByPatientId(patientId);
|
||||
if (list is List<PumpObservation> pumpObservations)
|
||||
return pumpObservations is { Count: 0 }
|
||||
? []
|
||||
: pumpObservations.OrderByDescending(x => x.Time).Take(num).ToList();
|
||||
return [];
|
||||
}
|
||||
|
||||
// Última fecha de observación por paciente (para todos los pacientes)
|
||||
public async Task<Dictionary<ObjectId, DateTime>> FindAllLastPatientObservationTime()
|
||||
{
|
||||
return await pumpObservationRepository.FindAllLastPatientObservationTimeAsync();
|
||||
}
|
||||
|
||||
// Borrado completo por paciente (observaciones + alarmas + alarmState)
|
||||
public async Task DeleteByPatientId(ObjectId id)
|
||||
{
|
||||
logger.LogDebug("Delete Pump data by Patient Id {id}", id);
|
||||
await pumpObservationRepository.DeleteByPatientId(id);
|
||||
await alarmEventRepo.DeleteByPatientId(id);
|
||||
await alarmStateRepo.DeleteByPatientId(id);
|
||||
}
|
||||
|
||||
// Archivo → por entidad paciente
|
||||
public async Task Archive(Patient patient) => await ArchiveByPatientId(patient.Id);
|
||||
|
||||
// Archivo → por PatientId (mueve a archive_pumpobservations y elimina del activo)
|
||||
public async Task ArchiveByPatientId(ObjectId id)
|
||||
{
|
||||
var list = await pumpObservationRepository.FindByPatientId(id);
|
||||
var pumpObservations = list.ToList();
|
||||
if (pumpObservations.Count != 0)
|
||||
await pumpArchiveRepo.InsertManyAsync(pumpObservations);
|
||||
|
||||
await DeleteByPatientId(id);
|
||||
logger.LogDebug("Archived Pump observations & deleted active data by Patient Id {id}", id);
|
||||
}
|
||||
|
||||
// Actualización masiva
|
||||
public async Task UpdateManyObjectId(string nameId, ObjectId id, ObjectId oldId)
|
||||
{
|
||||
|
||||
if (string.IsNullOrWhiteSpace(nameId))
|
||||
throw new ArgumentException("nameId no puede ser nulo o vacío.", nameof(nameId));
|
||||
|
||||
var updatedObs = await pumpObservationRepository.UpdateManyObjectIdByFieldAsync(nameId, id, oldId);
|
||||
|
||||
// actualizar también alarmas activas e históricas
|
||||
_ = await alarmEventRepo.UpdateManyObjectIdByFiledNameAsync(nameId, id, oldId);
|
||||
_ = await alarmStateRepo.UpdateManyObjectIdByFieldNameAsync(nameId, id, oldId);
|
||||
|
||||
logger.LogInformation(
|
||||
"UpdateManyObjectId completado. Campo={field}, oldId={oldId}, newId={newId}. Obs actualizadas={obsUpdated}",
|
||||
nameId, oldId, id, updatedObs);
|
||||
|
||||
// Si quieres auditar el cambio:
|
||||
await auditService.CreateAuditLogAsync(
|
||||
httpContextAccessor.HttpContext?.User!,
|
||||
new { Field = nameId, OldId = oldId, NewId = id, Scope = "PumpObservation" },
|
||||
null);
|
||||
|
||||
}
|
||||
|
||||
// ConfigPumpsService
|
||||
public async Task<List<ConfigPumpItem>?> GetItemsById(string id)
|
||||
=> await configPumpsService.GetConfigItems(id);
|
||||
|
||||
public async Task<List<ConfigPumps>?> GetAllPumpConfig()
|
||||
=> await configPumpsService.GetAllPumpConfigs()
|
||||
?? throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
|
||||
public async Task<ConfigPumps?> GetPumpConfigsById(string id)
|
||||
=> await configPumpsService.GetPumpConfigById(id)
|
||||
?? throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
|
||||
public async Task<ConfigPumps?> UpdatePumpConfig(ConfigPumps config)
|
||||
{
|
||||
var oldConfig = await configPumpsService.GetPumpConfigById(config.Id);
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldConfig, config);
|
||||
return await configPumpsService.UpdatePumpConfig(config)
|
||||
?? throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
|
||||
public async Task<ConfigPumps?> InsertPumpConfig(ConfigPumps config)
|
||||
{
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, null, config);
|
||||
return await configPumpsService.InsertPumpConfig(config)
|
||||
?? throw new ConflictException(HttpEnum.ErrorMessage.ConflictCreationFailed);
|
||||
}
|
||||
|
||||
public async Task<bool> DeletePumpConfig(ConfigPumps config)
|
||||
{
|
||||
var result = await configPumpsService.DeletePumpConfig(config);
|
||||
if (!result) throw new ConflictException(HttpEnum.ErrorMessage.ConflictDeleteFailed);
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, config, null);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Paginación de observaciones
|
||||
public async Task<PaginationResponse<PumpObservation>?> GetPaginatedPump(PaginationFilter filter)
|
||||
{
|
||||
// Implementación compatible sin nuevos métodos en los repos:
|
||||
// 1) Si llega PatientId, paginamos en memoria desde FindByPatientId.
|
||||
// 2) Si llega DeviceId, usamos FindByDeviceIdAsync y paginamos en memoria.
|
||||
// 3) Si no hay filtro, devolvemos vacío para evitar lecturas completas.
|
||||
|
||||
var page = filter.PageNumber <= 0 ? 1 : filter.PageNumber;
|
||||
var size = filter.PageSize <= 0 ? 20 : filter.PageSize;
|
||||
|
||||
var fr = filter.FilteredRequest;
|
||||
if (fr == null) return new PaginationResponse<PumpObservation>([], page, size, 0);
|
||||
|
||||
List<PumpObservation> all;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(fr.PatientId))
|
||||
{
|
||||
if (!ObjectId.TryParse(fr.PatientId, out var patientId))
|
||||
return new PaginationResponse<PumpObservation>([], page, size, 0);
|
||||
|
||||
var list = await pumpObservationRepository.FindByPatientId(patientId);
|
||||
all = list.ToList();
|
||||
|
||||
if (fr.StartDate.HasValue)
|
||||
all = all.Where(o => o.Time >= fr.StartDate.Value).ToList();
|
||||
if (fr.EndDate.HasValue)
|
||||
all = all.Where(o => o.Time <= fr.EndDate.Value).ToList();
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(fr.DeviceId))
|
||||
{
|
||||
var found = await pumpObservationRepository.FindByDeviceIdAsync(fr.DeviceId, fr.StartDate, fr.EndDate);
|
||||
all = found.ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
all = [];
|
||||
}
|
||||
|
||||
var count = all.Count;
|
||||
var pageData = all
|
||||
.OrderByDescending(o => o.Time)
|
||||
.Skip((page - 1) * size)
|
||||
.Take(size)
|
||||
.ToList();
|
||||
|
||||
return new PaginationResponse<PumpObservation>(pageData, page, size, count);
|
||||
}
|
||||
|
||||
// Inserción manual
|
||||
public async Task InsertPumpObservation(PumpObservation obs)
|
||||
{
|
||||
logger.LogDebug("Insert {obs}", obs);
|
||||
obs.Id = ObjectId.GenerateNewId();
|
||||
if (obs.Time == DateTime.MinValue) obs.Time = DateTime.UtcNow;
|
||||
var obsMapped = await MapPumpObservation(obs);
|
||||
|
||||
if (obsMapped != null)
|
||||
{
|
||||
await pumpObservationRepository.InsertAsync(obsMapped);
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, null, obsMapped);
|
||||
|
||||
if (!_sendPumpsZero && obsMapped.Number == 0) return;
|
||||
|
||||
// Tras inserción manual, actualizar y emitir snapshots
|
||||
var state = await UpdatePumpState(obsMapped);
|
||||
var activeAlarms = await alarmStateRepo.FindAllActiveByDeviceAsync(obsMapped.DeviceId!);
|
||||
await SendSnapshotsBroadcast(state, activeAlarms, obsMapped.PatientId, null);
|
||||
|
||||
await DoRetentionActions(obsMapped);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DoRetentionActions(PumpObservation obs)
|
||||
{
|
||||
var result = await configPumpsService.RetentionActions(obs);
|
||||
if (result is not { RetentionPolicyValue: not null })
|
||||
return;
|
||||
|
||||
switch (result.RetentionPolicy)
|
||||
{
|
||||
case RetentionPolicy.DeleteOlderDays:
|
||||
{
|
||||
var removed = await pumpObservationRepository.DeleteOlderThanDaysAsync(
|
||||
result.RetentionPolicyValue.Value);
|
||||
|
||||
logger.LogInformation(
|
||||
"Retention DeleteOlderDays: {removed} deleted (>{days} days)",
|
||||
removed, result.RetentionPolicyValue);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case RetentionPolicy.DeleteOlderNumber:
|
||||
{
|
||||
var removed = await pumpObservationRepository.DeleteKeepLastNAsync(
|
||||
result.RetentionPolicyValue.Value);
|
||||
|
||||
logger.LogInformation(
|
||||
"Retention DeleteOlderNumber: {removed} deleted (keeping {max})",
|
||||
removed, result.RetentionPolicyValue);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case RetentionPolicy.NoDelete:
|
||||
case RetentionPolicy.DeleteOlderSeconds:
|
||||
default:
|
||||
logger.LogWarning("Unknown retention policy: {policy}", result.RetentionPolicy);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// BROADCAST SNAPSHOTS: PumpState + PumpAlarmState (activos)
|
||||
private async Task SendSnapshotsBroadcast(
|
||||
PumpState state,
|
||||
IEnumerable<PumpAlarmState> activeAlarms,
|
||||
ObjectId? patientId,
|
||||
ApiRequest? req)
|
||||
{
|
||||
var subscribers = new List<WsSubscriber>();
|
||||
|
||||
if (patientId != null)
|
||||
{
|
||||
var patient = await patientService.FindById(patientId.Value);
|
||||
if (patient != null)
|
||||
{
|
||||
subscribers = subscribersService.GetSubscribers()
|
||||
.Where(s => !s.Locations.IsNullOrEmpty()
|
||||
&& s.Locations.Any(c =>
|
||||
c.UnitName == patient.Location.UnitName &&
|
||||
c.Bed == patient.Location.Bed &&
|
||||
c.Room == patient.Location.Room))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
else if (req?.Location != null)
|
||||
{
|
||||
var loc = req.Location;
|
||||
subscribers = subscribersService.GetSubscribers()
|
||||
.Where(s => !s.Locations.IsNullOrEmpty()
|
||||
&& s.Locations.Any(c =>
|
||||
c.UnitName == loc.UnitName &&
|
||||
c.Bed == loc.Bed &&
|
||||
c.Room == loc.Room))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
if (subscribers.Count == 0) return;
|
||||
|
||||
// Enviar PumpState
|
||||
foreach (var sub in subscribers)
|
||||
await clientMessageService.SendAsync(sub.Id, OperationType.Pump, state);
|
||||
|
||||
// Enviar todas las PumpAlarmState activas
|
||||
foreach (var alarm in activeAlarms)
|
||||
{
|
||||
foreach (var sub in subscribers)
|
||||
await clientMessageService.SendAsync(sub.Id, OperationType.PumpAlarm, alarm);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void UpdatePatientFromRequest(ApiRequest req, PumpObservation obs, ObjectId? foundPatientId)
|
||||
{
|
||||
if (obs.PatientId == null && foundPatientId != null)
|
||||
obs.PatientId = foundPatientId;
|
||||
|
||||
|
||||
if (obs.PatientId != null || string.IsNullOrWhiteSpace(req.PatientId)) return;
|
||||
|
||||
if (ObjectId.TryParse(req.PatientId, out var parsed))
|
||||
obs.PatientId = parsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user