From 86fa7fd21102fd017d80c9933721eb6993ee305e Mon Sep 17 00:00:00 2001 From: akinast Date: Mon, 11 Mar 2024 11:49:12 +0100 Subject: [PATCH] Large updates to the framework --- Easy2Sim.Optimization/Class1.cs | 7 + .../Easy2Sim.Optimization.csproj | 9 ++ Easy2Sim/Easy2Sim.sln | 31 +++++ Easy2Sim/Environment/ComponentRegister.cs | 9 +- Easy2Sim/Environment/Easy2SimLogging.cs | 30 ++++- Easy2Sim/Environment/SimulationBase.cs | 82 ++++++++++-- Easy2Sim/Environment/SimulationEnvironment.cs | 98 ++++++++++++-- Easy2Sim/Solvers/BaseSolverModel.cs | 2 - Easy2Sim/Solvers/Discrete/DiscreteEvent.cs | 8 +- Easy2Sim/Solvers/Discrete/DiscreteSolver.cs | 125 +++++++++--------- .../Solvers/Discrete/DiscreteSolverModel.cs | 33 ++++- Easy2Sim/Solvers/Dynamic/DynamicSolver.cs | 11 ++ Easy2Sim/Solvers/SolverBase.cs | 31 +++++ 13 files changed, 381 insertions(+), 95 deletions(-) create mode 100644 Easy2Sim.Optimization/Class1.cs create mode 100644 Easy2Sim.Optimization/Easy2Sim.Optimization.csproj create mode 100644 Easy2Sim/Easy2Sim.sln diff --git a/Easy2Sim.Optimization/Class1.cs b/Easy2Sim.Optimization/Class1.cs new file mode 100644 index 0000000..5b20a4c --- /dev/null +++ b/Easy2Sim.Optimization/Class1.cs @@ -0,0 +1,7 @@ +namespace Easy2Sim.Optimization +{ + public class Class1 + { + + } +} diff --git a/Easy2Sim.Optimization/Easy2Sim.Optimization.csproj b/Easy2Sim.Optimization/Easy2Sim.Optimization.csproj new file mode 100644 index 0000000..fa71b7a --- /dev/null +++ b/Easy2Sim.Optimization/Easy2Sim.Optimization.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/Easy2Sim/Easy2Sim.sln b/Easy2Sim/Easy2Sim.sln new file mode 100644 index 0000000..f34bf56 --- /dev/null +++ b/Easy2Sim/Easy2Sim.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34408.163 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Easy2Sim", "Easy2Sim.csproj", "{3BE37AAA-1E6E-41EF-99A3-B9FD50886F92}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Easy2Sim.Optimization", "..\Easy2Sim.Optimization\Easy2Sim.Optimization.csproj", "{DEA429CB-604C-4A5A-9554-B2EAA5DB3CAE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3BE37AAA-1E6E-41EF-99A3-B9FD50886F92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3BE37AAA-1E6E-41EF-99A3-B9FD50886F92}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3BE37AAA-1E6E-41EF-99A3-B9FD50886F92}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3BE37AAA-1E6E-41EF-99A3-B9FD50886F92}.Release|Any CPU.Build.0 = Release|Any CPU + {DEA429CB-604C-4A5A-9554-B2EAA5DB3CAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DEA429CB-604C-4A5A-9554-B2EAA5DB3CAE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DEA429CB-604C-4A5A-9554-B2EAA5DB3CAE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DEA429CB-604C-4A5A-9554-B2EAA5DB3CAE}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C33B355E-3552-4745-854C-B6B0390C641F} + EndGlobalSection +EndGlobal diff --git a/Easy2Sim/Environment/ComponentRegister.cs b/Easy2Sim/Environment/ComponentRegister.cs index fb8655d..1605815 100644 --- a/Easy2Sim/Environment/ComponentRegister.cs +++ b/Easy2Sim/Environment/ComponentRegister.cs @@ -18,9 +18,10 @@ static ComponentRegister() RegisteredComponents = new Dictionary(); } /// - /// Get one specific Simulation Environment by its unique Id + /// Get one specific Simulation Environment by its unique guid /// - /// + /// + /// /// public static SimulationEnvironment? GetEnvironment(Guid id) { @@ -31,7 +32,7 @@ static ComponentRegister() } /// - /// Get one specific Solver by its unique ID + /// Get one specific Solver by its unique guid /// /// /// @@ -46,6 +47,7 @@ static ComponentRegister() /// /// Add any component (solver, environment) to the component register /// + /// Guid of the component which should be added public static void AddComponent(Guid componentGuid, object component) { RegisteredComponents.Add(componentGuid, component); @@ -54,6 +56,7 @@ public static void AddComponent(Guid componentGuid, object component) /// /// Remove any component (solver, environment) from the component register /// + /// Guid of the component which should be removed public static void RemoveComponent(Guid componentGuid) { RegisteredComponents.Remove(componentGuid); diff --git a/Easy2Sim/Environment/Easy2SimLogging.cs b/Easy2Sim/Environment/Easy2SimLogging.cs index ca2294e..ccacfd9 100644 --- a/Easy2Sim/Environment/Easy2SimLogging.cs +++ b/Easy2Sim/Environment/Easy2SimLogging.cs @@ -5,13 +5,39 @@ namespace Easy2Sim.Environment { /// - /// Class that can be used to send information over a udp port + /// The Logger of the Easy2Sim Framework. + /// Set when creating a new Environment. + /// + /// SimulationEnvironment environment = new(); + /// Logger l1 = new LoggerConfiguration() + /// .MinimumLevel.Verbose() + /// .WriteTo.Console() + /// .CreateLogger(); + /// environment.SetLogConfiguration(l1); + /// + /// /// public class Easy2SimLogging { + /// + /// Serilog is used for logging. + /// Can be used to log to any destination which serilog supports. + /// Make sure to set the correct minimum logging level. + /// public Logger? Logger { get; set; } - + /// + /// Method can be used to send information over a Udp client. + /// + /// + /// Message to send + /// + /// + /// Destination ip Address + /// + /// + /// Destination port where the message should be sent + /// public void UdpInformation(string message, string ipAddress = "127.0.0.1", int port = 12345) { UdpClient udpClient = new UdpClient(); diff --git a/Easy2Sim/Environment/SimulationBase.cs b/Easy2Sim/Environment/SimulationBase.cs index 387019e..810f8ef 100644 --- a/Easy2Sim/Environment/SimulationBase.cs +++ b/Easy2Sim/Environment/SimulationBase.cs @@ -5,9 +5,6 @@ namespace Easy2Sim.Environment { - - - /// /// Base class for simulation components. /// @@ -35,32 +32,52 @@ public abstract class SimulationBase : IFrameworkBase /// [JsonProperty] public string Name { get; private set; } + /// - /// Index in the simulation, this value is used + /// Index in the simulation. + /// In each time stamp in the simulation, components are called in order of the simulation index. /// [JsonProperty] public int Index => _simulationIndex; /// - /// Set the simulation index of a component manually. + /// Set the simulation of a component manually. /// Make sure that all indexes are correct! /// - /// + /// + /// The index is typically automatically generated based on creation order of the components. + /// This parameter allows to overwrite it. + /// public void SetIndexManually(int index) { _simulationIndex = index; } + /// + /// Set the simulation of a component manually. + /// Make sure that all names are unique and correct! + /// + /// + /// A unique name for each simulation component is generated automatically. + /// This parameter allows to change it. + /// + public void SetNameManually(string name) + { + Name = name; + } + [JsonIgnore] private SimulationEnvironment? _simulationEnvironment; + /// + /// Simulation environment where the simulation component is registered. + /// [JsonIgnore] public SimulationEnvironment? SimulationEnvironment { get { _simulationEnvironment = ComponentRegister.GetEnvironment(SimulationEnvironmentGuid); - return _simulationEnvironment; } } @@ -69,6 +86,9 @@ public SimulationEnvironment? SimulationEnvironment private SolverBase? _solverBase; + /// + /// Solver where the component is registered. + /// [JsonIgnore] public SolverBase? Solver { @@ -81,7 +101,10 @@ public SolverBase? Solver } - + /// + /// Automatically set the simulation index based on the current index in the environment. + /// This method is called, when a new component is created. + /// private void SetSimulationIndex() { if (SimulationEnvironment == null) return; @@ -143,6 +166,43 @@ protected SimulationBase(Guid environmentGuid) SimulationEnvironment?.AddComponent(this); } + /// + /// Returns all simulation bases for a given output parameter name + /// + /// Name of the output field or property + /// + public List GetConnectedInputComponents(string output) + { + if (SimulationEnvironment == null) + return new List(); + + List resultList = new List(); + List connectedComponents = SimulationEnvironment.Model.Connections + .Where(x => x.Source == this) + .Where(x => x.SourceName == output) + .Select(x => x.Target).ToList(); + resultList.AddRange(connectedComponents); + return resultList; + } + + /// + /// Returns all simulation bases for a given input parameter name + /// + /// Name of the input field or property + /// + public List GetConnectedOutputComponents(string input) + { + if (SimulationEnvironment == null) + return new List(); + + List resultList = new List(); + List connectedComponents = SimulationEnvironment.Model.Connections + .Where(x => x.Target == this) + .Where(x => x.TargetName == input) + .Select(x => x.Source).ToList(); + resultList.AddRange(connectedComponents); + return resultList; + } /// @@ -166,8 +226,14 @@ public virtual void DiscreteCalculation() { } /// public virtual void DynamicCalculation() { } + /// + /// This method is called at the end of the simulation + /// When calculate finish ends + /// + public virtual void End() { } /// /// Serialize to json uses the default constructor. + /// Each component needs to be serializable. /// public abstract string SerializeToJson(); diff --git a/Easy2Sim/Environment/SimulationEnvironment.cs b/Easy2Sim/Environment/SimulationEnvironment.cs index 54065f9..09d59e6 100644 --- a/Easy2Sim/Environment/SimulationEnvironment.cs +++ b/Easy2Sim/Environment/SimulationEnvironment.cs @@ -39,6 +39,36 @@ public void AddComponent(SimulationBase simulationBase) Model.SimulationObjects.Add(simulationBase.Index, simulationBase); } + /// + /// Add a new connection between to components in the environment. + /// + /// SimulationEnvironment environment = new SimulationEnvironment(); + /// DiscreteSolver solver = new DiscreteSolver(environment); + /// + /// Component1 comp1 = new Component1(environment, solver); + /// Component2 comp2 = new Component2(environment, solver); + /// + /// environment.AddConnection(comp1, "Output", comp2, "Input"); + /// + /// + /// + /// Component which has the output property/field + /// + /// + /// Name of the output property/field + /// + /// + /// Component which has the input property/field + /// + /// + /// Name of the input property/field + /// + /// + /// If the source is an IEnumerable, we can link individual positions for the IEnumerable + /// + /// + /// If the target is an IEnumerable, we can link individual positions for the IEnumerable + /// public void AddConnection(SimulationBase output, string outputName, SimulationBase input, string inputName, int sourceIndex = -1, int targetIndex = -1) { FieldInfo? outputField = output.GetType().GetFields().FirstOrDefault(x => x.Name == outputName); @@ -91,29 +121,53 @@ public void AddConnection(SimulationBase output, string outputName, SimulationBa con.TargetName = inputName; Model.Connections.Add(con); } - + + + + #region Logging + /// - /// Sets the logger that is used in the environment. - /// The default logger that is used logs to the console. + /// Method can be used to send information over a Udp client. /// - /// - public void SetLogConfiguration(Logger logger) - { - if (Model.Easy2SimLogging != null) - Model.Easy2SimLogging.Logger = logger; - } - + /// + /// Message to send + /// + /// + /// Destination ip Address + /// + /// + /// Destination port where the message should be sent + /// public void UdpInformation(string message, string ipAddress = "127.0.0.1", int port = 12345) { - if (Model.Easy2SimLogging != null) + if (Model.Easy2SimLogging != null) Model.Easy2SimLogging.UdpInformation(message, ipAddress, port); } - + /// + /// Sets the that is used in the environment. + /// Make sure to set the minimum logging level to your desired level. + /// + /// SimulationEnvironment environment = new(); + /// Logger l1 = new LoggerConfiguration() + /// .MinimumLevel.Verbose() + /// .WriteTo.Console() + /// .CreateLogger(); + /// environment.SetLogConfiguration(l1); + /// + /// + /// + /// Every serilog logger can be used in the framework. + /// + public void SetLogConfiguration(Logger logger) + { + if (Model.Easy2SimLogging != null) + Model.Easy2SimLogging.Logger = logger; + } public void LogInformation(string message) { @@ -127,11 +181,13 @@ public void LogError(string message) { Model.Easy2SimLogging?.Logger?.Error(message); } + + public void LogFatal(string message) { Model.Easy2SimLogging?.Logger?.Fatal(message); } - + #endregion Logging /// /// Remove this component from the register once it is disposed @@ -183,6 +239,22 @@ public void SetSolverForComponents(SolverBase solver) return null; } + /// + /// Returns a list containing all components based on the given name + /// + public List GetComponentsContainingName(string name) + { + List components = new(); + foreach (KeyValuePair component in Model.SimulationObjects) + { + if (component.Value.Name.Contains(name)) + { + components.Add(component.Value); + } + } + return components; + } + /// /// Duplicate a solver and environment pair, set references of the components /// diff --git a/Easy2Sim/Solvers/BaseSolverModel.cs b/Easy2Sim/Solvers/BaseSolverModel.cs index 209684c..b8f3669 100644 --- a/Easy2Sim/Solvers/BaseSolverModel.cs +++ b/Easy2Sim/Solvers/BaseSolverModel.cs @@ -51,8 +51,6 @@ public BaseSolverModel() Delay = 0; IsFinished = false; } - - public BaseSolverModel(Guid environmentGuid) { Results = new Dictionary(); diff --git a/Easy2Sim/Solvers/Discrete/DiscreteEvent.cs b/Easy2Sim/Solvers/Discrete/DiscreteEvent.cs index fa3af87..576a9db 100644 --- a/Easy2Sim/Solvers/Discrete/DiscreteEvent.cs +++ b/Easy2Sim/Solvers/Discrete/DiscreteEvent.cs @@ -20,14 +20,18 @@ public class DiscreteEvent : IFrameworkBase [JsonProperty] public long TimeStamp; + /// + /// In case of loops, this value keeps track of the order of events + /// + [JsonProperty] + public long TimeStampIndex; + /// /// Name of the component which the event should be simulated at the specified time /// [JsonProperty] public string ComponentName { get; set; } - - /// /// Constructor that is used for serialization. /// Should not be used, as a environment guid is necessary. diff --git a/Easy2Sim/Solvers/Discrete/DiscreteSolver.cs b/Easy2Sim/Solvers/Discrete/DiscreteSolver.cs index e89d45e..a6e1e47 100644 --- a/Easy2Sim/Solvers/Discrete/DiscreteSolver.cs +++ b/Easy2Sim/Solvers/Discrete/DiscreteSolver.cs @@ -1,5 +1,4 @@ -using System.Runtime.CompilerServices; -using Easy2Sim.Connect; +using Easy2Sim.Connect; using Easy2Sim.Environment; using Newtonsoft.Json; @@ -14,9 +13,20 @@ public class DiscreteSolver : SolverBase, IDisposable /// /// Represents all additional information that is necessary for the discrete solver /// - [JsonProperty] private DiscreteSolverModel _discreteSolverModel; + [JsonProperty] + private DiscreteSolverModel _discreteSolverModel; + + [JsonIgnore] + public DiscreteSolverModel DiscreteSolverModel => _discreteSolverModel; + + /// + /// Better access to the simulation time during the simulation + /// The real value is stored in the BaseModel + /// + [JsonIgnore] + public long SimulationTime => BaseModel.SimulationTime; + - [JsonIgnore] public DiscreteSolverModel DiscreteSolverModel => _discreteSolverModel; /// /// Represents all data that is necessary to run one event based simulation. /// @@ -61,14 +71,13 @@ public override void CalculateFinish() try { - AddInitialEvents(); - // Stopping condition: // A component sets the simulation to finished or // no events left while (!BaseModel.IsFinished && _discreteSolverModel.EventList.Any()) { DiscreteEvent? discreteEvent = GetNextEvent(); + if (discreteEvent == null) break; if (discreteEvent.TimeStamp > BaseModel.SimulationTime) @@ -86,6 +95,12 @@ public override void CalculateFinish() if (BaseModel.Delay > 0) Thread.Sleep(BaseModel.Delay); } + + foreach (SimulationBase simulationBase in SimulationEnvironment.Model.SimulationObjects.Values) + { + simulationBase.End(); + UpdateConnections(simulationBase); + } } catch (Exception ex) { @@ -100,7 +115,6 @@ public override void CalculateTo(long maxTime) try { - AddInitialEvents(); // Stopping condition: // A component sets the simulation to finished or // no events left or @@ -140,24 +154,19 @@ public override void CalculateTo(long maxTime) } /// - /// Each components discrete event should be added at least once at time step 0 + /// Add a event for each component at the current simulation time /// - private void AddInitialEvents() + public void AddEventForAllComponents() { - //Add events for each component - if (BaseModel.SimulationTime == 0) - { - List components = SimulationEnvironment.Model.SimulationObjects.Values.ToList(); - - List initialEvents = _discreteSolverModel.EventList.Where(x => x.TimeStamp == 0).ToList(); - - List componentsWithoutEvents = components.Where(x => !initialEvents.Select(y => y.ComponentName).Contains(x.Name)).ToList(); + AddEvents(SimulationEnvironment.Model.SimulationObjects.Values); + } - foreach (SimulationBase simulationBase in componentsWithoutEvents) - { - AddEvent(simulationBase); - } - } + /// + /// Add a event for each component at a specific simulation time + /// + public void AddEventForAllComponentsAtTime(long time) + { + AddEventsAtTime(SimulationEnvironment.Model.SimulationObjects.Values, time); } /// @@ -206,7 +215,6 @@ private void UpdateConnections(SimulationBase simulationComponent) con.CurrentValue = con.SourceValue; //Update the value in the target con.SetValue(); - AddEvent(con.Target); } } @@ -219,33 +227,40 @@ private void UpdateConnections(SimulationBase simulationComponent) //next event DiscreteEvent? discreteEvent = null; //lowest found simulation index - int simulationIndex = int.MaxValue; + long simulationIndex = int.MaxValue; try { - //If we do not hav events in our list we need to return null - if (_discreteSolverModel.EventList.Count == 0) + //If we do not have events in our list we need to return null + if (DiscreteSolverModel.EventList.Count == 0) return null; //Find the next simulation time => minimal time of all events - long nextSimTime = _discreteSolverModel.EventList.Select(x => x.TimeStamp).Min(); + long nextSimTime = DiscreteSolverModel.EventList.Select(x => x.TimeStamp).Min(); + //We only need information about events from the current simulation time + DiscreteSolverModel.HistoricEvents.RemoveAll(x => x.TimeStamp < nextSimTime); //Iterate all events and find the event with the lowest simulation index - foreach (DiscreteEvent e in _discreteSolverModel.EventList.Where(x => x.TimeStamp == nextSimTime)) + foreach (DiscreteEvent e in DiscreteSolverModel.EventList.Where(x => x.TimeStamp == nextSimTime)) { SimulationBase? comp = SimulationEnvironment?.GetComponentByName(e.ComponentName); if (comp == null) continue; - if (comp.Index < simulationIndex) + if (e.TimeStampIndex < simulationIndex) { - simulationIndex = comp.Index; + simulationIndex = e.TimeStampIndex; discreteEvent = e; } } //Remove the event from the event list and return it if (discreteEvent != null) - _discreteSolverModel.EventList.Remove(discreteEvent); + { + DiscreteSolverModel.EventList.Remove(discreteEvent); + DiscreteSolverModel.HistoricEvents.Add(discreteEvent); + + } + return discreteEvent; } @@ -267,7 +282,16 @@ public void AddEvent(SimulationBase simulationBase) if (SimulationEnvironment == null) return; - AddEventToTime(simulationBase, BaseModel.SimulationTime); + AddEventAtTime(simulationBase, BaseModel.SimulationTime); + } + /// + /// Add an even for a collection of simulation bases, e.g. when the output of a component changes a event for all inputs is generated + /// + /// + public void AddEvents(IEnumerable connectedInputs) + { + foreach (SimulationBase simBase in connectedInputs) + AddEvent(simBase); } /// @@ -277,39 +301,16 @@ public void AddEvent(SimulationBase simulationBase) /// public void AddEventAtTime(SimulationBase simulationBase, long simulationTime) { - if (SimulationEnvironment == null) - return; - - AddEventToTime(simulationBase, simulationTime); + DiscreteSolverModel.AddEvent(simulationTime, simulationBase); } - private void AddEventToTime(SimulationBase simulationBase, long simulationTime) + /// + /// Add an even for a collection of simulation bases, e.g. when the output of a component changes a event for all inputs is generated + /// + public void AddEventsAtTime(IEnumerable simulationBases, long simulationTime) { - if (_discreteSolverModel.AllowLoops) - { - //If no event has been added at this simulation time yet, add a empty list - if (!_discreteSolverModel.ComponentsAtSimulationTime.ContainsKey(simulationTime)) - _discreteSolverModel.ComponentsAtSimulationTime[simulationTime] = new List(); - - //We allow loops, so we only add the name to keep track of endless loops - _discreteSolverModel.ComponentsAtSimulationTime[simulationTime].Add(simulationBase.Name); - - //Add the event - _discreteSolverModel.EventList.Add(new DiscreteEvent(simulationTime, simulationBase.Name)); - } - else - { - //If no event has been added at this simulation time yet, add a empty list - if (!_discreteSolverModel.ComponentsAtSimulationTime.ContainsKey(simulationTime)) - _discreteSolverModel.ComponentsAtSimulationTime[simulationTime] = new List(); - - //Only allow each component once at each time step - if (!_discreteSolverModel.ComponentsAtSimulationTime[simulationTime].Contains(simulationBase.Name)) - { - _discreteSolverModel.ComponentsAtSimulationTime[simulationTime].Add(simulationBase.Name); - _discreteSolverModel.EventList.Add(new DiscreteEvent(simulationTime, simulationBase.Name)); - } - } + foreach (SimulationBase simBase in simulationBases) + AddEventAtTime(simBase, simulationTime); } /// diff --git a/Easy2Sim/Solvers/Discrete/DiscreteSolverModel.cs b/Easy2Sim/Solvers/Discrete/DiscreteSolverModel.cs index 30d743e..94f8ff1 100644 --- a/Easy2Sim/Solvers/Discrete/DiscreteSolverModel.cs +++ b/Easy2Sim/Solvers/Discrete/DiscreteSolverModel.cs @@ -22,11 +22,37 @@ public class DiscreteSolverModel : IFrameworkBase public List EventList { get; set; } + public void AddEvent(long timeStamp, SimulationBase simBase) + { + DiscreteEvent result = new DiscreteEvent(); + result.TimeStamp = timeStamp; + result.ComponentName = simBase.Name; + result.TimeStampIndex = simBase.Index + + HistoricEvents.Where(x => x.TimeStamp == timeStamp) + .Count(x => x.ComponentName == simBase.Name) * + simBase.SimulationEnvironment.Model.SimulationIndex; + if (AllowLoops) + { + EventList.Add(result); + } + else + { + //No loops allowed, check if the event exists already + if (!HistoricEvents.Any(x => (x.TimeStamp == timeStamp) && (x.ComponentName == simBase.Name))) + { + EventList.Add(result); + } + } + } + + /// - /// Keep track of all components that have been added to a event list at a specific time + /// Keep track of historic events /// [JsonProperty] - public Dictionary> ComponentsAtSimulationTime { get; set; } + public List HistoricEvents { get; set; } + + [JsonProperty] public Guid Guid { get; set; } @@ -37,7 +63,7 @@ public class DiscreteSolverModel : IFrameworkBase /// public DiscreteSolverModel() { - ComponentsAtSimulationTime = new Dictionary>(); + HistoricEvents = new List(); EventList = new List(); } @@ -49,5 +75,6 @@ public string SerializeToJson() { return JsonConvert.SerializeObject(this); } + } } diff --git a/Easy2Sim/Solvers/Dynamic/DynamicSolver.cs b/Easy2Sim/Solvers/Dynamic/DynamicSolver.cs index 9cde5a1..4c90c6f 100644 --- a/Easy2Sim/Solvers/Dynamic/DynamicSolver.cs +++ b/Easy2Sim/Solvers/Dynamic/DynamicSolver.cs @@ -16,6 +16,11 @@ public class DynamicSolver : SolverBase, IDisposable [JsonProperty] private DynamicSolverModel _dynamicSolverModel; + /// + /// Better access to the simulation time during the simulation + /// The real value is stored in the BaseModel + /// + [JsonIgnore] public long SimulationTime => BaseModel.SimulationTime; /// /// Represents all data that is necessary to run one event based simulation. @@ -77,6 +82,12 @@ public override void CalculateFinish() if (BaseModel.Delay > 0) Thread.Sleep(BaseModel.Delay); } + + foreach (SimulationBase simulationBase in SimulationEnvironment.Model.SimulationObjects.Values) + { + simulationBase.End(); + UpdateConnections(simulationBase); + } } catch (Exception ex) { diff --git a/Easy2Sim/Solvers/SolverBase.cs b/Easy2Sim/Solvers/SolverBase.cs index 72ba311..09b9a45 100644 --- a/Easy2Sim/Solvers/SolverBase.cs +++ b/Easy2Sim/Solvers/SolverBase.cs @@ -1,4 +1,6 @@ using Easy2Sim.Environment; +using Easy2Sim.Solvers.Discrete; +using Easy2Sim.Solvers.Dynamic; using Newtonsoft.Json; namespace Easy2Sim.Solvers @@ -24,6 +26,35 @@ public abstract class SolverBase : IFrameworkBase [JsonIgnore] public SimulationEnvironment? SimulationEnvironment => ComponentRegister.GetEnvironment(BaseModel.EnvironmentGuid); + /// + /// If the solver is a discrete solver it is returned. + /// Otherwise returns null. + /// + [JsonIgnore] + public DiscreteSolver? AsDiscreteSolver + { + get + { + if (this is DiscreteSolver discreteSolver) + return discreteSolver; + return null; + } + } + + /// + /// If the solver is a discrete solver it is returned. + /// Otherwise returns null. + /// + [JsonIgnore] + public DynamicSolver? AsDynamicSolver + { + get + { + if (this is DynamicSolver dynamicSolver) + return dynamicSolver; + return null; + } + } /// /// This method is called before a simulation is started and can be used to initialize components