using System; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Volo.Abp; using Volo.Abp.DependencyInjection; using Volo.ClientSimulation.Clients; using Volo.ClientSimulation.Scenarios; using Volo.ClientSimulation.Snapshot; namespace Volo.ClientSimulation; public class Simulation : ISingletonDependency, IDisposable { public ILogger Logger { get; set; } public SimulationState State { get => _state; private set => _state = value; } private volatile SimulationState _state; public List Clients { get; } protected ClientSimulationOptions Options { get; } protected IServiceScopeFactory ServiceScopeFactory { get; } protected IServiceScope ServiceScope { get; private set; } protected object SyncObj { get; } = new object(); public Simulation( IServiceScopeFactory serviceScopeFactory, IOptions options) { ServiceScopeFactory = serviceScopeFactory; Options = options.Value; Logger = NullLogger.Instance; Clients = new List(); } public virtual void Start() { lock (SyncObj) { if (State != SimulationState.Stopped) { throw new UserFriendlyException($"Simulation should be stopped to be able to start. Current state is '{State}'."); } State = SimulationState.Starting; try { DisposeResources(); ServiceScope = ServiceScopeFactory.CreateScope(); foreach (var scenarioConfiguration in Options.Scenarios) { for (int i = 0; i < scenarioConfiguration.ClientCount; i++) { var scenario = (Scenario)ServiceScope.ServiceProvider.GetRequiredService( scenarioConfiguration.ScenarioType ); var client = ServiceScope.ServiceProvider.GetRequiredService(); client.Stopped += Client_OnStopped; client.Initialize(scenario); Clients.Add(client); } } foreach (var client in Clients) { client.Start(); } State = SimulationState.Started; } catch (Exception ex) { Logger.LogException(ex); State = SimulationState.Stopped; } } } public virtual void Stop() { lock (SyncObj) { if (State != SimulationState.Started) { throw new UserFriendlyException($"Simulation should be started to be able to stop. Current state is '{State}'."); } State = SimulationState.Stopping; foreach (var client in Clients) { client.Stop(); } } } public virtual SimulationSnapshot CreateSnapshot() { SimulationSnapshot snapshot; lock (SyncObj) { snapshot = new SimulationSnapshot { State = State, Clients = Clients .Select(c => c.CreateSnapshot()) .ToList() }; } snapshot.CreateSummaries(); return snapshot; } public virtual void Dispose() { DisposeResources(); } protected virtual void Client_OnStopped(object sender, EventArgs e) { lock (SyncObj) { if (Clients.All(c => c.State == ClientState.Stopped)) { OnStopped(); } } } private void OnStopped() { State = SimulationState.Stopped; } private void DisposeResources() { Clients.Clear(); ServiceScope?.Dispose(); } }