diff --git a/modules/client-simulation/src/Volo.ClientSimulation.Web/Pages/ClientSimulation/SimulationArea.cshtml b/modules/client-simulation/src/Volo.ClientSimulation.Web/Pages/ClientSimulation/SimulationArea.cshtml
index aa8de38b3f..c63baf4af5 100644
--- a/modules/client-simulation/src/Volo.ClientSimulation.Web/Pages/ClientSimulation/SimulationArea.cshtml
+++ b/modules/client-simulation/src/Volo.ClientSimulation.Web/Pages/ClientSimulation/SimulationArea.cshtml
@@ -8,18 +8,18 @@
@model Volo.ClientSimulation.Pages.ClientSimulation.SimulationAreaModel
- @Model.Simulation.State
+ @Model.Snapshot.State
- @foreach (var client in Model.Simulation.Clients)
+ @foreach (var client in Model.Snapshot.Clients)
{
@@ -37,12 +37,12 @@
}
- @client.Scenario.GetDisplayText()
+ @client.Scenario.DisplayText
@if (client.State != ClientState.Stopped)
{
- | @client.Scenario.CurrentStep.GetDisplayText()
+ | @client.Scenario.CurrentStep.DisplayText
}
diff --git a/modules/client-simulation/src/Volo.ClientSimulation.Web/Pages/ClientSimulation/SimulationArea.cshtml.cs b/modules/client-simulation/src/Volo.ClientSimulation.Web/Pages/ClientSimulation/SimulationArea.cshtml.cs
index 6faa48d538..e55f504f10 100644
--- a/modules/client-simulation/src/Volo.ClientSimulation.Web/Pages/ClientSimulation/SimulationArea.cshtml.cs
+++ b/modules/client-simulation/src/Volo.ClientSimulation.Web/Pages/ClientSimulation/SimulationArea.cshtml.cs
@@ -1,12 +1,15 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
+using Volo.ClientSimulation.Snapshot;
namespace Volo.ClientSimulation.Pages.ClientSimulation
{
public class SimulationAreaModel : PageModel
{
- public Simulation Simulation { get; }
+ public SimulationSnapshot Snapshot { get; private set; }
+
+ protected Simulation Simulation { get; }
public SimulationAreaModel(Simulation simulation)
{
@@ -15,6 +18,7 @@ namespace Volo.ClientSimulation.Pages.ClientSimulation
public Task OnGetAsync()
{
+ Snapshot = Simulation.CreateSnapshot();
return Task.CompletedTask;
}
diff --git a/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Clients/Client.cs b/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Clients/Client.cs
index 3d4f303799..b56f733f6f 100644
--- a/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Clients/Client.cs
+++ b/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Clients/Client.cs
@@ -1,13 +1,17 @@
-using System.Threading;
+using System;
+using System.Threading;
using Volo.Abp;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Threading;
using Volo.ClientSimulation.Scenarios;
+using Volo.ClientSimulation.Snapshot;
namespace Volo.ClientSimulation.Clients
{
public class Client : IClient, ITransientDependency
{
+ public event EventHandler Stopped;
+
public Scenario Scenario { get; private set; }
public ClientState State
@@ -23,7 +27,15 @@ namespace Volo.ClientSimulation.Clients
public void Initialize(Scenario scenario)
{
- Scenario = scenario;
+ lock (SyncLock)
+ {
+ if (State != ClientState.Stopped)
+ {
+ throw new UserFriendlyException($"Client should be stopped to be able to initialize it. Current state is '{State}'.");
+ }
+
+ Scenario = scenario;
+ }
}
public void Start()
@@ -37,6 +49,7 @@ namespace Volo.ClientSimulation.Clients
State = ClientState.Running;
+ Scenario.Reset();
_thread = new Thread(Run);
_thread.Start();
}
@@ -55,10 +68,20 @@ namespace Volo.ClientSimulation.Clients
}
}
- private void Run()
+ public ClientSnapshot CreateSnapshot()
{
- Scenario.Reset();
+ lock (SyncLock)
+ {
+ return new ClientSnapshot
+ {
+ State = State,
+ Scenario = Scenario.CreateSnapshot()
+ };
+ }
+ }
+ private void Run()
+ {
while (true)
{
lock (SyncLock)
@@ -66,6 +89,8 @@ namespace Volo.ClientSimulation.Clients
if (State != ClientState.Running)
{
State = ClientState.Stopped;
+ _thread = null;
+ Stopped.InvokeSafely(this);
break;
}
}
diff --git a/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Clients/IClient.cs b/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Clients/IClient.cs
index 790c1df553..918dcc5d34 100644
--- a/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Clients/IClient.cs
+++ b/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Clients/IClient.cs
@@ -1,9 +1,13 @@
-using Volo.ClientSimulation.Scenarios;
+using System;
+using Volo.ClientSimulation.Scenarios;
+using Volo.ClientSimulation.Snapshot;
namespace Volo.ClientSimulation.Clients
{
public interface IClient
{
+ event EventHandler Stopped;
+
Scenario Scenario { get; }
ClientState State { get; }
@@ -13,5 +17,7 @@ namespace Volo.ClientSimulation.Clients
void Start();
void Stop();
+
+ ClientSnapshot CreateSnapshot();
}
}
\ No newline at end of file
diff --git a/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Scenarios/Scenario.cs b/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Scenarios/Scenario.cs
index ec35580a43..b75542557f 100644
--- a/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Scenarios/Scenario.cs
+++ b/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Scenarios/Scenario.cs
@@ -1,29 +1,30 @@
using System;
using System.Collections.Generic;
-using System.Collections.Immutable;
+using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
+using Volo.ClientSimulation.Snapshot;
namespace Volo.ClientSimulation.Scenarios
{
public abstract class Scenario : ITransientDependency
{
- public IReadOnlyList Steps => StepList.ToImmutableList();
- protected List StepList { get; }
+ protected List Steps { get; }
- public ScenarioStep CurrentStep
+ protected ScenarioStep CurrentStep
{
get
{
CheckStepCount();
- return StepList[CurrentStepIndex];
+ return Steps[CurrentStepIndex];
}
}
- public int CurrentStepIndex { get; protected set; }
+
+ protected int CurrentStepIndex { get; set; }
protected Scenario()
{
- StepList = new List();
+ Steps = new List();
}
public virtual string GetDisplayText()
@@ -37,11 +38,11 @@ namespace Volo.ClientSimulation.Scenarios
{
CheckStepCount();
- await StepList[CurrentStepIndex].RunAsync();
+ await Steps[CurrentStepIndex].RunAsync();
CurrentStepIndex++;
- if (CurrentStepIndex >= StepList.Count)
+ if (CurrentStepIndex >= Steps.Count)
{
CurrentStepIndex = 0;
}
@@ -52,19 +53,29 @@ namespace Volo.ClientSimulation.Scenarios
CurrentStepIndex = 0;
}
+ public ScenarioSnapshot CreateSnapshot()
+ {
+ return new ScenarioSnapshot
+ {
+ DisplayText = GetDisplayText(),
+ Steps = Steps.Select(s => s.CreateSnapshot()).ToList(),
+ CurrentStep = CurrentStep.CreateSnapshot()
+ };
+ }
+
+ protected void AddStep(ScenarioStep step)
+ {
+ Steps.Add(step);
+ }
+
private void CheckStepCount()
{
- if (StepList.Count <= 0)
+ if (Steps.Count <= 0)
{
throw new ApplicationException(
$"No Steps added to the scenario '{GetDisplayText()}'"
);
}
}
-
- protected void AddStep(ScenarioStep step)
- {
- StepList.Add(step);
- }
}
}
diff --git a/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Scenarios/ScenarioStep.cs b/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Scenarios/ScenarioStep.cs
index e21c6d4cd1..53df153fc5 100644
--- a/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Scenarios/ScenarioStep.cs
+++ b/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Scenarios/ScenarioStep.cs
@@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using System.Threading.Tasks;
+using Volo.ClientSimulation.Snapshot;
namespace Volo.ClientSimulation.Scenarios
{
@@ -46,5 +47,13 @@ namespace Volo.ClientSimulation.Scenarios
.Name
.RemovePostFix(nameof(ScenarioStep));
}
+
+ public ScenarioStepSnapshot CreateSnapshot()
+ {
+ return new ScenarioStepSnapshot
+ {
+ DisplayText = GetDisplayText()
+ };
+ }
}
}
\ No newline at end of file
diff --git a/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Simulation.cs b/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Simulation.cs
index 6d858d7dc0..3684965bbc 100644
--- a/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Simulation.cs
+++ b/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Simulation.cs
@@ -1,23 +1,29 @@
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; private set; }
public List Clients { get; }
protected ClientSimulationOptions Options { get; }
-
- protected IServiceScope ServiceScope { get; }
+ protected IServiceScopeFactory ServiceScopeFactory { get; }
+ protected IServiceScope ServiceScope { get; private set; }
protected readonly object SyncObj = new object();
@@ -25,27 +31,15 @@ namespace Volo.ClientSimulation
IServiceScopeFactory serviceScopeFactory,
IOptions options)
{
+ ServiceScopeFactory = serviceScopeFactory;
Options = options.Value;
- ServiceScope = serviceScopeFactory.CreateScope();
- Clients = new List();
+ Logger = NullLogger.Instance;
- 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.Initialize(scenario);
- Clients.Add(client);
- }
- }
+ Clients = new List();
}
- public void Start()
+ public virtual void Start()
{
lock (SyncObj)
{
@@ -56,16 +50,42 @@ namespace Volo.ClientSimulation
State = SimulationState.Starting;
- foreach (var client in Clients)
+ try
{
- client.Start();
+ 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;
}
-
- State = SimulationState.Started;
}
}
- public void Stop()
+ public virtual void Stop()
{
lock (SyncObj)
{
@@ -80,14 +100,48 @@ namespace Volo.ClientSimulation
{
client.Stop();
}
+ }
+ }
- State = SimulationState.Stopped;
+ public virtual SimulationSnapshot CreateSnapshot()
+ {
+ lock (SyncObj)
+ {
+ return new SimulationSnapshot
+ {
+ State = State,
+ Clients = Clients
+ .Select(c => c.CreateSnapshot())
+ .ToList()
+ };
+ }
+ }
+
+ 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();
+ }
}
}
- public void Dispose()
+ private void OnStopped()
+ {
+ State = SimulationState.Stopped;
+ }
+
+ private void DisposeResources()
{
- ServiceScope.Dispose();
+ Clients.Clear();
+ ServiceScope?.Dispose();
}
}
}
diff --git a/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Snapshot/ClientSnapshot.cs b/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Snapshot/ClientSnapshot.cs
new file mode 100644
index 0000000000..457762d536
--- /dev/null
+++ b/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Snapshot/ClientSnapshot.cs
@@ -0,0 +1,13 @@
+using System;
+using Volo.ClientSimulation.Clients;
+
+namespace Volo.ClientSimulation.Snapshot
+{
+ [Serializable]
+ public class ClientSnapshot
+ {
+ public ClientState State { get; set; }
+
+ public ScenarioSnapshot Scenario { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Snapshot/ScenarioSnapshot.cs b/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Snapshot/ScenarioSnapshot.cs
new file mode 100644
index 0000000000..9965a06dad
--- /dev/null
+++ b/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Snapshot/ScenarioSnapshot.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+
+namespace Volo.ClientSimulation.Snapshot
+{
+ [Serializable]
+ public class ScenarioSnapshot
+ {
+ public string DisplayText { get; set; }
+
+ public List Steps { get; set; }
+
+ public ScenarioStepSnapshot CurrentStep { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Snapshot/ScenarioStepSnapshot.cs b/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Snapshot/ScenarioStepSnapshot.cs
new file mode 100644
index 0000000000..3e4363eda1
--- /dev/null
+++ b/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Snapshot/ScenarioStepSnapshot.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Volo.ClientSimulation.Snapshot
+{
+ [Serializable]
+ public class ScenarioStepSnapshot
+ {
+ public string DisplayText { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Snapshot/SimulationSnapshot.cs b/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Snapshot/SimulationSnapshot.cs
new file mode 100644
index 0000000000..0fe835e0aa
--- /dev/null
+++ b/modules/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Snapshot/SimulationSnapshot.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+
+namespace Volo.ClientSimulation.Snapshot
+{
+ [Serializable]
+ public class SimulationSnapshot
+ {
+ public SimulationState State { get; set; }
+
+ public List Clients { get; set; }
+ }
+}