Files

441 lines
18 KiB
C#

using System.Net;
using System.Net.Http.Headers;
using System.Text;
using adas_core.Application.Services.Interfaces;
using adas_core.Domain.Enums;
using adas_core.Domain.Models;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.DTO;
using adas_core.Domain.Models.MongoModels;
using adas_core.Domain.Models.Recording;
using adas_core.Domain.Models.Responses;
using adas_core.Domain.Utils;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Serilog;
using Patient = adas_core.Domain.Models.MongoModels.Patient;
namespace adas_core.Application.Services;
public class RecordingService : IRecordingService
{
private readonly IAuthService _authService;
private readonly IClientMessageService _clientMessageService;
private readonly HttpClient _httpClient;
private readonly ILogger<RecordingService> _logger;
private readonly Lazy<IPatientService> _patientService;
private readonly IPublisherService _publisherService;
private readonly RabbitMqSettings _rabbitMqSettings;
private readonly RecordingSettings _recordingSettings;
private readonly bool _startRecordingWithoutPatientNumber;
private readonly ISubscribersService _subscribersService;
private readonly string? _url;
private AccessGrant? _accessGrant;
public RecordingService(IOptions<RabbitMqSettings> rabbitMqSettings,
IOptions<RecordingSettings> recordingSettings,
ILogger<RecordingService> logger,
IHttpClientFactory httpClientFactory,
IPublisherService publisherService,
IAuthService authService,
IOptions<ApiSettings> apiSettings, IClientMessageService clientMessageService,
ISubscribersService subscribersService,
Lazy<IPatientService> patientService)
{
_logger = logger;
_httpClient = httpClientFactory.CreateClient();
_accessGrant ??= new AccessGrant();
_rabbitMqSettings = rabbitMqSettings.Value;
_recordingSettings = recordingSettings.Value ??
throw new Exception("RecordingSettings must be defined on appSettings");
_httpClient.Timeout = new TimeSpan(0, 0, _recordingSettings.HttpClientTimeout);
_url = _recordingSettings.RecordingApiUrl;
_publisherService = publisherService;
_authService = authService;
_clientMessageService = clientMessageService;
_subscribersService = subscribersService;
_patientService = patientService;
RecordingQueueName = _rabbitMqSettings.RecordingQueue;
ErrorRecordingQueueName = $"{RecordingQueueName}_Error";
_startRecordingWithoutPatientNumber = apiSettings.Value.StartRecordingWithoutPatientNumber;
}
private string RecordingQueueName { get; }
private string ErrorRecordingQueueName { get; }
public async Task<bool> SendCancelRecordingToRecordingApi(Patient patient, PointOfCare poc)
{
var token = await _authService.GetToken();
if (string.IsNullOrEmpty(_url) || string.IsNullOrEmpty(token))
throw new Exception(
"send recording data to recording api require RecordingOrApiUrl defined and valid token");
var body = JsonConvert.SerializeObject(GenerateRecordingData(patient, poc, DateTime.MinValue,
null, null, null, null, AlarmEnum.Severity.None, ""));
var request =
new HttpRequestMessage(HttpMethod.Post,
$"{_url}/videos/delete-video-in-progress") //las fechas no se mandan
{
Content = new StringContent(body, Encoding.UTF8)
};
request.Headers.TryAddWithoutValidation("Authorization", $"Bearer {token}");
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await _httpClient.SendAsync(request);
_logger.LogDebug(
"send cancel to recording api status code:{responseStatusCode} url: {url} body: {body}",
response.StatusCode, _url, body);
return response.IsSuccessStatusCode;
}
public async Task SendRecordingData(Patient patient, PointOfCare poc, ManualRecording manualRecording,
bool start)
{
await SendRecordingDataToQueue(patient, poc, manualRecording.Recording?.StartRecordingTime,
manualRecording.Recording?.StopRecordingTime, null, null, AlarmEnum.Severity.None, null, start);
}
public async Task<bool> SendAutoRecordingData(Patient patient, PointOfCare poc, RecordingData automaticRecording)
{
try
{
if (!Enum.TryParse<AlarmEnum.Name>(automaticRecording.AlarmName, out var alarmName))
return false;
await SendRecordingDataToQueue(patient, poc, automaticRecording.StartRecordingTime,
automaticRecording.StopRecordingTime, automaticRecording.EventTime,
alarmName, automaticRecording.Severity, automaticRecording.AlarmDescription,
type: AlarmEnum.Type.Auto);
return true;
}
catch (Exception ex)
{
Log.Error("Exception sending automatic recording data. Exception: {ex}", ex);
return false;
}
}
public async Task SendRecordingDataToQueue(Patient patient, PointOfCare poc, DateTime? date, DateTime? endDate,
DateTime? eventDate, AlarmEnum.Name? alarmName, AlarmEnum.Severity severity, string? alarmDescription,
bool start = true, AlarmEnum.Type type = AlarmEnum.Type.Manual)
{
if (!_startRecordingWithoutPatientNumber && string.IsNullOrEmpty(patient.PatientNumber))
{
_logger.LogError(
"Error sending recording data to queue. Patient number is null or empty. PatientId: {patientId}",
patient.PatientId);
return;
}
if (poc.Status != StatusEnum.PointOfCare.InUse)
throw new Exception($"Box does not contain patients:->{JsonConvert.SerializeObject(poc)}");
if (string.IsNullOrEmpty(alarmDescription)) alarmDescription = "UNKNOWN";
var retryCount = 1;
while (retryCount <= _rabbitMqSettings.MaxRetries)
{
try
{
ApiRequest recordingRequest = new()
{
Recording = GenerateRecordingData(patient, poc, date, endDate, eventDate, null, alarmName,
severity, alarmDescription, type)
};
_logger.LogDebug("Send recording data patient {patient}", patient);
await _publisherService.SendMessage(recordingRequest, RecordingQueueName);
if (recordingRequest.Recording != null)
_ = SendRecordingBroadcast([recordingRequest.Recording]);
return;
}
catch (HttpRequestException ce)
{
retryCount++;
_logger.LogError("Connection error sending recording data to queue. {ce} Retrying...", ce.Message);
}
catch (Exception e)
{
_logger.LogError("Error sending recording data to queue.{eError} {eMessage} ", e, e.Message);
await _publisherService.SendMessage(e.Message, ErrorRecordingQueueName);
throw;
}
if (retryCount == _rabbitMqSettings.MaxRetries)
await _publisherService.SendMessage("Error: Connection retries exceeded.", ErrorRecordingQueueName);
}
}
public async Task<List<RecordingData>?> GetRecordings(int roomId)
{
var retryCount = 1;
while (retryCount <= _rabbitMqSettings.MaxRetries)
try
{
var token = await _authService.GetToken();
if (string.IsNullOrEmpty(_recordingSettings.RecordingApiUrl) || string.IsNullOrEmpty(token))
{
Log.Warning(
"Not recording url defined at recordingSettings or empty token, ignoring recordings");
return null;
}
var request = new HttpRequestMessage(HttpMethod.Get,
$"{_recordingSettings.RecordingApiUrl}/videos/allInProgress/{roomId}");
request.Headers.TryAddWithoutValidation("Authorization", $"Bearer {token}");
var response = await _httpClient.SendAsync(request);
_logger.LogDebug(
"GetByCodeSysAndCode recordings to recording api status code:{responseStatusCode} url: {url} body",
response.StatusCode, _url);
var responseContent = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
_accessGrant = null;
_logger.LogError(
"error connecting to RecordingOrApi url:{requestRequestUri} errorCode: {responseStatusCode} error {responseContent} token: {}",
request.RequestUri, response.StatusCode, response.Content,
_accessGrant?.AccessToken ?? "not exist");
}
else if (response.StatusCode == HttpStatusCode.NotFound)
{
return [];
}
else
{
_logger.LogError(
"error connecting to RecordingOrApi url:{requestRequestUri} errorCode: {responseStatusCode} error {responseContent}",
request.RequestUri, response.StatusCode, response.Content);
return [];
}
if (retryCount == _rabbitMqSettings.MaxRetries)
await _publisherService.SendMessage("Error: Connection retries exceeded.",
ErrorRecordingQueueName);
return [];
}
var records = JsonConvert.DeserializeObject<List<RecordingData>>(responseContent);
return records;
}
catch (HttpRequestException ce)
{
retryCount++;
_logger.LogError(
"Connection error sending recording data to queue. {ce} Retrying... {retryCount} of {maxRetrys}",
ce.Message, retryCount, _rabbitMqSettings.MaxRetries);
//return new List<RecordingData>();
}
catch (Exception e)
{
_logger.LogError("Error getting recordings room: {roomId} error: {eMessage} ", roomId, e.Message);
await _publisherService.SendMessage(e.Message, ErrorRecordingQueueName);
return [];
}
if (retryCount == _rabbitMqSettings.MaxRetries)
{
await _publisherService.SendMessage("Error: Connection retries exceeded.", ErrorRecordingQueueName);
return null;
}
return [];
}
public async Task SaveRequestAsync(ApiRequest apiRequest)
{
var retryCount = 1;
while (retryCount <= _rabbitMqSettings.MaxRetries)
{
try
{
var token = await _authService.GetToken();
_logger.LogDebug("sending recording to Recording or api");
var recordingRequest = apiRequest.Recording;
var request = new HttpRequestMessage(HttpMethod.Post,
$"{_recordingSettings.RecordingApiUrl}/videos/save-recording-data")
{
Content = new StringContent(JsonConvert.SerializeObject(recordingRequest), Encoding.UTF8)
};
request.Headers.TryAddWithoutValidation("Authorization", $"Bearer {token}");
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await _httpClient.SendAsync(request);
_logger.LogDebug("Save Request to recording api status code:{responseStatusCode}",
response.StatusCode);
var responseContent = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
if (response.StatusCode == HttpStatusCode.Unauthorized) _accessGrant = null;
_logger.LogError(
"error connecting to RecordingOrApi url:{requestRequestUri} errorCode: {responseStatusCode} error {responseContent}",
request.RequestUri, response.StatusCode, response.Content);
retryCount++;
}
else
{
// Response y en Data esta el videoDTO
var resp = JsonConvert.DeserializeObject<Response<VideoDto>>(responseContent);
var video = resp?.Data;
if (video == null)
return;
RecordingData recording = new()
{
StartRecordingTime = video.StartDate,
StopRecordingTime = video.EndDate,
EventTime = video.Date,
RoomId = video.RoomId,
AlarmType = video.Alarm,
Store = video.Store,
Status = video.VideoStatus.ToString(),
Patient = new Domain.Models.Recording.Patient
{
FirstName = apiRequest.Patient?.FirstName ??
apiRequest.Recording?.Patient?.FirstName ?? string.Empty,
Id = apiRequest.PatientNumber ?? apiRequest.Recording?.Patient?.Id ?? string.Empty,
LastName = apiRequest.Patient?.LastName ??
apiRequest.Recording?.Patient?.LastName ?? string.Empty
}
};
_ = SendRecordingBroadcast([recording]);
return;
}
}
catch (HttpRequestException ce)
{
retryCount++;
_logger.LogError("Connection error sending recording data to queue. {ce} Retrying...", ce.Message);
await _publisherService.SendMessage(apiRequest, ErrorRecordingQueueName);
}
catch (Exception e)
{
_logger.LogError("Error sending recording request to Recording or {eMessage}", e.Message);
await _publisherService.SendMessage(apiRequest, ErrorRecordingQueueName);
await Task.FromException(e);
}
if (retryCount > _rabbitMqSettings.MaxRetries)
await _publisherService.SendMessage(
$"Error: Connection retries exceeded. for api request: {apiRequest}", ErrorRecordingQueueName);
}
}
public Task SaveRequest(ApiRequest apiRequest)
{
//return Task.CompletedTask;
throw new NotImplementedException();
}
private RecordingData? GenerateRecordingData(Patient patient, PointOfCare poc, DateTime? startDate,
DateTime? endDate, DateTime? eventTime, int? minToExpired, AlarmEnum.Name? alarmName,
AlarmEnum.Severity severity,
string alarmDescription, AlarmEnum.Type alarm = AlarmEnum.Type.Manual)
{
if (patient.PatientNumber == null)
{
_logger.LogError("Error Generating Recording Data . Patient number is null: {patient}", patient);
return null;
}
var roomId = poc.Configuration?.Id;
if (roomId == null)
{
_logger.LogError("Error Generating Recording Data on roomId");
return null;
}
var patientRecording = new Domain.Models.Recording.Patient
{
Id = patient.PatientNumber,
FirstName = patient.Person?.FirstName ?? "X",
LastName = $"{patient.Person?.SecondName} {patient.Person?.LastName}".TrimEnd()
};
if (!int.TryParse(roomId.ToString(), out var roomIdParsed))
{
_logger.LogError("Error Generating Recording Data on roomId parse: {roomId}", roomId);
return null;
}
var recordingData = new RecordingData
{
Patient = patientRecording,
RoomId = roomIdParsed,
StartRecordingTime = startDate,
StopRecordingTime =
endDate ?? (minToExpired != null ? DateTime.UtcNow.AddMinutes(minToExpired.Value) : null),
EventTime = eventTime,
AlarmType = alarm,
Severity = severity,
AlarmName = alarmName.HasValue ? EnumUtils.GetDescription(alarmName) : null,
AlarmDescription = alarmDescription,
Retry = 0
};
recordingData.GenerateStore();
return recordingData;
}
private async Task SendRecordingBroadcast(List<RecordingData> recording)
{
if (!recording.IsNullOrEmpty())
{
var patientNumber = recording[0].Patient?.Id;
if (string.IsNullOrEmpty(patientNumber))
{
_logger.LogError("Box not found by RoomId {roomId}", recording[0].RoomId);
return;
}
var patient = await _patientService.Value.FindByPatientNumber(patientNumber);
if (patient == null)
return;
var type = OperationType.RecordingStatus;
var subscribers = _subscribersService.GetSubscribers().Where(s =>
s.SubscriptionType == SubscriptionEnum.WsType.Box && s.Box == patient.Location.Bed &&
s.Section == patient.Location.UnitName).ToList();
foreach (var subscriber in subscribers)
await _clientMessageService.SendAsync(subscriber.Id, type, recording);
}
}
protected static string Base64Encode(string plainText)
{
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
return Convert.ToBase64String(plainTextBytes);
}
}