441 lines
18 KiB
C#
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);
|
|
}
|
|
} |