Files
vigilcare-clinical/VigilCareClinicalAPI/Services/Simulation/SimulationRunState.cs
T
voltsrage 24f45851e9
CI / frontend (push) Canceled after 0s
CI / backend (push) Canceled after 8m32s
feature: In-App Simulation Runner (Backend)
2026-08-06 01:52:53 +08:00

127 lines
4.1 KiB
C#

public sealed class SimulationRunState
{
private readonly object _gate = new();
private DateTimeOffset? _completedAt;
public Guid RunId { get; init; }
public string ScenarioId { get; init; } = null!;
public string ScenarioName { get; init; } = null!;
public double Speed { get; init; }
public string StartedByUserId { get; init; } = null!;
public DateTimeOffset StartedAt { get; init; }
public double TotalOffsetMinutes { get; init; }
public string PatientDisplayName { get; init; } = null!;
public SimulationRunStatus Status { get; private set; } = SimulationRunStatus.Pending;
public Guid? PatientId { get; private set; }
public Guid? EncounterId { get; private set; }
public int ObservationsSent { get; private set; }
public int MedicationsSent { get; private set; }
public int OrdersPlaced { get; private set; }
public double LastOffsetMinutes { get; private set; }
public double ProgressPercent { get; private set; }
public string? FailureReason { get; private set; }
public double ElapsedRealSeconds
{
get
{
lock (_gate)
{
var end = _completedAt ?? DateTimeOffset.UtcNow;
return (end - StartedAt).TotalSeconds;
}
}
}
public void MarkRunning()
{
lock (_gate) Status = SimulationRunStatus.Running;
}
public void SetIds(Guid? patientId, Guid? encounterId)
{
lock (_gate)
{
if (patientId.HasValue) PatientId = patientId;
if (encounterId.HasValue) EncounterId = encounterId;
}
}
public void UpdateOffset(double offsetMinutes)
{
lock (_gate)
{
LastOffsetMinutes = offsetMinutes;
ProgressPercent = TotalOffsetMinutes <= 0
? 100
: Math.Clamp(offsetMinutes / TotalOffsetMinutes * 100.0, 0, 100);
}
}
public void ApplyResultCounters(int observationsSent, int medicationsSent, int ordersPlaced)
{
lock (_gate)
{
ObservationsSent = observationsSent;
MedicationsSent = medicationsSent;
OrdersPlaced = ordersPlaced;
}
}
public void NoteEvent(string description)
{
lock (_gate)
{
if (description.StartsWith("MEDICATION", StringComparison.Ordinal))
MedicationsSent++;
else if (description.StartsWith("ORDER ", StringComparison.Ordinal))
OrdersPlaced++;
else if (!description.StartsWith("ORDER_RESULT", StringComparison.Ordinal)
&& !description.StartsWith("ACK ", StringComparison.Ordinal))
ObservationsSent++;
}
}
public void MarkTerminal(SimulationRunStatus status, string? failureReason = null)
{
lock (_gate)
{
Status = status;
FailureReason = failureReason;
_completedAt = DateTimeOffset.UtcNow;
if (status == SimulationRunStatus.Completed)
ProgressPercent = 100;
}
}
public SimulationRunState Snapshot()
{
lock (_gate)
{
var copy = new SimulationRunState
{
RunId = RunId,
ScenarioId = ScenarioId,
ScenarioName = ScenarioName,
Speed = Speed,
StartedByUserId = StartedByUserId,
StartedAt = StartedAt,
TotalOffsetMinutes = TotalOffsetMinutes,
PatientDisplayName = PatientDisplayName,
};
copy.Status = Status;
copy.PatientId = PatientId;
copy.EncounterId = EncounterId;
copy.ObservationsSent = ObservationsSent;
copy.MedicationsSent = MedicationsSent;
copy.OrdersPlaced = OrdersPlaced;
copy.LastOffsetMinutes = LastOffsetMinutes;
copy.ProgressPercent = ProgressPercent;
copy.FailureReason = FailureReason;
copy._completedAt = _completedAt;
return copy;
}
}
}