Files

299 lines
13 KiB
C#

using System.Timers;
using adas_core.Domain.Enums;
using adas_core.Domain.Models;
using adas_core.Domain.Models.GroupedObservations;
using adas_core.Domain.Utils;
using MongoDB.Bson;
using Serilog;
using static adas_core.Domain.Models.GroupedObservation;
using Timer = System.Timers.Timer;
namespace adas_core.Application.Subscriptions;
/**
* This class is used to group the subscribers by the same group
*/
public class WsSubscriberGrouped
{
private readonly EventHandler<string> _sendEvent;
public WsSubscriberGrouped(ObjectId patientId, string wsId, string? timeZoneId,
GroupedField gf, GroupedObservation lastGroupedObservationObs, EventHandler<string> sendEvent)
{
_sendEvent = sendEvent;
if (WsSubscriber.ToList().FirstOrDefault(s => s == wsId) == null) WsSubscriber.Add(wsId);
TimeZoneId = timeZoneId ?? "Romance Standard Time";
PatientId = patientId;
Names = CollectionsUtils.IfEmptyOrNull(gf.Names, [gf.Name ?? string.Empty]);
Max = gf.Max;
Since = gf.Since;
StartTimeShift = gf.StartTimeShift;
Regularity = gf.Regularity;
Result = gf.Result;
Group.Add(wsId, gf.Group ?? string.Empty);
UpdateLastGo(lastGroupedObservationObs);
HashCode = CryptoAdas.CreateMd5GroupedObs(gf, patientId);
Timer = new Timer
{
Interval = SetUpTimerInterval()
};
Timer.Elapsed += Timer_Elapsed;
if (Regularity != GroupedObservationEnum.Regularity.Times) Timer.Start();
}
public List<string> WsSubscriber { get; set; } = [];
public ObjectId PatientId { get; set; }
public List<string> Names { get; set; }
public int Max { get; set; }
public GroupedObservationEnum.Regularity? Regularity { get; set; }
public List<GroupedObservationEnum.Result> Result { get; set; }
public GroupedObservationEnum.Since Since { get; set; }
public List<string> StartTimeShift { get; set; }
public string HashCode { get; set; }
public string TimeZoneId { get; set; }
public List<GroupedObservationObs> LastGroupedObservationObs { get; set; } = [];
public Dictionary<string, string> Group { get; set; } = new();
public Timer Timer { get; set; }
public void UpdateLastGo(GroupedObservation lastGroupedObservationObs)
{
LastGroupedObservationObs.Clear();
foreach (var name in Names)
{
var lastObs = lastGroupedObservationObs.Observations.LastOrDefault(c => c.Name == name);
if (lastObs != null)
{
LastGroupedObservationObs.Add(lastObs);
}
else
{
lastObs = lastGroupedObservationObs.Observations.LastOrDefault();
if (lastObs != null) LastGroupedObservationObs.Add(lastObs);
}
}
}
private void Timer_Elapsed(object? sender, ElapsedEventArgs e)
{
var currentDateTime = DateTime.Now;
Timer.Stop();
try
{
bool hasCurrentObs;
switch (Regularity)
{
case GroupedObservationEnum.Regularity.Second:
// Filtra observaciones con el mismo segundo actual
hasCurrentObs = LastGroupedObservationObs.Any(obs => obs.Time.Second == currentDateTime.Second
&& obs.Time.Minute == currentDateTime.Minute
&& obs.Time.Hour == currentDateTime.Hour
&& obs.Time.Date == currentDateTime.Date);
break;
case GroupedObservationEnum.Regularity.Minute:
// Filtra observaciones con el mismo minuto actual
hasCurrentObs = LastGroupedObservationObs.Any(obs => obs.Time.Minute == currentDateTime.Minute
&& obs.Time.Hour == currentDateTime.Hour
&& obs.Time.Date == currentDateTime.Date);
break;
case GroupedObservationEnum.Regularity.Day:
// Filtra observaciones con la misma fecha actual (día completo)
hasCurrentObs = LastGroupedObservationObs.Any(obs => obs.Time.Date == currentDateTime.Date);
break;
default:
// Filtra observaciones con la misma hora actual
hasCurrentObs = LastGroupedObservationObs.Any(obs => obs.Time.Hour == currentDateTime.Hour
&& obs.Time.Date == currentDateTime.Date);
break;
}
// Si no hay al menos una observación que coincide, invoca el evento
if (!hasCurrentObs) _sendEvent(this, HashCode);
}
catch (Exception ex)
{
Log.Warning(
$"Exception on WsSubsciptor for patient: {PatientId} message: {ex.Message} exception: {ex}");
}
TimerReestart();
}
private void TimerReestart()
{
Timer.Interval = SetUpTimerInterval();
Timer.Start();
}
private int SetUpTimerInterval()
{
var currentDateTime = DateTime.Now;
int graceTime;
DateTime nextIntervalTime;
switch (Regularity)
{
case GroupedObservationEnum.Regularity.Second:
nextIntervalTime = new DateTime(
currentDateTime.Year,
currentDateTime.Month,
currentDateTime.Day,
currentDateTime.Hour,
currentDateTime.Minute,
currentDateTime.Second
).AddSeconds(1);
graceTime = 500;
break;
case GroupedObservationEnum.Regularity.Minute:
nextIntervalTime = new DateTime(
currentDateTime.Year,
currentDateTime.Month,
currentDateTime.Day,
currentDateTime.Hour,
currentDateTime.Minute,
0
).AddMinutes(1);
graceTime = 30000;
break;
case GroupedObservationEnum.Regularity.Day:
nextIntervalTime = new DateTime(
currentDateTime.Year,
currentDateTime.Month,
currentDateTime.Day,
0,
0,
0
).AddDays(1);
graceTime = 60000;
break;
default:
nextIntervalTime = new DateTime(
currentDateTime.Year,
currentDateTime.Month,
currentDateTime.Day,
currentDateTime.Hour,
0,
0
).AddHours(1);
graceTime = 30000;
break;
}
var timeSpanToNextInterval = nextIntervalTime - currentDateTime;
Log.Information(
"Obs {Obs}, currentDateTime: {CurrentDateTime}, nextIntervalTime: {NextIntervalTime}, timeSpanToNextInterval: {TimeSpanToNextInterval}, timeSpanToNextInterval.TotalMilliseconds+graceTime: {Tt}",
Group, currentDateTime, nextIntervalTime, timeSpanToNextInterval,
(int)timeSpanToNextInterval.TotalMilliseconds + graceTime);
return (int)timeSpanToNextInterval.TotalMilliseconds + graceTime;
}
}
public static class WsSubscriberExtension
{
public static bool Compare(this WsSubscriberGrouped source, GroupedField r, ObjectId patientId)
{
return r.Regularity == source.Regularity &&
r.Since == source.Since &&
CompareStringShiftList(r.StartTimeShift, source.StartTimeShift) &&
r.Max == source.Max &&
r.Result.SequenceEqual(source.Result) &&
patientId.ToString() == source.PatientId.ToString() &&
CompareNamesStringList(r.Names, [r.Name ?? string.Empty], source.Names);
}
private static bool CompareStringShiftList(List<string>? shift, List<string>? sourceShift)
{
if (shift != null && sourceShift != null) return sourceShift.SequenceEqual(shift);
if (shift == null && sourceShift == null) return true;
return false;
}
private static bool CompareNamesStringList(List<string> names, List<string> name, List<string> sourceNames)
{
if (!CollectionsUtils.IsEmptyOrNull(names) && sourceNames.SequenceEqual(names)) return true;
return !CollectionsUtils.IsEmptyOrNull(name) && sourceNames.SequenceEqual(name);
}
/// <summary>
/// Check if the incoming observation affect to the group and is new info or is irrelevant
/// </summary>
/// <returns></returns>
public static bool IsNewObservationRelevantForGroup(this WsSubscriberGrouped group, PatientObservation obs)
{
double.TryParse(obs.Value.ToString(), out var pobsValue);
switch (group.Regularity)
{
case GroupedObservationEnum.Regularity.Shift:
case GroupedObservationEnum.Regularity.Hour:
var obsInThisHour = group.LastGroupedObservationObs.Where(c =>
new DateTime(c.Time.Year, c.Time.Month, c.Time.Day, c.Time.Hour, 0, 0) ==
new DateTime(obs.Time.Year, obs.Time.Month, obs.Time.Day, obs.Time.Hour, 0, 0) && !c.IsFilled);
//Obs is in grouped time, don't generate group for observation outside of range
//Change to don't cut is obs time is a future
var obsInTime = DateTime.UtcNow.AddHours(group.Max) > obs.Time &&
DateTime.UtcNow.AddHours(group.Max * -1) < obs.Time;
//future data is relevant because maybe nothing is inserted in the same hour after it and we cant lose it
/* if (DateTime.UtcNow.AddHours(group.Max) > obs.time)
{
}
*/
if (!obsInTime) return false;
var groupedObservationObsEnumerable = obsInThisHour.ToList();
if (!groupedObservationObsEnumerable.Any() && obsInTime) return true;
//If obs in this hour check if is relevant by Result
if (groupedObservationObsEnumerable.Any())
foreach (var result in group.Result)
switch (result)
{
case GroupedObservationEnum.Result.Max:
var value = groupedObservationObsEnumerable.Max(s => s.Max?.Value);
var parsed = double.TryParse(value?.ToString(), out var valueParsed);
//actual value is higher, is relevant
if (parsed && pobsValue > valueParsed) return true;
break;
case GroupedObservationEnum.Result.Min:
var minValue = groupedObservationObsEnumerable.Min(s => s.Min?.Value);
var isParsed = double.TryParse(minValue?.ToString(), out var minValueParsed);
//actual value is higher, is relevant
if (isParsed && pobsValue < minValueParsed) return true;
break;
case GroupedObservationEnum.Result.Last:
var lastObsInTime = groupedObservationObsEnumerable.OrderBy(t => t.Last?.Time).First();
//is the new latest in the hour, relevant
if (lastObsInTime.Time < obs.Time) return true;
break;
case GroupedObservationEnum.Result.LastFilled:
var lastFilledObsInTime = groupedObservationObsEnumerable
.OrderBy(t => t.LastFilled?.Time).First();
//is the new latest in the hour, relevant
if (lastFilledObsInTime.Time < obs.Time) return true;
break;
//always is relevant because SUM everything in the hour
//TODO halfHour should check if any obs already in the halfHour
case GroupedObservationEnum.Result.Sum:
case GroupedObservationEnum.Result.Average:
case GroupedObservationEnum.Result.HalfHour:
return true;
}
return false;
default:
return true;
}
}
}