diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/RandomHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/RandomHelper.cs
index 97992ad27d..361173387e 100644
--- a/framework/src/Volo.Abp.Core/Volo/Abp/RandomHelper.cs
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/RandomHelper.cs
@@ -77,7 +77,7 @@ namespace Volo.Abp
///
/// Type of the objects
/// List of object to select a random one
- public static T GetRandomOf([NotNull] IList list)
+ public static T GetRandomOfList([NotNull] IList list)
{
Check.NotNullOrEmpty(list, nameof(list));
diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/Index.cshtml b/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/Index.cshtml
index 59a0e0e435..48292e6dd0 100644
--- a/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/Index.cshtml
+++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/Index.cshtml
@@ -1,7 +1,11 @@
@page
@model Volo.ClientSimulation.Web.Pages.IndexModel
-@section scripts
- {
+@section styles {
+
+
+
+}
+@section scripts {
diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/SimulationArea.cshtml b/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/SimulationArea.cshtml
index 97dca18d7e..fb97b9be73 100644
--- a/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/SimulationArea.cshtml
+++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/SimulationArea.cshtml
@@ -17,6 +17,11 @@
- Active client count: @Model.Simulation.ActiveClients.Count
+ @foreach (var clientHandler in Model.Simulation.ActiveClients)
+ {
+
+ [@clientHandler.Client.State] @clientHandler.Client.Scenario.GetDisplayText(): @clientHandler.Client.Scenario.CurrentStep.GetDisplayText()
+
+ }
\ No newline at end of file
diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/SimulationArea.css b/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/SimulationArea.css
new file mode 100644
index 0000000000..30cd9cb44e
--- /dev/null
+++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/SimulationArea.css
@@ -0,0 +1,5 @@
+.simulation-client {
+ border: 1px solid #008000;
+ background-color: #d6ffce;
+ margin: 3px;
+ padding: 5px; }
diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/SimulationArea.min.css b/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/SimulationArea.min.css
new file mode 100644
index 0000000000..b5120f66b5
--- /dev/null
+++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/SimulationArea.min.css
@@ -0,0 +1 @@
+.simulation-client{border:1px solid #008000;background-color:#d6ffce;margin:3px;padding:5px;}
\ No newline at end of file
diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/SimulationArea.scss b/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/SimulationArea.scss
new file mode 100644
index 0000000000..b43f39c08f
--- /dev/null
+++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/Pages/SimulationArea.scss
@@ -0,0 +1,6 @@
+.simulation-client {
+ border: 1px solid #008000;
+ background-color: #d6ffce;
+ margin: 3px;
+ padding: 5px;
+}
diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj b/utils/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj
index d654badfe3..1de5b06cf0 100644
--- a/utils/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj
+++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj
@@ -24,4 +24,12 @@
+
+
+
+
+
+
+
+
diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/compilerconfig.json b/utils/client-simulation/src/Volo.ClientSimulation.Web/compilerconfig.json
new file mode 100644
index 0000000000..d3e2a7ea96
--- /dev/null
+++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/compilerconfig.json
@@ -0,0 +1,6 @@
+[
+ {
+ "outputFile": "Pages/SimulationArea.css",
+ "inputFile": "Pages/SimulationArea.scss"
+ }
+]
\ No newline at end of file
diff --git a/utils/client-simulation/src/Volo.ClientSimulation.Web/compilerconfig.json.defaults b/utils/client-simulation/src/Volo.ClientSimulation.Web/compilerconfig.json.defaults
new file mode 100644
index 0000000000..b3519312c4
--- /dev/null
+++ b/utils/client-simulation/src/Volo.ClientSimulation.Web/compilerconfig.json.defaults
@@ -0,0 +1,63 @@
+{
+ "compilers": {
+ "less": {
+ "autoPrefix": "",
+ "cssComb": "none",
+ "ieCompat": true,
+ "strictMath": false,
+ "strictUnits": false,
+ "relativeUrls": true,
+ "rootPath": "",
+ "sourceMapRoot": "",
+ "sourceMapBasePath": "",
+ "sourceMap": false
+ },
+ "sass": {
+ "autoPrefix": "",
+ "includePath": "",
+ "indentType": "space",
+ "indentWidth": 2,
+ "outputStyle": "nested",
+ "Precision": 5,
+ "relativeUrls": true,
+ "sourceMapRoot": "",
+ "lineFeed": "",
+ "sourceMap": false
+ },
+ "stylus": {
+ "sourceMap": false
+ },
+ "babel": {
+ "sourceMap": false
+ },
+ "coffeescript": {
+ "bare": false,
+ "runtimeMode": "node",
+ "sourceMap": false
+ },
+ "handlebars": {
+ "root": "",
+ "noBOM": false,
+ "name": "",
+ "namespace": "",
+ "knownHelpersOnly": false,
+ "forcePartial": false,
+ "knownHelpers": [],
+ "commonjs": "",
+ "amd": false,
+ "sourceMap": false
+ }
+ },
+ "minifiers": {
+ "css": {
+ "enabled": true,
+ "termSemicolons": true,
+ "gzip": false
+ },
+ "javascript": {
+ "enabled": true,
+ "termSemicolons": true,
+ "gzip": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/ClientSimulationModule.cs b/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/ClientSimulationModule.cs
index 5bf3380a8a..28f79b1ba3 100644
--- a/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/ClientSimulationModule.cs
+++ b/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/ClientSimulationModule.cs
@@ -1,5 +1,6 @@
using Volo.Abp.Http.Client.IdentityModel;
using Volo.Abp.Modularity;
+using Volo.ClientSimulation.Scenarios;
namespace Volo.ClientSimulation
{
@@ -8,6 +9,13 @@ namespace Volo.ClientSimulation
)]
public class ClientSimulationModule : AbpModule
{
-
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ //TODO: Temporary add a DemoScenario, remove later
+ Configure(options =>
+ {
+ options.Scenarios.Add(new DemoScenario());
+ });
+ }
}
}
diff --git a/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/ClientSimulationOptions.cs b/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/ClientSimulationOptions.cs
new file mode 100644
index 0000000000..8de165fd2b
--- /dev/null
+++ b/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/ClientSimulationOptions.cs
@@ -0,0 +1,15 @@
+using System.Collections.Generic;
+using Volo.ClientSimulation.Scenarios;
+
+namespace Volo.ClientSimulation
+{
+ public class ClientSimulationOptions
+ {
+ public List Scenarios { get; }
+
+ public ClientSimulationOptions()
+ {
+ Scenarios = new List();
+ }
+ }
+}
diff --git a/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Clients/Client.cs b/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Clients/Client.cs
index a9df1fd974..059c8b8ae5 100644
--- a/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Clients/Client.cs
+++ b/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Clients/Client.cs
@@ -1,45 +1,65 @@
using System;
using System.Threading;
-using Volo.Abp.DependencyInjection;
+using Volo.ClientSimulation.Scenarios;
namespace Volo.ClientSimulation.Clients
{
- public class Client : IClient, ITransientDependency
+ public class Client : IClient
{
+ public IScenario Scenario { get; }
+
public event EventHandler Stopped;
- public ClientState State { get; private set; }
+ public ClientState State
+ {
+ get => _state;
+ private set => _state = value;
+ }
+ private volatile ClientState _state;
private Thread _thread;
- public Client()
+ protected readonly object SyncLock = new object();
+
+ public Client(IScenario scenario)
{
-
+ Scenario = scenario;
}
public void Start()
{
- if (State != ClientState.Stopped)
+ lock (SyncLock)
{
- throw new ApplicationException("State is not stopped. It is " + State);
- }
+ if (State != ClientState.Stopped)
+ {
+ throw new ApplicationException("State is not stopped. It is " + State);
+ }
- State = ClientState.Running;
+ State = ClientState.Running;
- _thread = new Thread(Run);
- _thread.Start();
+ _thread = new Thread(Run);
+ _thread.Start();
+ }
}
public void Stop()
{
- State = ClientState.Stopping;
+ lock (SyncLock)
+ {
+ if (State != ClientState.Running)
+ {
+ return;
+ }
+
+ State = ClientState.Stopping;
+ }
}
private void Run()
{
while (State == ClientState.Running)
{
- Thread.Sleep(1);
+ Scenario.Proceed();
}
State = ClientState.Stopped;
diff --git a/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Clients/IClient.cs b/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Clients/IClient.cs
index 869645392c..d056123c07 100644
--- a/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Clients/IClient.cs
+++ b/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Clients/IClient.cs
@@ -1,9 +1,12 @@
using System;
+using Volo.ClientSimulation.Scenarios;
namespace Volo.ClientSimulation.Clients
{
public interface IClient
{
+ IScenario Scenario { get; }
+
event EventHandler Stopped;
ClientState State { get; }
diff --git a/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Clients/IClientFactory.cs b/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Clients/IClientFactory.cs
new file mode 100644
index 0000000000..0fd4d64849
--- /dev/null
+++ b/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Clients/IClientFactory.cs
@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+using System.Text;
+using Volo.Abp.DependencyInjection;
+using Volo.ClientSimulation.Scenarios;
+
+namespace Volo.ClientSimulation.Clients
+{
+ public interface IClientFactory
+ {
+ IDisposableClientHandler Create(IScenario scenario);
+ }
+
+ public class DefaultClientFactory : IClientFactory, ISingletonDependency
+ {
+ public IDisposableClientHandler Create(IScenario scenario)
+ {
+ return new DisposableClientHandler(new Client(scenario));
+ }
+ }
+}
diff --git a/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Clients/IDisposableClientHandler.cs b/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Clients/IDisposableClientHandler.cs
new file mode 100644
index 0000000000..7d9799823e
--- /dev/null
+++ b/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Clients/IDisposableClientHandler.cs
@@ -0,0 +1,24 @@
+using System;
+
+namespace Volo.ClientSimulation.Clients
+{
+ public interface IDisposableClientHandler : IDisposable
+ {
+ IClient Client { get; }
+ }
+
+ public class DisposableClientHandler : IDisposableClientHandler
+ {
+ public IClient Client { get; }
+
+ public DisposableClientHandler(IClient client)
+ {
+ Client = client;
+ }
+
+ public virtual void Dispose()
+ {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Scenarios/Scenario.cs b/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Scenarios/Scenario.cs
new file mode 100644
index 0000000000..48170ee6c5
--- /dev/null
+++ b/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Scenarios/Scenario.cs
@@ -0,0 +1,122 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Text;
+using System.Threading;
+
+namespace Volo.ClientSimulation.Scenarios
+{
+ public abstract class Scenario : IScenario
+ {
+ public IReadOnlyList Steps => StepList.ToImmutableList();
+ protected List StepList { get; }
+
+ public IScenarioStep CurrentStep
+ {
+ get
+ {
+ CheckStepCount();
+ return StepList[CurrentStepIndex];
+ }
+ }
+ public int CurrentStepIndex { get; protected set; }
+
+ protected Scenario()
+ {
+ StepList = new List();
+ }
+
+ public virtual string GetDisplayText()
+ {
+ return GetType()
+ .Name
+ .RemovePostFix(nameof(Scenario));
+ }
+
+ public virtual void Proceed()
+ {
+ CheckStepCount();
+
+ StepList[CurrentStepIndex].Run();
+
+ CurrentStepIndex++;
+
+ if (CurrentStepIndex >= StepList.Count)
+ {
+ CurrentStepIndex = 0;
+ }
+ }
+
+ private void CheckStepCount()
+ {
+ if (StepList.Count <= 0)
+ {
+ throw new ApplicationException(
+ $"No Steps defined for the scenario '{GetDisplayText()}'"
+ );
+ }
+ }
+
+ protected void AddStep(IScenarioStep step)
+ {
+ StepList.Add(step);
+ }
+ }
+
+ public interface IScenario
+ {
+ IReadOnlyList Steps { get; }
+
+ IScenarioStep CurrentStep { get; }
+
+ int CurrentStepIndex { get; }
+
+ string GetDisplayText();
+
+ void Proceed();
+ }
+
+ public interface IScenarioStep
+ {
+ void Run();
+
+ string GetDisplayText();
+ }
+
+ public abstract class ScenarioStep : IScenarioStep
+ {
+ public abstract void Run();
+
+ public virtual string GetDisplayText()
+ {
+ return GetType()
+ .Name
+ .RemovePostFix(nameof(ScenarioStep));
+ }
+ }
+
+ public class DemoScenario : Scenario
+ {
+ public DemoScenario()
+ {
+ AddStep(new SleepScenarioStep());
+ AddStep(new SleepScenarioStep(3000));
+ AddStep(new SleepScenarioStep());
+ }
+ }
+
+ public class SleepScenarioStep : ScenarioStep
+ {
+ public int Duration { get; }
+
+ public SleepScenarioStep(int duration = 1000)
+ {
+ Duration = duration;
+ }
+
+ public override void Run()
+ {
+ Thread.Sleep(Duration);
+ }
+ }
+}
diff --git a/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Simulation.cs b/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Simulation.cs
index 314eb6318f..b266ea9c7c 100644
--- a/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Simulation.cs
+++ b/utils/client-simulation/src/Volo.ClientSimulation/Volo/ClientSimulation/Simulation.cs
@@ -1,12 +1,13 @@
using System;
using System.Collections.Generic;
-using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Volo.Abp;
using Volo.Abp.DependencyInjection;
using Volo.ClientSimulation.Clients;
namespace Volo.ClientSimulation
{
- public class Simulation : ISingletonDependency, IDisposable
+ public class Simulation : ISingletonDependency
{
public GlobalOptions GlobalOptions { get; }
@@ -14,17 +15,21 @@ namespace Volo.ClientSimulation
public SimulationState State { get; private set; }
- public List ActiveClients { get; }
+ public List ActiveClients { get; }
- private readonly IServiceScope _serviceScope;
+ protected IClientFactory ClientFactory { get; }
+
+ protected ClientSimulationOptions Options { get; }
public Simulation(
- GlobalOptions globalOptions,
- IServiceScopeFactory serviceScopeFactory)
+ GlobalOptions globalOptions,
+ IClientFactory clientFactory,
+ IOptions options)
{
- _serviceScope = serviceScopeFactory.CreateScope();
GlobalOptions = globalOptions;
- ActiveClients = new List();
+ ClientFactory = clientFactory;
+ Options = options.Value;
+ ActiveClients = new List();
}
public void Start()
@@ -39,13 +44,14 @@ namespace Volo.ClientSimulation
for (int i = 0; i < CurrentOptions.MaxClientCount; i++)
{
- ActiveClients.Add(_serviceScope.ServiceProvider.GetRequiredService());
+ var selectedScenario = RandomHelper.GetRandomOfList(Options.Scenarios);
+ ActiveClients.Add(ClientFactory.Create(selectedScenario));
}
foreach (var activeClient in ActiveClients)
{
- activeClient.Stopped += ActiveClientOnStopped;
- activeClient.Start();
+ activeClient.Client.Stopped += ActiveClientOnStopped;
+ activeClient.Client.Start();
}
}
@@ -60,7 +66,7 @@ namespace Volo.ClientSimulation
{
foreach (var activeClient in ActiveClients)
{
- activeClient.Stop();
+ activeClient.Client.Stop();
}
}
@@ -73,13 +79,8 @@ namespace Volo.ClientSimulation
lock (ActiveClients)
{
- ActiveClients.Remove(client);
+ ActiveClients.RemoveAll(c => c.Client == client);
}
}
-
- public void Dispose()
- {
- _serviceScope.Dispose();
- }
}
}