diff --git a/CBSVisualizer/CBSVisualizer.Common/CBSVisualizer.Common.csproj b/CBSVisualizer/CBSVisualizer.Common/CBSVisualizer.Common.csproj index f6a4ed07d99b090f85173c3b763aed92015e7d2b..222f2d4454380d628b74b6514cb69a5311f26e37 100644 --- a/CBSVisualizer/CBSVisualizer.Common/CBSVisualizer.Common.csproj +++ b/CBSVisualizer/CBSVisualizer.Common/CBSVisualizer.Common.csproj @@ -5,8 +5,8 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="log4net" Version="2.0.11" /> - <PackageReference Include="Prism.Core" Version="7.2.0.1422" /> + <PackageReference Include="log4net" Version="2.0.12" /> + <PackageReference Include="Prism.Core" Version="8.0.0.1909" /> </ItemGroup> <ItemGroup> diff --git a/CBSVisualizer/CBSVisualizer.Common/Interfaces/IStatisticsService.cs b/CBSVisualizer/CBSVisualizer.Common/Interfaces/IStatisticsService.cs new file mode 100644 index 0000000000000000000000000000000000000000..7f50b51528c4d33a0c08a90421ac35c2a11e50c6 --- /dev/null +++ b/CBSVisualizer/CBSVisualizer.Common/Interfaces/IStatisticsService.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CBSVisualizer.Messaging.Models; + +namespace CBSVisualizer.Common.Interfaces +{ + public interface IStatisticsService : INotifyPropertyChanged + { + /// <summary> + /// Registers the specified packet at the statistics service. + /// Should be called for each arrived packet. + /// </summary> + /// <param name="newPacket">the packet that should be registered at the statistics service.</param> + void RegisterPacket(PriorityPacket newPacket); + + /// <summary> + /// Used to signal to the statistics service that the specified packet has been transmitted by the link. + /// </summary> + /// <param name="finishedPacket">the packet whose transmission has finished</param> + void TransmissionFinished(PriorityPacket finishedPacket); + + /// <summary> + /// Used to signal to the statistics service that the transmission has been started. + /// The statistics service uses this point in time as a reference for its statistics. Furthermore, the values of the previous run are deleted at this point in time. + /// </summary> + void SimulationStarted(); + + /// <summary> + /// Used to signal to the statistics service that the current simulation has finished and it should collect the values and form statistics. + /// </summary> + void SimulationStopped(); + + /// <summary> + /// Returns the statistics per priority. + /// </summary> + /// <returns>an IStatistics object that contains different statistics.</returns> + public Dictionary<int, IStatisticsSet<double>> Statistics { get; } + } +} diff --git a/CBSVisualizer/CBSVisualizer.Common/Interfaces/IStatisticsSet.cs b/CBSVisualizer/CBSVisualizer.Common/Interfaces/IStatisticsSet.cs new file mode 100644 index 0000000000000000000000000000000000000000..c52742a13b2e746fe0b181c0735c15567b5fdbc5 --- /dev/null +++ b/CBSVisualizer/CBSVisualizer.Common/Interfaces/IStatisticsSet.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace CBSVisualizer.Common.Interfaces +{ + public interface IStatisticsSet<out T> + { + /// <summary> + /// The raw values. + /// </summary> + public IEnumerable<T> RawValues { get; } + + /// <summary> + /// The Five Number Summary: minimum value, the lower quartile, the median, the upper quartile and the maximum value. + /// </summary> + public IEnumerable<T> FiveNumberSummary { get; } + } +} diff --git a/CBSVisualizer/CBSVisualizer.Core/CBSVisualizer.Core.csproj b/CBSVisualizer/CBSVisualizer.Core/CBSVisualizer.Core.csproj index 420b73a2984a8ceb109a665c939b123abbea8439..7f8040f2e8acdc2e433c4da98ebf43c71804d0eb 100644 --- a/CBSVisualizer/CBSVisualizer.Core/CBSVisualizer.Core.csproj +++ b/CBSVisualizer/CBSVisualizer.Core/CBSVisualizer.Core.csproj @@ -1,11 +1,11 @@ -<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> +<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <TargetFramework>netcoreapp5.0</TargetFramework> + <TargetFramework>net5.0-windows</TargetFramework> <UseWPF>true</UseWPF> <AssemblyName>CBSVisualizer.Core</AssemblyName> </PropertyGroup> <ItemGroup> - <PackageReference Include="log4net" Version="2.0.11" /> - <PackageReference Include="Prism.Wpf" Version="7.2.0.1422" /> + <PackageReference Include="log4net" Version="2.0.12" /> + <PackageReference Include="Prism.Wpf" Version="8.0.0.1909" /> </ItemGroup> </Project> \ No newline at end of file diff --git a/CBSVisualizer/CBSVisualizer.Core/Mvvm/RegionViewModelBase.cs b/CBSVisualizer/CBSVisualizer.Core/Mvvm/RegionViewModelBase.cs index 7644149c04c3ab33fa3cf897964cdb2d315d1e4a..a9bb75216ab4bee71a2e07a4ac82654f622c48a5 100644 --- a/CBSVisualizer/CBSVisualizer.Core/Mvvm/RegionViewModelBase.cs +++ b/CBSVisualizer/CBSVisualizer.Core/Mvvm/RegionViewModelBase.cs @@ -1,11 +1,10 @@ -using log4net; -using Prism.Logging; + using Prism.Regions; using System; namespace CBSVisualizer.Core.Mvvm { - public class RegionViewModelBase : ViewModelBase, INavigationAware, IConfirmNavigationRequest + public class RegionViewModelBase : ViewModelBase, IConfirmNavigationRequest { protected IRegionManager RegionManager { get; private set; } diff --git a/CBSVisualizer/CBSVisualizer.MessagingCore/CBSVisualizer.Messaging.csproj b/CBSVisualizer/CBSVisualizer.MessagingCore/CBSVisualizer.Messaging.csproj index a7797547b6883ccf49d6e63615436169825628eb..5e2e0a4720830216fb69e6c3fc6ccf4f86454dfc 100644 --- a/CBSVisualizer/CBSVisualizer.MessagingCore/CBSVisualizer.Messaging.csproj +++ b/CBSVisualizer/CBSVisualizer.MessagingCore/CBSVisualizer.Messaging.csproj @@ -5,8 +5,8 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="log4net" Version="2.0.11" /> - <PackageReference Include="Prism.Core" Version="7.2.0.1422" /> + <PackageReference Include="log4net" Version="2.0.12" /> + <PackageReference Include="Prism.Core" Version="8.0.0.1909" /> </ItemGroup> </Project> diff --git a/CBSVisualizer/CBSVisualizer.MessagingCore/Events/Simulation/SimulationConditionFulfilledEvent.cs b/CBSVisualizer/CBSVisualizer.MessagingCore/Events/Simulation/SimulationConditionFulfilledEvent.cs new file mode 100644 index 0000000000000000000000000000000000000000..40755de3eea8c09775d047f42cf0dc352d22b7df --- /dev/null +++ b/CBSVisualizer/CBSVisualizer.MessagingCore/Events/Simulation/SimulationConditionFulfilledEvent.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Prism.Events; + +namespace CBSVisualizer.Messaging.Events.Simulation +{ + /// <summary> + /// Sent by components that recognize that the simulation condition was hit and the simulation should be stopped. + /// </summary> + public class SimulationConditionFulfilledEvent : PubSubEvent + { + } +} diff --git a/CBSVisualizer/CBSVisualizer.MessagingCore/Models/PriorityPacket.cs b/CBSVisualizer/CBSVisualizer.MessagingCore/Models/PriorityPacket.cs index 1acac44222eeba123e2443c58c2ca03b7e689813..4ab16217fb9726a06c3d36c2b02f209e725d9f6b 100644 --- a/CBSVisualizer/CBSVisualizer.MessagingCore/Models/PriorityPacket.cs +++ b/CBSVisualizer/CBSVisualizer.MessagingCore/Models/PriorityPacket.cs @@ -1,8 +1,9 @@ -namespace CBSVisualizer.Messaging.Models +using System; + +namespace CBSVisualizer.Messaging.Models { public class PriorityPacket { - public static PriorityPacket NoPacket { get; } = new PriorityPacket(-1, int.MaxValue) { UniqueId = -1 }; /// <summary> @@ -13,22 +14,22 @@ /// <summary> /// The priority of the packet. /// </summary> - public int Priority { get; private set; } + public int Priority { get; } /// <summary> /// The header size of the packet. /// </summary> - public int HeaderBytes { get; private set; } + public int HeaderBytes { get; } /// <summary> /// The amount of payload bytes. /// </summary> - public int PayloadBytes { get; private set; } + public int PayloadBytes { get; } /// <summary> /// The amount of trailer bytes. /// </summary> - public int TrailerBytes { get; private set; } + public int TrailerBytes { get; } public int Size => PayloadBytes + HeaderBytes + TrailerBytes; @@ -64,5 +65,22 @@ { return $"ID: {UniqueId} Priority: {Priority}, Size: {Size}"; } + + protected bool Equals(PriorityPacket other) + { + return UniqueId == other.UniqueId && Priority == other.Priority && HeaderBytes == other.HeaderBytes && PayloadBytes == other.PayloadBytes && TrailerBytes == other.TrailerBytes; + } + + public override bool Equals(object obj) + { + if (obj is null) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == GetType() && Equals((PriorityPacket)obj); + } + + public override int GetHashCode() + { + return HashCode.Combine(Priority, HeaderBytes, PayloadBytes, TrailerBytes); + } } } diff --git a/CBSVisualizer/CBSVisualizer.Modules.Charts/CBSVisualizer.Modules.Charts.csproj b/CBSVisualizer/CBSVisualizer.Modules.Charts/CBSVisualizer.Modules.Charts.csproj index 863a10f6181380a714ce81ccdd38af0e4d37baf7..aad405a3109c66435fe02302f28679c93f7cb888 100644 --- a/CBSVisualizer/CBSVisualizer.Modules.Charts/CBSVisualizer.Modules.Charts.csproj +++ b/CBSVisualizer/CBSVisualizer.Modules.Charts/CBSVisualizer.Modules.Charts.csproj @@ -1,12 +1,12 @@ -<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> +<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <TargetFramework>net5.0</TargetFramework> + <TargetFramework>net5.0-windows</TargetFramework> <UseWPF>true</UseWPF> <AssemblyName>CBSVisualizer.Modules.Charts</AssemblyName> </PropertyGroup> <ItemGroup> <PackageReference Include="ParallelExtensionsExtras" Version="1.2.0" /> - <PackageReference Include="Prism.Wpf" Version="7.2.0.1422" /> + <PackageReference Include="Prism.Wpf" Version="8.0.0.1909" /> <PackageReference Include="SciChart" Version="6.2.1.13304" /> <PackageReference Include="SciChart.DirectX" Version="6.2.1.13304" /> </ItemGroup> diff --git a/CBSVisualizer/CBSVisualizer.Modules.Charts/ViewModels/ChartsViewModel.cs b/CBSVisualizer/CBSVisualizer.Modules.Charts/ViewModels/ChartsViewModel.cs index 07c49359537db904c6b7c2654a559a2b6bef3670..71621b0cfc18c34712a7fc3e5d96e76ab93c4366 100644 --- a/CBSVisualizer/CBSVisualizer.Modules.Charts/ViewModels/ChartsViewModel.cs +++ b/CBSVisualizer/CBSVisualizer.Modules.Charts/ViewModels/ChartsViewModel.cs @@ -74,7 +74,7 @@ namespace CBSVisualizer.Modules.Charts.ViewModels StrokeThickness = 1, AntiAliasing = true, DataSeries = new XyDataSeries<DateTime, double> { SeriesName = $"Queue {idx} Credit"}, - StyleKey = "CreditStyle", + StyleKey = "CreditStyle" } }; } diff --git a/CBSVisualizer/CBSVisualizer.Modules.Link/CBSVisualizer.Modules.Link.csproj b/CBSVisualizer/CBSVisualizer.Modules.Link/CBSVisualizer.Modules.Link.csproj index 4bd8f21495747622572cea900f63cf161b85c9f7..c978d2572d24c7d85cb93f62a8d8ac497c1dc435 100644 --- a/CBSVisualizer/CBSVisualizer.Modules.Link/CBSVisualizer.Modules.Link.csproj +++ b/CBSVisualizer/CBSVisualizer.Modules.Link/CBSVisualizer.Modules.Link.csproj @@ -1,14 +1,14 @@ -<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> +<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <TargetFramework>net5.0</TargetFramework> + <TargetFramework>net5.0-windows</TargetFramework> <UseWPF>true</UseWPF> <AssemblyName>CBSVisualizer.Modules.Link</AssemblyName> </PropertyGroup> <ItemGroup> - <PackageReference Include="log4net" Version="2.0.11" /> + <PackageReference Include="log4net" Version="2.0.12" /> <PackageReference Include="MaterialDesignThemes" Version="3.2.0" /> <PackageReference Include="ParallelExtensionsExtras" Version="1.2.0" /> - <PackageReference Include="Prism.Wpf" Version="7.2.0.1422" /> + <PackageReference Include="Prism.Wpf" Version="8.0.0.1909" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\CBSVisualizer.Common\CBSVisualizer.Common.csproj" /> diff --git a/CBSVisualizer/CBSVisualizer.Modules.MenuBar/CBSVisualizer.Modules.MenuBar.csproj b/CBSVisualizer/CBSVisualizer.Modules.MenuBar/CBSVisualizer.Modules.MenuBar.csproj index e6f31b2b3b5c9b174e43a8b1b6ad2a88b307b033..88466bc067172a6dc3dde8b70285776b61f054d8 100644 --- a/CBSVisualizer/CBSVisualizer.Modules.MenuBar/CBSVisualizer.Modules.MenuBar.csproj +++ b/CBSVisualizer/CBSVisualizer.Modules.MenuBar/CBSVisualizer.Modules.MenuBar.csproj @@ -1,13 +1,13 @@ -<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> +<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <TargetFramework>net5.0</TargetFramework> + <TargetFramework>net5.0-windows</TargetFramework> <UseWPF>true</UseWPF> <AssemblyName>CBSVisualizer.Modules.MenuBar</AssemblyName> </PropertyGroup> <ItemGroup> - <PackageReference Include="log4net" Version="2.0.11" /> + <PackageReference Include="log4net" Version="2.0.12" /> <PackageReference Include="MaterialDesignThemes" Version="3.2.0" /> - <PackageReference Include="Prism.Wpf" Version="7.2.0.1422" /> + <PackageReference Include="Prism.Wpf" Version="8.0.0.1909" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\CBSVisualizer.Core\CBSVisualizer.Core.csproj" /> diff --git a/CBSVisualizer/CBSVisualizer.Modules.MenuBar/ViewModels/MenuBarViewModel.cs b/CBSVisualizer/CBSVisualizer.Modules.MenuBar/ViewModels/MenuBarViewModel.cs index 2c4195d2a90ca1458e7afb29b51306cf6480ef71..49a7e2291f17d552d16e5d9aad67376640ff1216 100644 --- a/CBSVisualizer/CBSVisualizer.Modules.MenuBar/ViewModels/MenuBarViewModel.cs +++ b/CBSVisualizer/CBSVisualizer.Modules.MenuBar/ViewModels/MenuBarViewModel.cs @@ -38,33 +38,78 @@ namespace CBSVisualizer.Modules.MenuBar.ViewModels public DelegateCommand OpenSettingsCommand { get; } + public DelegateCommand OpenStatisticsCommand { get; } + private readonly IEventAggregator eventAggregator; private readonly IPacketService packetService; + private SettingService settingService; + private IStatisticsService statisticsService; - public MenuBarViewModel(IRegionManager regionManager, IEventAggregator eventAggregator, IDialogService dialogService, SettingService settingService, IPacketService packetServ) + public MenuBarViewModel(IRegionManager regionManager, IEventAggregator eventAggregator, IDialogService dialogService, SettingService settingService, IPacketService packetService, + IStatisticsService statisticsService) : base(regionManager) { this.eventAggregator = eventAggregator; - packetService = packetServ; + this.settingService = settingService; + this.packetService = packetService; + this.statisticsService = statisticsService; - StartSimulationCommand = new DelegateCommand(SignalSimulationStart, () => !SimulationRunning).ObservesProperty<bool>(() => SimulationRunning); - StopSimulationCommand = new DelegateCommand(SignalSimulationStop).ObservesCanExecute(() => SimulationRunning); OpenSettingsCommand = new DelegateCommand(() => dialogService.Show("SettingsDialog", new DialogParameters(), result => { })); + OpenStatisticsCommand = new DelegateCommand(() => dialogService.Show("Statistics", new DialogParameters(), result => { })); + + StartSimulationCommand = new DelegateCommand(async () => await SignalSimulationStart(), () => !SimulationRunning).ObservesProperty(() => SimulationRunning); + StopSimulationCommand = new DelegateCommand(SignalSimulationStop).ObservesCanExecute(() => SimulationRunning); StartLoadGeneration = new DelegateCommand(async () => await StartLoadThread(), CanStartLoadGeneration).ObservesProperty(() => LoadGenerationInProgress); StopLoadGeneration = new DelegateCommand(StopLoadThread, () => !CanStartLoadGeneration()).ObservesProperty(() => LoadGenerationInProgress); + + RegisterSimulationConditionListener(); } - private void SignalSimulationStart() + + private void RegisterSimulationConditionListener() { + // Stop the simulation if the end condition was fulfilled. + eventAggregator.GetEvent<SimulationConditionFulfilledEvent>().Subscribe(() => + { + + SignalSimulationStop(); + StopLoadThread(); + + }); + } + + private async Task SignalSimulationStart() + { + if (SimulationRunning) + { + return; + } + + statisticsService.SimulationStarted(); eventAggregator.GetEvent<SimulationStartedEvent>().Publish(); SimulationRunning = true; + + if (settingService.GetSettingValue<bool>("packetgen_at_simstart")) + { + await StartLoadThread(); + } } private void SignalSimulationStop() { + if (!SimulationRunning) + { + return; + } + statisticsService.SimulationStopped(); eventAggregator.GetEvent<SimulationStoppedEvent>().Publish(); SimulationRunning = false; + + if (settingService.GetSettingValue<bool>("packetgen_at_simstart")) + { + StopLoadThread(); + } } private bool CanStartLoadGeneration() @@ -74,6 +119,11 @@ namespace CBSVisualizer.Modules.MenuBar.ViewModels private async Task StartLoadThread() { + if (LoadGenerationInProgress) + { + return; + } + LoadGenerationInProgress = true; try { @@ -86,6 +136,11 @@ namespace CBSVisualizer.Modules.MenuBar.ViewModels private void StopLoadThread() { + if (!LoadGenerationInProgress) + { + return; + } + LoadGenerationInProgress = false; packetService.StopLoadGeneration(); } diff --git a/CBSVisualizer/CBSVisualizer.Modules.MenuBar/Views/MenuBar.xaml b/CBSVisualizer/CBSVisualizer.Modules.MenuBar/Views/MenuBar.xaml index 0a0c6062cd70e671e137b892b12a8977e29b27a3..764b928774c2b34b6a08d98b8382ad3f74154010 100644 --- a/CBSVisualizer/CBSVisualizer.Modules.MenuBar/Views/MenuBar.xaml +++ b/CBSVisualizer/CBSVisualizer.Modules.MenuBar/Views/MenuBar.xaml @@ -18,5 +18,6 @@ <MenuItem Header="_Start Load Generation" Command="{Binding StartLoadGeneration}"/> <MenuItem Header="_Terminate Load Generation" Command="{Binding StopLoadGeneration}"/> </MenuItem> + <MenuItem Header="Statistics" Command="{Binding OpenStatisticsCommand}"/> </Menu> </UserControl> diff --git a/CBSVisualizer/CBSVisualizer.Modules.Queue.QueueGroup/CBSVisualizer.Modules.QueueGroup.csproj b/CBSVisualizer/CBSVisualizer.Modules.Queue.QueueGroup/CBSVisualizer.Modules.QueueGroup.csproj index ca0e11e4edfcc015da7acb86d4d2ec1a2e7f525f..3cf2bd678fb832c968ee5351e9329612bb9a2eb5 100644 --- a/CBSVisualizer/CBSVisualizer.Modules.Queue.QueueGroup/CBSVisualizer.Modules.QueueGroup.csproj +++ b/CBSVisualizer/CBSVisualizer.Modules.Queue.QueueGroup/CBSVisualizer.Modules.QueueGroup.csproj @@ -1,13 +1,13 @@ -<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> +<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <TargetFramework>net5.0</TargetFramework> + <TargetFramework>net5.0-windows</TargetFramework> <UseWPF>true</UseWPF> <AssemblyName>CBSVisualizer.Modules.Queue.QueueGroup</AssemblyName> </PropertyGroup> <ItemGroup> - <PackageReference Include="log4net" Version="2.0.11" /> + <PackageReference Include="log4net" Version="2.0.12" /> <PackageReference Include="MaterialDesignThemes" Version="3.2.0" /> - <PackageReference Include="Prism.Wpf" Version="7.2.0.1422" /> + <PackageReference Include="Prism.Wpf" Version="8.0.0.1909" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\CBSVisualizer.Core\CBSVisualizer.Core.csproj" /> diff --git a/CBSVisualizer/CBSVisualizer.Modules.Queue.QueueGroup/Views/QueueGroup.xaml.cs b/CBSVisualizer/CBSVisualizer.Modules.Queue.QueueGroup/Views/QueueGroup.xaml.cs index b58103c810f462516a7ae12adfaebb146e72e328..db048c75e406c707aa950c4838c45b44676ef378 100644 --- a/CBSVisualizer/CBSVisualizer.Modules.Queue.QueueGroup/Views/QueueGroup.xaml.cs +++ b/CBSVisualizer/CBSVisualizer.Modules.Queue.QueueGroup/Views/QueueGroup.xaml.cs @@ -12,14 +12,14 @@ namespace CBSVisualizer.Modules.QueueGroup.Views /// </summary> public partial class QueueGroup : UserControl { - public QueueGroup(IRegionManager regionManager, IEventAggregator eventAggregator, IPacketService packetService, SettingService settingService) + public QueueGroup(IRegionManager regionManager, IEventAggregator eventAggregator, IPacketService packetService, SettingService settingService, IStatisticsService statisticsService) { InitializeComponent(); - for (int prio = 7; prio >= 0; prio--) + for (var priority = 7; priority >= 0; priority--) { - Queue.Views.Queue newQueue = new Queue.Views.Queue(new QueueViewModel(regionManager, eventAggregator, prio, packetService, settingService)); - Grid.SetRow(newQueue, 7 - prio); + Queue.Views.Queue newQueue = new Queue.Views.Queue(new QueueViewModel(regionManager, eventAggregator, priority, packetService, settingService, statisticsService)); + Grid.SetRow(newQueue, 7 - priority); Grid.Children.Add(newQueue); } } diff --git a/CBSVisualizer/CBSVisualizer.Modules.Queue/CBSVisualizer.Modules.Queue.csproj b/CBSVisualizer/CBSVisualizer.Modules.Queue/CBSVisualizer.Modules.Queue.csproj index 5759bee39f2b1a03229c7d44779833e97cc530be..9accae29e75186d53e9791cb26fa9282ef7c5d5f 100644 --- a/CBSVisualizer/CBSVisualizer.Modules.Queue/CBSVisualizer.Modules.Queue.csproj +++ b/CBSVisualizer/CBSVisualizer.Modules.Queue/CBSVisualizer.Modules.Queue.csproj @@ -1,13 +1,13 @@ -<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> +<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <TargetFramework>net5.0</TargetFramework> + <TargetFramework>net5.0-windows</TargetFramework> <UseWPF>true</UseWPF> <AssemblyName>CBSVisualizer.Modules.Queue</AssemblyName> </PropertyGroup> <ItemGroup> - <PackageReference Include="log4net" Version="2.0.11" /> + <PackageReference Include="log4net" Version="2.0.12" /> <PackageReference Include="MaterialDesignThemes" Version="3.2.0" /> - <PackageReference Include="Prism.Wpf" Version="7.2.0.1422" /> + <PackageReference Include="Prism.Wpf" Version="8.0.0.1909" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\CBSVisualizer.Common\CBSVisualizer.Common.csproj" /> diff --git a/CBSVisualizer/CBSVisualizer.Modules.Queue/ViewModels/QueueViewModel.cs b/CBSVisualizer/CBSVisualizer.Modules.Queue/ViewModels/QueueViewModel.cs index 08d5601b1915789bc678bea9e2d1b3c69d394695..1400eda75bd88a92bc6380d9c1971921f5f63129 100644 --- a/CBSVisualizer/CBSVisualizer.Modules.Queue/ViewModels/QueueViewModel.cs +++ b/CBSVisualizer/CBSVisualizer.Modules.Queue/ViewModels/QueueViewModel.cs @@ -47,14 +47,16 @@ namespace CBSVisualizer.Modules.Queue.ViewModels private CreditBehaviour selectedCreditBehaviour; private long idleSlope; private SettingService settings; + private IStatisticsService statisticsService; public QueueViewModel(IRegionManager regionManager, IEventAggregator eventAggregator, int priority, - IPacketService packetService, SettingService settings) : + IPacketService packetService, SettingService settings, IStatisticsService statisticsService) : base(regionManager) { this.eventAggregator = eventAggregator; this.packetService = packetService; this.settings = settings; + this.statisticsService = statisticsService; Priority = priority; HandleLabels(); @@ -169,6 +171,7 @@ namespace CBSVisualizer.Modules.Queue.ViewModels } transmissionFinishedPending = false; + statisticsService.TransmissionFinished(Queue.GetFirstSynchronized()); await Task.Run(() => QueueImplementation.OnTransmissionFinished(Queue.GetRemoveFirstSynchronized(), bitrate)); }); @@ -231,21 +234,13 @@ namespace CBSVisualizer.Modules.Queue.ViewModels { if (e.PropertyName != null && e.PropertyName.Equals("SelectedCreditBehaviour")) { - switch (SelectedCreditBehaviour) + QueueImplementation = SelectedCreditBehaviour switch { - case CreditBehaviour.Standard: - QueueImplementation = new StandardBehaviourQueue(Priority, new ReadOnlyCollection<PriorityPacket>(Queue), eventAggregator); - break; - case CreditBehaviour.Frozen: - QueueImplementation = new FrozenBehaviourQueue(Priority, new ReadOnlyCollection<PriorityPacket>(Queue), eventAggregator); - break; - case CreditBehaviour.ReturnToZero: - QueueImplementation = new ReturnToZeroQueue(Priority, new ReadOnlyCollection<PriorityPacket>(Queue), eventAggregator); - break; - default: - QueueImplementation = new NoCbsQueue(); - break; - } + CreditBehaviour.Standard => new StandardBehaviourQueue(Priority, new ReadOnlyCollection<PriorityPacket>(Queue), eventAggregator), + CreditBehaviour.Frozen => new FrozenBehaviourQueue(Priority, new ReadOnlyCollection<PriorityPacket>(Queue), eventAggregator), + CreditBehaviour.ReturnToZero => new ReturnToZeroQueue(Priority, new ReadOnlyCollection<PriorityPacket>(Queue), eventAggregator), + _ => new NoCbsQueue() + }; } } @@ -255,6 +250,7 @@ namespace CBSVisualizer.Modules.Queue.ViewModels { if (packet.Priority != Priority) return; + statisticsService.RegisterPacket(packet); Queue.AddSynchronized(packet); }); } diff --git a/CBSVisualizer/CBSVisualizer.Modules.SettingsDialog/CBSVisualizer.Modules.SettingsDialog.csproj b/CBSVisualizer/CBSVisualizer.Modules.SettingsDialog/CBSVisualizer.Modules.SettingsDialog.csproj index 93b5cac740f9c2c01488040115f411df5dc33ab1..b1830d5c113b9803ae081505c0e38d653da2f66e 100644 --- a/CBSVisualizer/CBSVisualizer.Modules.SettingsDialog/CBSVisualizer.Modules.SettingsDialog.csproj +++ b/CBSVisualizer/CBSVisualizer.Modules.SettingsDialog/CBSVisualizer.Modules.SettingsDialog.csproj @@ -1,13 +1,13 @@ -<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> +<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <TargetFramework>net5.0</TargetFramework> + <TargetFramework>net5.0-windows</TargetFramework> <UseWPF>true</UseWPF> <AssemblyName>CBSVisualizer.Modules.SettingsDialog</AssemblyName> </PropertyGroup> <ItemGroup> - <PackageReference Include="log4net" Version="2.0.11" /> + <PackageReference Include="log4net" Version="2.0.12" /> <PackageReference Include="MaterialDesignThemes" Version="3.2.0" /> - <PackageReference Include="Prism.Wpf" Version="7.2.0.1422" /> + <PackageReference Include="Prism.Wpf" Version="8.0.0.1909" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\CBSVisualizer.Core\CBSVisualizer.Core.csproj" /> diff --git a/CBSVisualizer/CBSVisualizer.Modules.SettingsDialog/Views/SettingsDialog.xaml b/CBSVisualizer/CBSVisualizer.Modules.SettingsDialog/Views/SettingsDialog.xaml index 0c7bea91fa5fbcccb274c796da0bd02dba0d21ac..9cd43c10047ec97a4eea47dc6d745eecd4eef96c 100644 --- a/CBSVisualizer/CBSVisualizer.Modules.SettingsDialog/Views/SettingsDialog.xaml +++ b/CBSVisualizer/CBSVisualizer.Modules.SettingsDialog/Views/SettingsDialog.xaml @@ -31,7 +31,7 @@ <Grid Margin="5"> <Grid.Resources> <DataTemplate DataType="{x:Type settings:BooleanSetting}"> - <CheckBox Content="{Binding Name}" IsChecked="{Binding SettingValue}"/> + <CheckBox Content="{Binding Name}" IsChecked="{Binding SettingValue}" Margin="0 10"/> </DataTemplate> <DataTemplate DataType="{x:Type settings:StringSetting}"> diff --git a/CBSVisualizer/CBSVisualizer.Modules.StatisticsDialog/CBSVisualizer.Modules.StatisticsDialog.csproj b/CBSVisualizer/CBSVisualizer.Modules.StatisticsDialog/CBSVisualizer.Modules.StatisticsDialog.csproj new file mode 100644 index 0000000000000000000000000000000000000000..f28d2157cf8d7ae9c835bcddcba334031327f794 --- /dev/null +++ b/CBSVisualizer/CBSVisualizer.Modules.StatisticsDialog/CBSVisualizer.Modules.StatisticsDialog.csproj @@ -0,0 +1,23 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>net5.0-windows</TargetFramework> + <UseWPF>true</UseWPF> + <AssemblyName>CBSVisualizer.Modules.StatisticsDialog</AssemblyName> + </PropertyGroup> + <ItemGroup> + <PackageReference Include="Prism.Wpf" Version="8.0.0.1909" /> + <PackageReference Include="SciChart" Version="6.2.1.13304" /> + <PackageReference Include="SciChart.DirectX" Version="6.2.1.13304" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\CBSVisualizer.Common\CBSVisualizer.Common.csproj" /> + </ItemGroup> + <ItemGroup> + <Reference Include="MaterialDesignThemes.Wpf"> + <HintPath>C:\Users\Dmitrii\.nuget\packages\materialdesignthemes\3.2.0\lib\netcoreapp3.1\MaterialDesignThemes.Wpf.dll</HintPath> + </Reference> + <Reference Include="SciChart.Charting"> + <HintPath>C:\Users\Dmitrii\.nuget\packages\scichart\6.2.1.13304\lib\netcoreapp3.0\SciChart.Charting.dll</HintPath> + </Reference> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/CBSVisualizer/CBSVisualizer.Modules.StatisticsDialog/StatisticsDialogModule.cs b/CBSVisualizer/CBSVisualizer.Modules.StatisticsDialog/StatisticsDialogModule.cs new file mode 100644 index 0000000000000000000000000000000000000000..dded808210625e3f1f57e59085ee6a97826e0640 --- /dev/null +++ b/CBSVisualizer/CBSVisualizer.Modules.StatisticsDialog/StatisticsDialogModule.cs @@ -0,0 +1,19 @@ +using CBSVisualizer.Modules.StatisticsDialog.ViewModels; +using Prism.Ioc; +using Prism.Modularity; + +namespace CBSVisualizer.Modules.StatisticsDialog +{ + public class StatisticsDialogModule : IModule + { + public void OnInitialized(IContainerProvider containerProvider) + { + + } + + public void RegisterTypes(IContainerRegistry containerRegistry) + { + containerRegistry.RegisterDialog<Views.StatisticsDialog, StatisticsDialogViewModel>("Statistics"); + } + } +} \ No newline at end of file diff --git a/CBSVisualizer/CBSVisualizer.Modules.StatisticsDialog/ViewModels/StatisticsDialogViewModel.cs b/CBSVisualizer/CBSVisualizer.Modules.StatisticsDialog/ViewModels/StatisticsDialogViewModel.cs new file mode 100644 index 0000000000000000000000000000000000000000..1e0cfaeae5407f276d792ab311846364fad1bc9c --- /dev/null +++ b/CBSVisualizer/CBSVisualizer.Modules.StatisticsDialog/ViewModels/StatisticsDialogViewModel.cs @@ -0,0 +1,134 @@ +using Prism.Commands; +using Prism.Mvvm; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CBSVisualizer.Common.Interfaces; +using CBSVisualizer.Messaging.Events.Simulation; +using Prism.Events; +using Prism.Services.Dialogs; +using SciChart.Charting.Model.ChartSeries; +using SciChart.Charting.Model.DataSeries; +using SciChart.Charting.Visuals.RenderableSeries; +using SciChart.Core.Extensions; + +namespace CBSVisualizer.Modules.StatisticsDialog.ViewModels +{ + public class StatisticsDialogViewModel : BindableBase, IDialogAware + { + private ObservableCollection<IRenderableSeriesViewModel> boxPlotData = new ObservableCollection<IRenderableSeriesViewModel>(); + public ObservableCollection<IRenderableSeriesViewModel> BoxPlotData + { + get => boxPlotData; + set => SetProperty(ref boxPlotData, value); + } + + private ObservableCollection<IRenderableSeriesViewModel> delayData = new ObservableCollection<IRenderableSeriesViewModel>(); + public ObservableCollection<IRenderableSeriesViewModel> DelayData + { + get => delayData; + set => SetProperty(ref delayData, value); + } + + public string Title => "Statistics"; + + public event Action<IDialogResult> RequestClose; + + private readonly IEventAggregator eventAggregator; + private readonly IStatisticsService statisticsService; + + public StatisticsDialogViewModel(IEventAggregator eventAggregator, IStatisticsService statisticsService) + { + this.eventAggregator = eventAggregator; + this.statisticsService = statisticsService; + + InitSeries(); + } + + private void RefreshData() + { + RefreshBoxPlot(); + RefreshDelayData(); + } + + private void RefreshDelayData() + { + // Remove all Delay Data. + DelayData.Clear(); + + // Request data from the Statistics Service for all queues. + foreach ((int priority, IStatisticsSet<double> statistics) in statisticsService.Statistics + .OrderBy((statisticsData) => statisticsData.Key)) + { + // Construct the X-Axis: Cast to ulong and use distinct + some LINQ porn. + var xValues = statistics.RawValues.Select(value => (ulong) value).Distinct().OrderBy(value => value).ToList(); + var yValues = new int[xValues.Count]; + + // Now, do the cast again and count how often each distinct value is contained in the set. + for (var i = 0; i < yValues.Length; i++) + { + var x = xValues[i]; + yValues[i] = statistics.RawValues.Select(value => (ulong) value).Count(raw => raw == x); + } + + // Add the data to the chart. + var data = new XyDataSeries<ulong, int> {SeriesName = $"Priority {priority}"}; + data.Append(xValues, yValues); + DelayData.Add(new ColumnRenderableSeriesViewModel() + { + DataSeries = data + }); + } + } + + private void RefreshBoxPlot() + { + var boxPlotDataSeries = BoxPlotData[0].DataSeries as BoxPlotDataSeries<int, double>; + boxPlotDataSeries?.Clear(); + + // Request data from the Statistics Service for all queues. + foreach ((int priority, IStatisticsSet<double> statistics) in statisticsService.Statistics + .OrderBy((statisticsData) => statisticsData.Key)) + { + boxPlotDataSeries?.Append(priority, + statistics.FiveNumberSummary.ElementAt(2), // median + statistics.FiveNumberSummary.ElementAt(0), // minimum + statistics.FiveNumberSummary.ElementAt(1), // lower quartile + statistics.FiveNumberSummary.ElementAt(3), // upper quartile + statistics.FiveNumberSummary.ElementAt(4)); // maximum + } + } + + private void InitSeries() + { + BoxPlotData.Add(new BoxPlotRenderableSeriesViewModel + { + DataSeries = new BoxPlotDataSeries<int, double>() + }); + + DelayData.Add(new ColumnRenderableSeriesViewModel() + { + DataSeries = new XyDataSeries<double> {SeriesName = "Delay Distribution for selected queue", AcceptsUnsortedData = true} + }); + } + + public bool CanCloseDialog() + { + return true; + } + + public void OnDialogClosed() + { + // TODO: Maybe do some cleanup here? + return; + } + + public void OnDialogOpened(IDialogParameters parameters) + { + RefreshData(); + } + } +} diff --git a/CBSVisualizer/CBSVisualizer.Modules.StatisticsDialog/Views/StatisticsDialog.xaml b/CBSVisualizer/CBSVisualizer.Modules.StatisticsDialog/Views/StatisticsDialog.xaml new file mode 100644 index 0000000000000000000000000000000000000000..208d83313586243e85b48802085bf5e8b23aca6c --- /dev/null +++ b/CBSVisualizer/CBSVisualizer.Modules.StatisticsDialog/Views/StatisticsDialog.xaml @@ -0,0 +1,64 @@ +<UserControl x:Class="CBSVisualizer.Modules.StatisticsDialog.Views.StatisticsDialog" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:local="clr-namespace:CBSVisualizer.Modules.StatisticsDialog.Views" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + mc:Ignorable="d" + d:DesignHeight="300" d:DesignWidth="300" + xmlns:prism="http://prismlibrary.com/" + xmlns:s="http://schemas.abtsoftware.co.uk/scichart" + xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" + TextElement.Foreground="{DynamicResource MaterialDesignBody}" + Background="{DynamicResource MaterialDesignPaper}" + TextElement.FontWeight="Medium" + TextElement.FontSize="14" + FontFamily="{materialDesign:MaterialDesignFont}" + prism:ViewModelLocator.AutoWireViewModel="True" > + + <prism:Dialog.WindowStyle> + <Style TargetType="Window"> + <Setter Property="prism:Dialog.WindowStartupLocation" Value="CenterScreen" /> + <Setter Property="ResizeMode" Value="CanResizeWithGrip"/> + <Setter Property="ShowInTaskbar" Value="False"/> + <Setter Property="SizeToContent" Value="WidthAndHeight"/> + <Setter Property="MinWidth" Value="640"/> + <Setter Property="MinHeight" Value="360"/> + </Style> + </prism:Dialog.WindowStyle> + + <Grid> + <Grid.RowDefinitions> + <RowDefinition/> + <RowDefinition/> + </Grid.RowDefinitions> + + <s:SciChartSurface Grid.Row="0" s:RenderableSeries="{s:SeriesBinding BoxPlotData}" ChartTitle="Delay Box Plots"> + <s:SciChartSurface.XAxis> + <s:NumericAxis AutoRange="Once" VisibleRange="-0.5, 7.5" FlipCoordinates="True"/> + </s:SciChartSurface.XAxis> + <s:SciChartSurface.YAxis> + <s:NumericAxis AutoRange="Always"/> + </s:SciChartSurface.YAxis> + </s:SciChartSurface> + + <s:SciChartSurface Grid.Row="1" s:RenderableSeries="{s:SeriesBinding DelayData}" ChartTitle="Individual Delays"> + + <s:SciChartSurface.ChartModifier> + <s:ModifierGroup> + <s:LegendModifier ShowLegend="True" Orientation="Horizontal" Margin="10" + LegendPlacement="Inside" GetLegendDataFor="AllSeries" + ShowVisibilityCheckboxes="True"/> + </s:ModifierGroup> + </s:SciChartSurface.ChartModifier> + + + <s:SciChartSurface.XAxis> + <s:NumericAxis AutoRange="Always"/> + </s:SciChartSurface.XAxis> + <s:SciChartSurface.YAxis> + <s:NumericAxis AutoRange="Always"/> + </s:SciChartSurface.YAxis> + </s:SciChartSurface> + </Grid> +</UserControl> diff --git a/CBSVisualizer/CBSVisualizer.Modules.StatisticsDialog/Views/StatisticsDialog.xaml.cs b/CBSVisualizer/CBSVisualizer.Modules.StatisticsDialog/Views/StatisticsDialog.xaml.cs new file mode 100644 index 0000000000000000000000000000000000000000..ebc2bbd3181e80cbb5026f1837421fa3880a2a7e --- /dev/null +++ b/CBSVisualizer/CBSVisualizer.Modules.StatisticsDialog/Views/StatisticsDialog.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace CBSVisualizer.Modules.StatisticsDialog.Views +{ + /// <summary> + /// Interaction logic for StatisticsDialog.xaml + /// </summary> + public partial class StatisticsDialog : UserControl + { + public StatisticsDialog() + { + InitializeComponent(); + } + } +} diff --git a/CBSVisualizer/CBSVisualizer.Services.PacketService/CBSVisualizer.Services.PacketService.csproj b/CBSVisualizer/CBSVisualizer.Services.PacketService/CBSVisualizer.Services.PacketService.csproj index 1ace85c0c60fabf9e13cf6e8fbda9eeb5c710b59..473dd435a9f73aa0e0bcd604b1996414888d1af6 100644 --- a/CBSVisualizer/CBSVisualizer.Services.PacketService/CBSVisualizer.Services.PacketService.csproj +++ b/CBSVisualizer/CBSVisualizer.Services.PacketService/CBSVisualizer.Services.PacketService.csproj @@ -1,7 +1,7 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <TargetFramework>net5.0</TargetFramework> + <TargetFramework>net5.0-windows</TargetFramework> </PropertyGroup> <ItemGroup> diff --git a/CBSVisualizer/CBSVisualizer.Services.SchedulingService/CBSVisualizer.Services.SchedulingService.csproj b/CBSVisualizer/CBSVisualizer.Services.SchedulingService/CBSVisualizer.Services.SchedulingService.csproj index 210af7dad6db787f4cc02d6e2ad3e057c6ccc9cd..afad604d038675c36fe0317b5dc474e2bd2a5916 100644 --- a/CBSVisualizer/CBSVisualizer.Services.SchedulingService/CBSVisualizer.Services.SchedulingService.csproj +++ b/CBSVisualizer/CBSVisualizer.Services.SchedulingService/CBSVisualizer.Services.SchedulingService.csproj @@ -1,12 +1,12 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <TargetFramework>net5.0</TargetFramework> + <TargetFramework>net5.0-windows</TargetFramework> </PropertyGroup> <ItemGroup> - <PackageReference Include="log4net" Version="2.0.11" /> - <PackageReference Include="Prism.Core" Version="7.2.0.1422" /> + <PackageReference Include="log4net" Version="2.0.12" /> + <PackageReference Include="Prism.Core" Version="8.0.0.1909" /> </ItemGroup> <ItemGroup> diff --git a/CBSVisualizer/CBSVisualizer.Services.SchedulingService/FlowBasedSchedulingService.cs b/CBSVisualizer/CBSVisualizer.Services.SchedulingService/FlowBasedSchedulingService.cs index a0b76de2ef40f2ecc093f2e605c897391593e241..7e005dd868b21b1408740500870ece3211c205f8 100644 --- a/CBSVisualizer/CBSVisualizer.Services.SchedulingService/FlowBasedSchedulingService.cs +++ b/CBSVisualizer/CBSVisualizer.Services.SchedulingService/FlowBasedSchedulingService.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using CBSVisualizer.Common.Interfaces; using CBSVisualizer.Messaging.Events.Queue; +using CBSVisualizer.Messaging.Events.Simulation; using CBSVisualizer.Services.JsonService.JsonTypes; using log4net; using Prism.Events; @@ -41,6 +42,12 @@ namespace CBSVisualizer.Services.SchedulingService await PerformSchedule(config, schedule).ConfigureAwait(false); cycles++; } + + // If the desired cycles amount was hit, publish the SimulationConditionFulfilled event. + if (cycles >= config.Cycles) + { + eventAggregator.GetEvent<SimulationConditionFulfilledEvent>().Publish(); + } } private async Task PerformSchedule(FlowConfiguration config, IList<TimeSpan> schedule) diff --git a/CBSVisualizer/CBSVisualizer.Services.SettingsService/CBSVisualizer.Services.SettingService.csproj b/CBSVisualizer/CBSVisualizer.Services.SettingsService/CBSVisualizer.Services.SettingService.csproj index 1a6d97eabd55b33cb39f8d1252cb656d61a81ae9..805d8903a56224bd0b62439e353bf900790da1f0 100644 --- a/CBSVisualizer/CBSVisualizer.Services.SettingsService/CBSVisualizer.Services.SettingService.csproj +++ b/CBSVisualizer/CBSVisualizer.Services.SettingsService/CBSVisualizer.Services.SettingService.csproj @@ -1,15 +1,15 @@ -<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> +<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <TargetFramework>net5.0</TargetFramework> + <TargetFramework>net5.0-windows</TargetFramework> <UseWPF>true</UseWPF> <AssemblyName>CBSVisualizer.Services.SettingsService</AssemblyName> </PropertyGroup> <ItemGroup> - <PackageReference Include="log4net" Version="2.0.11" /> - <PackageReference Include="Ookii.Dialogs.Wpf" Version="1.1.0" /> - <PackageReference Include="Prism.Core" Version="7.2.0.1422" /> + <PackageReference Include="log4net" Version="2.0.12" /> + <PackageReference Include="Ookii.Dialogs.Wpf" Version="1.2.0" /> + <PackageReference Include="Prism.Core" Version="8.0.0.1909" /> </ItemGroup> <ItemGroup> diff --git a/CBSVisualizer/CBSVisualizer.Services.SettingsService/SettingService.cs b/CBSVisualizer/CBSVisualizer.Services.SettingsService/SettingService.cs index e7be406064ced24b9849bb52813a4db2e0e49c70..aa81a625fed8c36571682511782ab206fadfa446 100644 --- a/CBSVisualizer/CBSVisualizer.Services.SettingsService/SettingService.cs +++ b/CBSVisualizer/CBSVisualizer.Services.SettingsService/SettingService.cs @@ -36,6 +36,9 @@ namespace CBSVisualizer.Services.SettingService { var loadGenerationGroupMembers = new ObservableCollection<Setting>(); + // Start Packet Generation at Simulation Start? + loadGenerationGroupMembers.Add(new BooleanSetting("packetgen_at_simstart", "Start/Stop Packet Generation at Simulation Start/Stop?", true)); + // Packet interarrival time. loadGenerationGroupMembers.Add(new StringSetting("interarrival_time", "Packet Inter-Arrival Time [ms]", "100")); diff --git a/CBSVisualizer/CBSVisualizer.Services.StatisticsService/CBSVisualizer.Services.StatisticsService.csproj b/CBSVisualizer/CBSVisualizer.Services.StatisticsService/CBSVisualizer.Services.StatisticsService.csproj new file mode 100644 index 0000000000000000000000000000000000000000..50a07f8ed001641453f8e0b593bae921dcbd841a --- /dev/null +++ b/CBSVisualizer/CBSVisualizer.Services.StatisticsService/CBSVisualizer.Services.StatisticsService.csproj @@ -0,0 +1,15 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net5.0</TargetFramework> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="MathNet.Numerics" Version="4.12.0" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\CBSVisualizer.Common\CBSVisualizer.Common.csproj" /> + </ItemGroup> + +</Project> diff --git a/CBSVisualizer/CBSVisualizer.Services.StatisticsService/PacketTimestampCollection.cs b/CBSVisualizer/CBSVisualizer.Services.StatisticsService/PacketTimestampCollection.cs new file mode 100644 index 0000000000000000000000000000000000000000..503c578b4a6723b8c76892684fd682493ec5e628 --- /dev/null +++ b/CBSVisualizer/CBSVisualizer.Services.StatisticsService/PacketTimestampCollection.cs @@ -0,0 +1,26 @@ +using System; +using CBSVisualizer.Messaging.Models; + +namespace CBSVisualizer.Services.StatisticsService +{ + /// <summary> + /// An object that allows to keep track of the timestamps that belong to a specific packet. + /// </summary> + public class PacketTimestampCollection + { + /// <summary> + /// The packet this timestamp belongs to. + /// </summary> + public PriorityPacket Packet { get; set; } + + /// <summary> + /// The arrival timestamp. + /// </summary> + public DateTime? Arrival { get; set; } + + /// <summary> + /// The departure timestamp (= the timestamp of the time when the packet transmission finished). + /// </summary> + public DateTime? Departure { get; set; } + } +} diff --git a/CBSVisualizer/CBSVisualizer.Services.StatisticsService/StatisticsService.cs b/CBSVisualizer/CBSVisualizer.Services.StatisticsService/StatisticsService.cs new file mode 100644 index 0000000000000000000000000000000000000000..1c2b93e05c761f0b0913013b46da88cff32d7566 --- /dev/null +++ b/CBSVisualizer/CBSVisualizer.Services.StatisticsService/StatisticsService.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using CBSVisualizer.Common.Interfaces; +using CBSVisualizer.Messaging.Models; +using MathNet.Numerics.Statistics; +using Prism.Mvvm; + +namespace CBSVisualizer.Services.StatisticsService +{ + public class StatisticsService : BindableBase, IStatisticsService + { + /// <summary> + /// Contains the timestamps for each queue. + /// </summary> + private ConcurrentDictionary<int, ConcurrentBag<PacketTimestampCollection>> dataPerPriority = new ConcurrentDictionary<int, ConcurrentBag<PacketTimestampCollection>>(); + + private Dictionary<int, IStatisticsSet<double>> statistics = new Dictionary<int, IStatisticsSet<double>>(); + public Dictionary<int, IStatisticsSet<double>> Statistics + { + get => statistics; + set => SetProperty(ref statistics, value); + } + + public void RegisterPacket(PriorityPacket newPacket) + { + // Don't care about PriorityPacket.NoPacket. + if (newPacket.Equals(PriorityPacket.NoPacket)) + { + return; + } + + lock (dataPerPriority) + { + // Add a new record with Departure = null so that we know that this packet did not depart yet. + dataPerPriority.GetOrAdd(newPacket.Priority, new ConcurrentBag<PacketTimestampCollection>()) + .Add(new PacketTimestampCollection { Packet = newPacket, Arrival = DateTime.Now, Departure = null }); + } + + } + + public void TransmissionFinished(PriorityPacket finishedPacket) + { + lock (dataPerPriority) + { + // Set now as the Departure DateTime for the specified packet. + if (!dataPerPriority.ContainsKey(finishedPacket.Priority)) + { + return; + } + + var finishedPacketCollection = dataPerPriority[finishedPacket.Priority].Where(timestampData => timestampData.Packet.Equals(finishedPacket)).ToList(); + + if (finishedPacketCollection.Count == 1) + { + finishedPacketCollection.First().Departure = DateTime.Now; + } + } + } + + public void SimulationStarted() + { + // Go through all the packets which were present before the simulation start and set their arrival to "Now" so that the arrival time is correct. + var now = DateTime.Now; + + lock (dataPerPriority) + { + foreach (var priorityPacketTimestamps in dataPerPriority.Values) + { + foreach (var timestampCollection in priorityPacketTimestamps) + { + timestampCollection.Arrival = now; + } + } + } + } + + public void SimulationStopped() + { + var cleanedDataPerPriority = new ConcurrentDictionary<int, ConcurrentBag<PacketTimestampCollection>>(); + + lock (dataPerPriority) + { + // Throw all the records of the packets away that did not departure during the simulation. + foreach (var (priority, timestamps) in dataPerPriority) + { + cleanedDataPerPriority[priority] = new ConcurrentBag<PacketTimestampCollection>(timestamps.Where(timestamp => timestamp.Departure != null)); + } + + // Throw the old data away. + dataPerPriority = cleanedDataPerPriority; + + // Calculate the new statistics. + UpdateStatistics(); + } + } + + public void UpdateStatistics() + { + var result = new Dictionary<int, IStatisticsSet<double>>(); + + lock (dataPerPriority) + { + // Create statistics for each priority. + foreach (var (priority, data) in dataPerPriority) + { + // Calculate delays. + var delays = data.Select(collection => + { + if (collection.Arrival.HasValue && collection.Departure.HasValue) + { + return collection.Departure.Value.Subtract(collection.Arrival.Value).TotalMilliseconds; + } + return 0; + }).ToList(); + + // Calculate the statistics. + result[priority] = new StatisticsSet<double> { RawValues = delays, FiveNumberSummary = delays.FiveNumberSummary() }; + } + + Statistics = result; + } + } + } +} diff --git a/CBSVisualizer/CBSVisualizer.Services.StatisticsService/StatisticsSet.cs b/CBSVisualizer/CBSVisualizer.Services.StatisticsService/StatisticsSet.cs new file mode 100644 index 0000000000000000000000000000000000000000..4120976d7db8d95768b89bc00b77f588575d60c9 --- /dev/null +++ b/CBSVisualizer/CBSVisualizer.Services.StatisticsService/StatisticsSet.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CBSVisualizer.Common.Interfaces; +using Prism.Mvvm; + +namespace CBSVisualizer.Services.StatisticsService +{ + public class StatisticsSet<T> : BindableBase, IStatisticsSet<T> + { + private IEnumerable<T> rawValues; + public IEnumerable<T> RawValues { get => rawValues; set => SetProperty(ref rawValues, value); } + + private IEnumerable<T> fiveNumberSummary; + public IEnumerable<T> FiveNumberSummary { get => fiveNumberSummary; set => SetProperty(ref fiveNumberSummary, value); } + } +} diff --git a/CBSVisualizer/CBSVisualizer.sln b/CBSVisualizer/CBSVisualizer.sln index d8c9c6863beaeb7647f41e4ab056a47a6683648d..5d2a9a0c1486343e7a3232ab7c92b45b222bf5bd 100644 --- a/CBSVisualizer/CBSVisualizer.sln +++ b/CBSVisualizer/CBSVisualizer.sln @@ -40,7 +40,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CBSVisualizer.Services.Pack EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CBSVisualizer.Modules.Charts", "CBSVisualizer.Modules.Charts\CBSVisualizer.Modules.Charts.csproj", "{FD3FDABD-9C06-4AC7-A348-68D39929A038}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CBSVisualizer.Services.JsonService", "CBSVisualizer.Services.JsonService\CBSVisualizer.Services.JsonService.csproj", "{97D76788-9F72-4031-AF3C-BAF5E1F53A69}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CBSVisualizer.Services.JsonService", "CBSVisualizer.Services.JsonService\CBSVisualizer.Services.JsonService.csproj", "{97D76788-9F72-4031-AF3C-BAF5E1F53A69}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CBSVisualizer.Services.StatisticsService", "CBSVisualizer.Services.StatisticsService\CBSVisualizer.Services.StatisticsService.csproj", "{9B27F8F5-F9DB-4F8C-A545-364ED0D53B9F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CBSVisualizer.Modules.StatisticsDialog", "CBSVisualizer.Modules.StatisticsDialog\CBSVisualizer.Modules.StatisticsDialog.csproj", "{B59F13FE-76B5-4C59-B37D-C462F7F431EB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -104,6 +108,14 @@ Global {97D76788-9F72-4031-AF3C-BAF5E1F53A69}.Debug|Any CPU.Build.0 = Debug|Any CPU {97D76788-9F72-4031-AF3C-BAF5E1F53A69}.Release|Any CPU.ActiveCfg = Release|Any CPU {97D76788-9F72-4031-AF3C-BAF5E1F53A69}.Release|Any CPU.Build.0 = Release|Any CPU + {9B27F8F5-F9DB-4F8C-A545-364ED0D53B9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9B27F8F5-F9DB-4F8C-A545-364ED0D53B9F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9B27F8F5-F9DB-4F8C-A545-364ED0D53B9F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9B27F8F5-F9DB-4F8C-A545-364ED0D53B9F}.Release|Any CPU.Build.0 = Release|Any CPU + {B59F13FE-76B5-4C59-B37D-C462F7F431EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B59F13FE-76B5-4C59-B37D-C462F7F431EB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B59F13FE-76B5-4C59-B37D-C462F7F431EB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B59F13FE-76B5-4C59-B37D-C462F7F431EB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -120,6 +132,8 @@ Global {274C0DC3-C59C-4C91-8130-6DEF2BDBD0A1} = {BFDCC6CE-CAA6-45AA-832F-0CBAB9A3C8BA} {FD3FDABD-9C06-4AC7-A348-68D39929A038} = {6D2194E0-E17B-44D6-AD8C-F3699549D259} {97D76788-9F72-4031-AF3C-BAF5E1F53A69} = {BFDCC6CE-CAA6-45AA-832F-0CBAB9A3C8BA} + {9B27F8F5-F9DB-4F8C-A545-364ED0D53B9F} = {BFDCC6CE-CAA6-45AA-832F-0CBAB9A3C8BA} + {B59F13FE-76B5-4C59-B37D-C462F7F431EB} = {6D2194E0-E17B-44D6-AD8C-F3699549D259} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {EE8EFE44-67AF-451F-9DFD-7B458AA3B3B3} diff --git a/CBSVisualizer/CBSVisualizer/App.xaml.cs b/CBSVisualizer/CBSVisualizer/App.xaml.cs index aced66513c3d014ef12ff855c3fbcd34fd4e7b69..50924abdd8bcb0bdf4afda048bcb57d84fb4e02a 100644 --- a/CBSVisualizer/CBSVisualizer/App.xaml.cs +++ b/CBSVisualizer/CBSVisualizer/App.xaml.cs @@ -8,9 +8,11 @@ using CBSVisualizer.Modules.SettingsDialog; using CBSVisualizer.Modules.MenuBar; using CBSVisualizer.Modules.Charts; using CBSVisualizer.Modules.QueueGroup; +using CBSVisualizer.Modules.StatisticsDialog; using CBSVisualizer.Services.PacketService; using CBSVisualizer.Services.SchedulingService; using CBSVisualizer.Services.SettingService; +using CBSVisualizer.Services.StatisticsService; using SciChart.Charting.Visuals; namespace CBSVisualizer @@ -36,6 +38,7 @@ namespace CBSVisualizer containerRegistry.RegisterSingleton<SettingService>(); containerRegistry.RegisterSingleton<ISchedulingService, SchedulingServiceProxy>(); containerRegistry.RegisterSingleton<IPacketService, PacketServiceProxy>(); + containerRegistry.RegisterSingleton<IStatisticsService, StatisticsService>(); } protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) @@ -46,6 +49,7 @@ namespace CBSVisualizer moduleCatalog.AddModule<QueueGroupModule>(); moduleCatalog.AddModule<MenuBarModule>(); moduleCatalog.AddModule<ChartsModule>(); + moduleCatalog.AddModule<StatisticsDialogModule>(); } } } diff --git a/CBSVisualizer/CBSVisualizer/CBSVisualizer.csproj b/CBSVisualizer/CBSVisualizer/CBSVisualizer.csproj index bcbaa66d6567ea90fd752e55753b680e4a8c33c3..78035b5c3259401f73b87de70ee66b1a2b2cacda 100644 --- a/CBSVisualizer/CBSVisualizer/CBSVisualizer.csproj +++ b/CBSVisualizer/CBSVisualizer/CBSVisualizer.csproj @@ -1,15 +1,15 @@ -<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> +<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>WinExe</OutputType> <LangVersion>latest</LangVersion> - <TargetFramework>net5.0</TargetFramework> + <TargetFramework>net5.0-windows</TargetFramework> <UseWPF>true</UseWPF> <AssemblyName>CBSVisualizer</AssemblyName> </PropertyGroup> <ItemGroup> - <PackageReference Include="log4net" Version="2.0.11" /> + <PackageReference Include="log4net" Version="2.0.12" /> <PackageReference Include="MaterialDesignThemes" Version="3.2.0" /> - <PackageReference Include="Prism.DryIoc" Version="7.2.0.1422" /> + <PackageReference Include="Prism.DryIoc" Version="8.0.0.1909" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\CBSVisualizer.Modules.Charts\CBSVisualizer.Modules.Charts.csproj" /> @@ -17,9 +17,11 @@ <ProjectReference Include="..\CBSVisualizer.Modules.MenuBar\CBSVisualizer.Modules.MenuBar.csproj" /> <ProjectReference Include="..\CBSVisualizer.Modules.Queue.QueueGroup\CBSVisualizer.Modules.QueueGroup.csproj" /> <ProjectReference Include="..\CBSVisualizer.Modules.SettingsDialog\CBSVisualizer.Modules.SettingsDialog.csproj" /> + <ProjectReference Include="..\CBSVisualizer.Modules.StatisticsDialog\CBSVisualizer.Modules.StatisticsDialog.csproj" /> <ProjectReference Include="..\CBSVisualizer.Services.PacketService\CBSVisualizer.Services.PacketService.csproj" /> <ProjectReference Include="..\CBSVisualizer.Services.SchedulingService\CBSVisualizer.Services.SchedulingService.csproj" /> <ProjectReference Include="..\CBSVisualizer.Services.SettingsService\CBSVisualizer.Services.SettingService.csproj" /> + <ProjectReference Include="..\CBSVisualizer.Services.StatisticsService\CBSVisualizer.Services.StatisticsService.csproj" /> </ItemGroup> <ItemGroup> <None Update="log4net.config">