diff --git a/PowerToys.slnx b/PowerToys.slnx index dd79e518fb..c93236d9a1 100644 --- a/PowerToys.slnx +++ b/PowerToys.slnx @@ -333,6 +333,10 @@ + + + + diff --git a/src/modules/cmdpal/CommandPalette - no UI tests.slnf b/src/modules/cmdpal/CommandPalette - no UI tests.slnf index 74ad9d58a7..57caced6f4 100644 --- a/src/modules/cmdpal/CommandPalette - no UI tests.slnf +++ b/src/modules/cmdpal/CommandPalette - no UI tests.slnf @@ -19,7 +19,9 @@ "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Apps.UnitTests\\Microsoft.CmdPal.Ext.Apps.UnitTests.csproj", "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Bookmarks.UnitTests\\Microsoft.CmdPal.Ext.Bookmarks.UnitTests.csproj", "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Calc.UnitTests\\Microsoft.CmdPal.Ext.Calc.UnitTests.csproj", - "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.ClipboardHistory.UnitTests\\Microsoft.CmdPal.Ext.ClipboardHistory.UnitTests.csproj", + "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.ClipboardHistory.UnitTests\\Microsoft.CmdPal.Ext.ClipboardHistory.UnitTests.csproj", + "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Indexer.UnitTests\\Microsoft.CmdPal.Ext.Indexer.UnitTests.csproj", + "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests\\Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests.csproj", "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Registry.UnitTests\\Microsoft.CmdPal.Ext.Registry.UnitTests.csproj", "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.WindowsSettings.UnitTests\\Microsoft.CmdPal.Ext.WindowsSettings.UnitTests.csproj", "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.RemoteDesktop.UnitTests\\Microsoft.CmdPal.Ext.RemoteDesktop.UnitTests.csproj", diff --git a/src/modules/cmdpal/CommandPalette.slnf b/src/modules/cmdpal/CommandPalette.slnf index 2d2ef72ae9..fd543f357d 100644 --- a/src/modules/cmdpal/CommandPalette.slnf +++ b/src/modules/cmdpal/CommandPalette.slnf @@ -24,6 +24,7 @@ "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Calc.UnitTests\\Microsoft.CmdPal.Ext.Calc.UnitTests.csproj", "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.ClipboardHistory.UnitTests\\Microsoft.CmdPal.Ext.ClipboardHistory.UnitTests.csproj", "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Indexer.UnitTests\\Microsoft.CmdPal.Ext.Indexer.UnitTests.csproj", + "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests\\Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests.csproj", "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Registry.UnitTests\\Microsoft.CmdPal.Ext.Registry.UnitTests.csproj", "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.RemoteDesktop.UnitTests\\Microsoft.CmdPal.Ext.RemoteDesktop.UnitTests.csproj", "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Shell.UnitTests\\Microsoft.CmdPal.Ext.Shell.UnitTests.csproj", diff --git a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests/FormatIncomingDataTests.cs b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests/FormatIncomingDataTests.cs new file mode 100644 index 0000000000..806be3407a --- /dev/null +++ b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests/FormatIncomingDataTests.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests; + +[TestClass] +public class FormatIncomingDataTests +{ + [DataTestMethod] + [DataRow(0f, "0.0 Kbps")] + [DataRow(6400f, "50.0 Kbps")] + [DataRow(12793f, "99.9 Kbps")] + [DataRow(12800f, "100 Kbps")] + [DataRow(127872f, "999 Kbps")] + [DataRow(131072f, "1.0 Mbps")] + [DataRow(6553600f, "50.0 Mbps")] + [DataRow(13107200f, "100 Mbps")] + [DataRow(134217728f, "1.0 Gbps")] + [DataRow(6710886400f, "50.0 Gbps")] + [DataRow(13421772800f, "100 Gbps")] + public void AsBitsPerSecString_FormatsBoundaryValues(float bytesPerSecond, string expected) + { + Assert.AreEqual(expected, FormatIncomingData.AsBitsPerSecString(bytesPerSecond)); + } + + [DataTestMethod] + [DataRow(0f, "0.0 KB/s")] + [DataRow(50000f, "50.0 KB/s")] + [DataRow(100000f, "100 KB/s")] + [DataRow(999000f, "999 KB/s")] + [DataRow(1000000f, "1.0 MB/s")] + [DataRow(50000000f, "50.0 MB/s")] + [DataRow(100000000f, "100 MB/s")] + [DataRow(1000000000f, "1.0 GB/s")] + [DataRow(50000000000f, "50.0 GB/s")] + [DataRow(100000000000f, "100 GB/s")] + public void AsBytesPerSecString_FormatsBoundaryValues(float bytesPerSecond, string expected) + { + Assert.AreEqual(expected, FormatIncomingData.AsBytesPerSecString(bytesPerSecond)); + } + + [TestMethod] + public void AsBytesPerSecString_UsesDecimalScalingDistinctFromBinary() + { + const float bytesPerSecond = 51200f; + + Assert.AreEqual("51.2 KB/s", FormatIncomingData.AsBytesPerSecString(bytesPerSecond)); + Assert.AreEqual("50.0 KiB/s", FormatIncomingData.AsBinaryBytesPerSecString(bytesPerSecond)); + } + + [DataTestMethod] + [DataRow(0f, "0.0 KiB/s")] + [DataRow(51200f, "50.0 KiB/s")] + [DataRow(102400f, "100 KiB/s")] + [DataRow(1022976f, "999 KiB/s")] + [DataRow(1048576f, "1.0 MiB/s")] + [DataRow(52428800f, "50.0 MiB/s")] + [DataRow(104857600f, "100 MiB/s")] + [DataRow(1073741824f, "1.0 GiB/s")] + [DataRow(53687091200f, "50.0 GiB/s")] + [DataRow(107374182400f, "100 GiB/s")] + public void AsBinaryBytesPerSecString_FormatsBoundaryValues(float bytesPerSecond, string expected) + { + Assert.AreEqual(expected, FormatIncomingData.AsBinaryBytesPerSecString(bytesPerSecond)); + } +} diff --git a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests/Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests.csproj b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests/Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests.csproj new file mode 100644 index 0000000000..979bfc6190 --- /dev/null +++ b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests/Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests.csproj @@ -0,0 +1,23 @@ + + + + + + false + true + Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests + $(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal\tests\ + false + false + true + + + + + + + + + + + diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/ChartHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/ChartHelper.cs index bec3398c8b..4cc56866ef 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/ChartHelper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/ChartHelper.cs @@ -17,6 +17,7 @@ internal sealed class ChartHelper GPU, Mem, Net, + Dis, } public const int ChartHeight = 86; @@ -28,6 +29,7 @@ internal sealed class ChartHelper private const string GPULineStyle = "fill:none;stroke:rgb(222,104,242);stroke-width:1"; private const string MemLineStyle = "fill:none;stroke:rgb(92,158,250);stroke-width:1"; private const string NetLineStyle = "fill:none;stroke:rgb(245,98,142);stroke-width:1"; + private const string DisLineStyle = "fill:none;stroke:rgb(103,153,24);stroke-width:1"; private const string FillStyle = "fill:url(#gradientId);stroke:transparent"; @@ -43,6 +45,9 @@ internal sealed class ChartHelper private const string NetBrushStop1Style = "stop-color:rgb(245,98,142);stop-opacity:0.4"; private const string NetBrushStop2Style = "stop-color:rgb(130,0,47);stop-opacity:0.25"; + private const string DisBrushStop1Style = "stop-color:rgb(103,153,24);stop-opacity:0.4"; + private const string DisBrushStop2Style = "stop-color:rgb(45,62,15);stop-opacity:0.25"; + private const string SvgElement = "svg"; private const string RectElement = "rect"; private const string PolylineElement = "polyline"; @@ -174,6 +179,10 @@ internal sealed class ChartHelper stop1Style = NetBrushStop1Style; stop2Style = NetBrushStop2Style; break; + case ChartType.Dis: + stop1Style = DisBrushStop1Style; + stop2Style = DisBrushStop2Style; + break; case ChartType.CPU: default: stop1Style = CPUBrushStop1Style; @@ -213,6 +222,7 @@ internal sealed class ChartHelper ChartType.GPU => GPULineStyle, ChartType.Mem => MemLineStyle, ChartType.Net => NetLineStyle, + ChartType.Dis => DisLineStyle, _ => CPULineStyle, }; diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/DataManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/DataManager.cs index 8dc80db698..aefc001de9 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/DataManager.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/DataManager.cs @@ -45,6 +45,14 @@ internal sealed partial class DataManager : IDisposable } } + private void GetDiskData() + { + lock (_systemData.DiskStats) + { + _systemData.DiskStats.GetData(); + } + } + private void GetGPUData() { lock (_systemData.GPUStats) @@ -107,6 +115,13 @@ internal sealed partial class DataManager : IDisposable break; } + case DataType.Disk: + { + // disk + GetDiskData(); + break; + } + case DataType.Battery: { GetBatteryData(); @@ -146,6 +161,7 @@ internal sealed partial class DataManager : IDisposable DataType.GPU => "GPU.FirstUpdate", DataType.Memory => "Memory.FirstUpdate", DataType.Network => "Network.FirstUpdate", + DataType.Disk => "Disk.FirstUpdate", DataType.Battery => "Battery.FirstUpdate", _ => null, }; @@ -167,6 +183,14 @@ internal sealed partial class DataManager : IDisposable } } + internal DiskStats GetDiskStats() + { + lock (_systemData.DiskStats) + { + return _systemData.DiskStats; + } + } + internal GPUStats GetGPUStats() { lock (_systemData.GPUStats) diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/DataType.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/DataType.cs index e9af0f6986..700e9ccf4c 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/DataType.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/DataType.cs @@ -37,4 +37,9 @@ public enum DataType /// Battery related data. /// Battery, + + /// + /// Disk related data. + /// + Disk, } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/DiskStats.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/DiskStats.cs new file mode 100644 index 0000000000..8f5e629fa1 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/DiskStats.cs @@ -0,0 +1,197 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using Microsoft.CmdPal.Common; + +namespace CoreWidgetProvider.Helpers; + +internal sealed partial class DiskStats : PerformanceCounterSourceBase, IDisposable +{ + private readonly Dictionary> _diskCounters = new(); + private bool _diskCounterReadFailureLogged; + + private Dictionary DiskUsages { get; set; } = new(); + + private Dictionary> DiskChartValues { get; set; } = new(); + + public sealed class Data + { + public float Usage + { + get; set; + } + + public float Read + { + get; set; + } + + public float Written + { + get; set; + } + } + + public DiskStats() + { + InitDiskPerfCounters(); + } + + private void InitDiskPerfCounters() + { + try + { + var perfCounterCategory = CreatePerformanceCounterCategory("PhysicalDisk"); + if (perfCounterCategory is null) + { + return; + } + + var instanceNames = perfCounterCategory.GetInstanceNames(); + foreach (var instanceName in instanceNames) + { + if (string.Equals(instanceName, "_Total", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + try + { + var bytesRead = CreatePerformanceCounter("PhysicalDisk", "Disk Read Bytes/sec", instanceName, logFailure: false); + var bytesWritten = CreatePerformanceCounter("PhysicalDisk", "Disk Write Bytes/sec", instanceName, logFailure: false); + var diskTime = CreatePerformanceCounter("PhysicalDisk", "% Disk Time", instanceName, logFailure: false); + if (bytesRead is null || bytesWritten is null || diskTime is null) + { + bytesRead?.Dispose(); + bytesWritten?.Dispose(); + diskTime?.Dispose(); + continue; + } + + var instanceCounters = new List { bytesRead, bytesWritten, diskTime }; + _diskCounters.Add(instanceName, instanceCounters); + DiskChartValues.Add(instanceName, new List()); + DiskUsages.Add(instanceName, new Data()); + } + catch (Exception) + { + // Skip interfaces whose counters cannot be initialized. + } + } + } + catch (Exception ex) + { + CoreLogger.LogError("Failed to initialize disk performance counters.", ex); + } + } + + public void GetData() + { + foreach (var diskCounterWithName in _diskCounters) + { + try + { + var read = diskCounterWithName.Value[0].NextValue(); + var written = diskCounterWithName.Value[1].NextValue(); + + var diskTimePercent = Math.Clamp(diskCounterWithName.Value[2].NextValue(), 0f, 100f); + var name = diskCounterWithName.Key; + + DiskUsages[name].Read = read; + DiskUsages[name].Written = written; + DiskUsages[name].Usage = diskTimePercent / 100f; + + var chartValues = DiskChartValues[name]; + lock (chartValues) + { + ChartHelper.AddNextChartValue(diskTimePercent, chartValues); + } + } + catch (Exception ex) + { + LogFailureOnce(ref _diskCounterReadFailureLogged, "Failed while reading disk performance counters.", ex); + } + } + } + + public string CreateDiskImageUrl(int diskChartIndex) + { + var chartValues = DiskChartValues.ElementAt(diskChartIndex).Value; + lock (chartValues) + { + return ChartHelper.CreateImageUrl(chartValues, ChartHelper.ChartType.Dis); + } + } + + public string GetDiskName(int diskIndex) + { + if (DiskChartValues.Count <= diskIndex) + { + return string.Empty; + } + + return DiskChartValues.ElementAt(diskIndex).Key; + } + + public Data GetDiskUsage(int diskIndex) + { + if (DiskChartValues.Count <= diskIndex) + { + return new Data(); + } + + var currDiskName = DiskChartValues.ElementAt(diskIndex).Key; + if (!DiskUsages.TryGetValue(currDiskName, out var value)) + { + return new Data(); + } + + return value; + } + + public int GetPrevDiskIndex(int diskIndex) + { + if (DiskChartValues.Count == 0) + { + return 0; + } + + if (diskIndex == 0) + { + return DiskChartValues.Count - 1; + } + + return diskIndex - 1; + } + + public int GetNextDiskIndex(int diskIndex) + { + if (DiskChartValues.Count == 0) + { + return 0; + } + + if (diskIndex == DiskChartValues.Count - 1) + { + return 0; + } + + return diskIndex + 1; + } + + public void Dispose() + { + foreach (var counterPair in _diskCounters) + { + foreach (var counter in counterPair.Value) + { + counter.Dispose(); + } + } + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/SystemData.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/SystemData.cs index 55fdbc53db..1abab760d2 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/SystemData.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/SystemData.cs @@ -13,6 +13,7 @@ internal sealed partial class SystemData private readonly Lazy _memoryStats = new(() => CreateGuarded("Memory.Initialize", static () => new MemoryStats())); private readonly Lazy _networkStats = new(() => CreateGuarded("Network.Initialize", static () => new NetworkStats())); + private readonly Lazy _diskStats = new(() => CreateGuarded("Disk.Initialize", static () => new DiskStats())); private readonly Lazy _gpuStats = new(() => CreateGuarded("GPU.Initialize", static () => new GPUStats())); private readonly Lazy _cpuStats = new(() => CreateGuarded("CPU.Initialize", static () => new CPUStats())); private readonly Lazy _batteryStats = new(() => CreateGuarded("Battery.Initialize", static () => new BatteryStats())); @@ -21,6 +22,8 @@ internal sealed partial class SystemData public NetworkStats NetworkStats => _networkStats.Value; + public DiskStats DiskStats => _diskStats.Value; + public GPUStats GPUStats => _gpuStats.Value; public CPUStats CpuStats => _cpuStats.Value; diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Templates/SystemDiskUsageTemplate.json b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Templates/SystemDiskUsageTemplate.json new file mode 100644 index 0000000000..851f689b0c --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Templates/SystemDiskUsageTemplate.json @@ -0,0 +1,106 @@ +{ + "type": "AdaptiveCard", + "body": [ + { + "type": "Container", + "$when": "${errorMessage != null}", + "items": [ + { + "type": "TextBlock", + "text": "${errorMessage}", + "wrap": true, + "size": "small" + } + ], + "style": "warning" + }, + { + "type": "Container", + "$when": "${errorMessage == null}", + "items": [ + { + "type": "Image", + "url": "${diskGraphUrl}", + "height": "${chartHeight}", + "width": "${chartWidth}", + "$when": "${$host.widgetSize != \"small\"}", + "horizontalAlignment": "center" + }, + { + "type": "ColumnSet", + "columns": [ + { + "type": "Column", + "items": [ + { + "text": "%DiskUsage_Widget_Template/Disk_Usage%", + "type": "TextBlock", + "spacing": "none", + "size": "small", + "isSubtle": true + }, + { + "text": "${diskUsage}", + "type": "TextBlock", + "size": "large", + "weight": "bolder" + } + ] + }, + { + "type": "Column", + "items": [ + { + "text": "%DiskUsage_Widget_Template/Read%", + "type": "TextBlock", + "spacing": "none", + "size": "small", + "isSubtle": true + }, + { + "text": "${diskRead}", + "type": "TextBlock", + "size": "large", + "weight": "bolder" + } + ] + }, + { + "type": "Column", + "items": [ + { + "text": "%DiskUsage_Widget_Template/Write%", + "type": "TextBlock", + "spacing": "none", + "size": "small", + "isSubtle": true, + "horizontalAlignment": "right" + }, + { + "text": "${diskWrite}", + "type": "TextBlock", + "size": "large", + "weight": "bolder", + "horizontalAlignment": "right" + } + ] + } + ] + }, + { + "text": "%DiskUsage_Widget_Template/Disk_Name%", + "type": "TextBlock", + "size": "small", + "isSubtle": true + }, + { + "text": "${diskName}", + "type": "TextBlock", + "size": "medium" + } + ] + } + ], + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "version": "1.5" +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Icons.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Icons.cs index 02b704d3d4..56fb01896c 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Icons.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Icons.cs @@ -19,6 +19,10 @@ internal static class Icons internal static IconInfo HardDriveIcon => new("\uEDA2"); // HardDrive icon + internal static IconInfo FileReadIcon => new("\uE890"); // FileRead icon + + internal static IconInfo FileWriteIcon => new("\uE70F"); // FileWrite icon + internal static IconInfo NetworkIcon => new("\uEC05"); // Network icon internal static IconInfo NetworkUpIcon => new("\uE74A"); // Up arrow icon diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Microsoft.CmdPal.Ext.PerformanceMonitor.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Microsoft.CmdPal.Ext.PerformanceMonitor.csproj index 9ec357605e..319296f540 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Microsoft.CmdPal.Ext.PerformanceMonitor.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Microsoft.CmdPal.Ext.PerformanceMonitor.csproj @@ -50,6 +50,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/PerformanceMonitorCommandsProvider.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/PerformanceMonitorCommandsProvider.cs index aaf21373a1..ffdea05b1c 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/PerformanceMonitorCommandsProvider.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/PerformanceMonitorCommandsProvider.cs @@ -39,6 +39,7 @@ public partial class PerformanceMonitorCommandsProvider : CommandProvider private PerformanceWidgetsPage? _bandPage; private PerformanceWidgetsPage? _cpuBandPage; private PerformanceWidgetsPage? _memoryBandPage; + private PerformanceWidgetsPage? _diskBandPage; private PerformanceWidgetsPage? _networkBandPage; private PerformanceWidgetsPage? _gpuBandPage; private PerformanceWidgetsPage? _batteryBandPage; @@ -198,6 +199,7 @@ public partial class PerformanceMonitorCommandsProvider : CommandProvider _cpuBandPage = new PerformanceWidgetsPage(_settingsManager, true, PerformanceMetricKind.Cpu); _memoryBandPage = new PerformanceWidgetsPage(_settingsManager, true, PerformanceMetricKind.Memory); _networkBandPage = new PerformanceWidgetsPage(_settingsManager, true, PerformanceMetricKind.Network); + _diskBandPage = new PerformanceWidgetsPage(_settingsManager, true, PerformanceMetricKind.Disk); _gpuBandPage = new PerformanceWidgetsPage(_settingsManager, true, PerformanceMetricKind.Gpu); _batteryBandPage = new PerformanceWidgetsPage(_settingsManager, true, PerformanceMetricKind.Battery); @@ -206,6 +208,7 @@ public partial class PerformanceMonitorCommandsProvider : CommandProvider new CommandItem(_cpuBandPage) { Title = Resources.GetResource("CPU_Usage_Title") }, new CommandItem(_memoryBandPage) { Title = Resources.GetResource("Memory_Usage_Title") }, new CommandItem(_networkBandPage) { Title = Resources.GetResource("Network_Usage_Title") }, + new CommandItem(_diskBandPage) { Title = Resources.GetResource("Disk_Usage_Title") }, new CommandItem(_gpuBandPage) { Title = Resources.GetResource("GPU_Usage_Title") } ]; var batteryStats = new BatteryStats(); @@ -242,6 +245,9 @@ public partial class PerformanceMonitorCommandsProvider : CommandProvider _memoryBandPage?.Dispose(); _memoryBandPage = null; + _diskBandPage?.Dispose(); + _diskBandPage = null; + _networkBandPage?.Dispose(); _networkBandPage = null; diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/PerformanceWidgetsPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/PerformanceWidgetsPage.cs index 1459e5db28..90fd33d412 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/PerformanceWidgetsPage.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/PerformanceWidgetsPage.cs @@ -31,6 +31,7 @@ internal enum PerformanceMetricKind Cpu, Memory, Network, + Disk, Gpu, Battery, } @@ -59,6 +60,7 @@ internal sealed partial class PerformanceWidgetsPage : OnLoadStaticListPage, IDi PerformanceMetricKind.Cpu => Icons.CpuIcon, PerformanceMetricKind.Memory => Icons.MemoryIcon, PerformanceMetricKind.Network => Icons.NetworkIcon, + PerformanceMetricKind.Disk => Icons.HardDriveIcon, PerformanceMetricKind.Gpu => Icons.GpuIcon, PerformanceMetricKind.Battery => _batteryPage?.CurrentIcon ?? Icons.BatteryIcon, _ => Icons.PerformanceMonitorIcon, @@ -74,6 +76,9 @@ internal sealed partial class PerformanceWidgetsPage : OnLoadStaticListPage, IDi private readonly SystemMemoryUsageWidgetPage? _memoryPage; private readonly ListItem? _memoryItem; + private readonly SystemDiskUsageWidgetPage? _diskPage; + private readonly ListItem? _diskItem; + private readonly SystemNetworkUsageWidgetPage? _networkPage; private readonly ListItem? _networkItem; @@ -89,6 +94,12 @@ internal sealed partial class PerformanceWidgetsPage : OnLoadStaticListPage, IDi private string _networkUpSpeed = string.Empty; private string _networkDownSpeed = string.Empty; + // For bands, we want two bands, one for read and one for write + private ListItem? _diskReadItem; + private ListItem? _diskWriteItem; + private string _diskReadSpeed = string.Empty; + private string _diskWriteSpeed = string.Empty; + public PerformanceWidgetsPage(SettingsManager settingsManager, bool isBandPage = false, PerformanceMetricKind? singleMetric = null) { _isBandPage = isBandPage; @@ -163,6 +174,26 @@ internal sealed partial class PerformanceWidgetsPage : OnLoadStaticListPage, IDi }; } + if (IncludesMetric(PerformanceMetricKind.Disk)) + { + _diskPage = new SystemDiskUsageWidgetPage(settingsManager); + _diskItem = new ListItem(_diskPage) + { + Title = _diskPage.GetItemTitle(isBandPage), + MoreCommands = _diskPage.Commands, + }; + + _diskPage.Updated += (s, e) => + { + _diskItem.Title = _diskPage.GetItemTitle(isBandPage); + _diskReadSpeed = _diskPage.GetReadSpeed(); + _diskWriteSpeed = _diskPage.GetWriteSpeed(); + _diskReadItem?.Title = $"{_diskReadSpeed}"; + _diskWriteItem?.Title = $"{_diskWriteSpeed}"; + RaiseItemsChanged(); + }; + } + if (IncludesMetric(PerformanceMetricKind.Gpu)) { _gpuPage = new SystemGPUUsageWidgetPage(); @@ -225,6 +256,12 @@ internal sealed partial class PerformanceWidgetsPage : OnLoadStaticListPage, IDi _networkItem.Subtitle = Resources.GetResource("Network_Usage_Subtitle"); } + if (_diskItem is not null) + { + _diskItem.Subtitle = Resources.GetResource("Disk_Active_Time_Subtitle"); + _diskItem.Icon = Icons.HardDriveIcon; + } + if (_gpuItem is not null) { _gpuItem.Subtitle = Resources.GetResource("GPU_Usage_Subtitle"); @@ -242,6 +279,7 @@ internal sealed partial class PerformanceWidgetsPage : OnLoadStaticListPage, IDi _cpuPage?.PushActivate(); _memoryPage?.PushActivate(); _networkPage?.PushActivate(); + _diskPage?.PushActivate(); _gpuPage?.PushActivate(); _batteryPage?.PushActivate(); } @@ -251,6 +289,7 @@ internal sealed partial class PerformanceWidgetsPage : OnLoadStaticListPage, IDi _cpuPage?.PopActivate(); _memoryPage?.PopActivate(); _networkPage?.PopActivate(); + _diskPage?.PopActivate(); _gpuPage?.PopActivate(); _batteryPage?.PopActivate(); } @@ -260,11 +299,17 @@ internal sealed partial class PerformanceWidgetsPage : OnLoadStaticListPage, IDi // Per-metric pages just return the single matching item. if (_singleMetric is PerformanceMetricKind metric) { + if (metric == PerformanceMetricKind.Disk && _isBandPage) + { + return CreateDiskBandItems(); + } + return metric switch { PerformanceMetricKind.Cpu => new IListItem[] { _cpuItem! }, PerformanceMetricKind.Memory => new IListItem[] { _memoryItem! }, PerformanceMetricKind.Network => new IListItem[] { _networkItem! }, + PerformanceMetricKind.Disk => new IListItem[] { _diskItem! }, PerformanceMetricKind.Gpu => new IListItem[] { _gpuItem! }, PerformanceMetricKind.Battery => new IListItem[] { _batteryItem! }, _ => Array.Empty(), @@ -275,22 +320,71 @@ internal sealed partial class PerformanceWidgetsPage : OnLoadStaticListPage, IDi { // TODO add details return _batteryItem is not null - ? new[] { _cpuItem!, _memoryItem!, _networkItem!, _gpuItem!, _batteryItem! } - : new[] { _cpuItem!, _memoryItem!, _networkItem!, _gpuItem! }; + ? new[] { _cpuItem!, _memoryItem!, _networkItem!, _diskItem!, _gpuItem!, _batteryItem! } + : new[] { _cpuItem!, _memoryItem!, _networkItem!, _diskItem!, _gpuItem! }; } else { + if (_networkUpItem is null) + { + _networkUpItem = new ListItem(_networkPage!) + { + Title = $"{_networkUpSpeed}", + Subtitle = Resources.GetResource("Network_Send_Subtitle"), + Icon = Icons.NetworkUpIcon, + MoreCommands = _networkPage!.Commands, + }; + } + + _networkUpItem.Title = _networkUpSpeed; + + if (_networkDownItem is null) + { + _networkDownItem = new ListItem(_networkPage!) + { + Title = $"{_networkDownSpeed}", + Subtitle = Resources.GetResource("Network_Receive_Subtitle"), + Icon = Icons.NetworkDownIcon, + MoreCommands = _networkPage!.Commands, + }; + } + + _networkDownItem.Title = _networkDownSpeed; + + CreateDiskBandItems(); return _batteryItem is not null - ? new[] { _cpuItem!, _memoryItem!, _networkUpItem!, _networkDownItem!, _gpuItem!, _batteryItem! } - : new[] { _cpuItem!, _memoryItem!, _networkUpItem!, _networkDownItem!, _gpuItem! }; + ? new[] { _cpuItem!, _memoryItem!, _networkUpItem!, _networkDownItem!, _diskReadItem!, _diskWriteItem!, _diskItem!, _gpuItem!, _batteryItem! } + : new[] { _cpuItem!, _memoryItem!, _networkUpItem!, _networkDownItem!, _diskReadItem!, _diskWriteItem!, _diskItem!, _gpuItem! }; } } + private IListItem[] CreateDiskBandItems() + { + _diskReadItem ??= new ListItem(_diskPage!) + { + Subtitle = Resources.GetResource("Disk_Read_Subtitle"), + Icon = Icons.FileReadIcon, + MoreCommands = _diskPage!.Commands, + }; + _diskReadItem.Title = _diskReadSpeed; + + _diskWriteItem ??= new ListItem(_diskPage!) + { + Subtitle = Resources.GetResource("Disk_Write_Subtitle"), + Icon = Icons.FileWriteIcon, + MoreCommands = _diskPage!.Commands, + }; + _diskWriteItem.Title = _diskWriteSpeed; + + return [_diskReadItem, _diskWriteItem, _diskItem!]; + } + public void Dispose() { _cpuPage?.Dispose(); _memoryPage?.Dispose(); _networkPage?.Dispose(); + _diskPage?.Dispose(); _gpuPage?.Dispose(); _batteryPage?.Dispose(); } @@ -312,6 +406,7 @@ internal sealed partial class PerformanceWidgetsPage : OnLoadStaticListPage, IDi PerformanceMetricKind.Cpu => "cpu", PerformanceMetricKind.Memory => "memory", PerformanceMetricKind.Network => "network", + PerformanceMetricKind.Disk => "disk", PerformanceMetricKind.Gpu => "gpu", PerformanceMetricKind.Battery => "battery", _ => "unknown", @@ -674,6 +769,198 @@ internal sealed partial class SystemMemoryUsageWidgetPage : WidgetPage, IDisposa } } +internal sealed partial class SystemDiskUsageWidgetPage : WidgetPage, IDisposable +{ + public override string Id => "com.microsoft.cmdpal.disk_widget"; + + public override string Title => Resources.GetResource("Disk_Usage_Title"); + + public override IconInfo Icon => Icons.HardDriveIcon; + + private readonly DataManager _dataManager; + private readonly SettingsManager _settingsManager; + private int _diskIndex; + + public SystemDiskUsageWidgetPage(SettingsManager settingsManager) + { + _settingsManager = settingsManager; + _dataManager = new(DataType.Disk, () => UpdateWidget()); + Commands = [ + new CommandContextItem(new PrevDiskCommand(this) { Name = Resources.GetResource("Previous_Disk_Title") }), + new CommandContextItem(new NextDiskCommand(this) { Name = Resources.GetResource("Next_Disk_Title") }), + new CommandContextItem(OpenTaskManagerCommand.Instance), + ]; + } + + protected override void LoadContentData() + { + // CoreLogger.LogDebug("Getting Disk stats"); + try + { + ContentData.Clear(); + + var timer = Stopwatch.StartNew(); + + var currentData = _dataManager.GetDiskStats(); + + var dataDuration = timer.ElapsedMilliseconds; + + var diskName = currentData.GetDiskName(_diskIndex); + var diskStats = currentData.GetDiskUsage(_diskIndex); + + ContentData["diskUsage"] = FloatToPercentString(diskStats.Usage); + ContentData["diskRead"] = SpeedToString(diskStats.Read); + ContentData["diskWrite"] = SpeedToString(diskStats.Written); + ContentData["diskName"] = diskName; + ContentData["diskGraphUrl"] = currentData.CreateDiskImageUrl(_diskIndex); + ContentData["chartHeight"] = ChartHelper.ChartHeight + "px"; + ContentData["chartWidth"] = ChartHelper.ChartWidth + "px"; + + var contentDuration = timer.ElapsedMilliseconds - dataDuration; + + // CoreLogger.LogDebug($"Disk stats retrieved in {dataDuration} ms, content prepared in {contentDuration} ms. (Total {timer.ElapsedMilliseconds} ms)"); + } + catch (Exception e) + { + ContentData.Clear(); + ContentData["errorMessage"] = e.Message; + return; + } + } + + protected override string GetTemplatePath(WidgetPageState page) + { + return page switch + { + WidgetPageState.Content => @"DevHome\Templates\SystemDiskUsageTemplate.json", + WidgetPageState.Loading => @"DevHome\Templates\SystemDiskUsageTemplate.json", + _ => throw new NotImplementedException(), + }; + } + + public string GetItemTitle(bool isBandPage) + { + if (ContentData.TryGetValue("diskName", out var name) && ContentData.TryGetValue("diskUsage", out var usage)) + { + return isBandPage ? usage : string.Format(CultureInfo.CurrentCulture, Resources.GetResource("Disk_Usage_Label"), name, usage); + } + else + { + return isBandPage ? Resources.GetResource("Disk_Usage_Unknown") : Resources.GetResource("Disk_Usage_Unknown_Label"); + } + } + + // read/write speed is always used for bands + public string GetReadSpeed() + { + if (ContentData.TryGetValue("diskRead", out var readSpeed)) + { + return readSpeed; + } + else + { + return "???"; + } + } + + public string GetWriteSpeed() + { + if (ContentData.TryGetValue("diskWrite", out var writeSpeed)) + { + return writeSpeed; + } + else + { + return "???"; + } + } + + private string SpeedToString(float bytesPerSec) + { + return _settingsManager.DiskSpeedUnit switch + { + SpeedUnit.BytesPerSecond => FormatIncomingData.AsBytesPerSecString(bytesPerSec), + SpeedUnit.BinaryBytesPerSecond => FormatIncomingData.AsBinaryBytesPerSecString(bytesPerSec), + _ => FormatIncomingData.AsBitsPerSecString(bytesPerSec), + }; + } + + internal override void PushActivate() + { + base.PushActivate(); + if (IsActive) + { + _dataManager.Start(); + } + } + + internal override void PopActivate() + { + base.PopActivate(); + if (!IsActive) + { + _dataManager.Stop(); + } + } + + private void HandlePrevDisk() + { + _diskIndex = _dataManager.GetDiskStats().GetPrevDiskIndex(_diskIndex); + UpdateWidget(); + } + + private void HandleNextDisk() + { + _diskIndex = _dataManager.GetDiskStats().GetNextDiskIndex(_diskIndex); + UpdateWidget(); + } + + public void Dispose() + { + _dataManager.Dispose(); + } + + private sealed partial class PrevDiskCommand : InvokableCommand + { + private readonly SystemDiskUsageWidgetPage _page; + + public PrevDiskCommand(SystemDiskUsageWidgetPage page) + { + _page = page; + } + + public override string Id => "com.microsoft.cmdpal.disk_widget.prev"; + + public override IconInfo Icon => Icons.NavigateBackwardIcon; + + public override ICommandResult Invoke() + { + _page.HandlePrevDisk(); + return CommandResult.KeepOpen(); + } + } + + private sealed partial class NextDiskCommand : InvokableCommand + { + private readonly SystemDiskUsageWidgetPage _page; + + public NextDiskCommand(SystemDiskUsageWidgetPage page) + { + _page = page; + } + + public override string Id => "com.microsoft.cmdpal.disk_widget.next"; + + public override IconInfo Icon => Icons.NavigateForwardIcon; + + public override ICommandResult Invoke() + { + _page.HandleNextDisk(); + return CommandResult.KeepOpen(); + } + } +} + internal sealed partial class SystemNetworkUsageWidgetPage : WidgetPage, IDisposable { public override string Id => "com.microsoft.cmdpal.network_widget"; @@ -784,123 +1071,12 @@ internal sealed partial class SystemNetworkUsageWidgetPage : WidgetPage, IDispos { return _settingsManager.NetworkSpeedUnit switch { - NetworkSpeedUnit.BytesPerSecond => FormatAsBytesPerSecString(bytesPerSec), - NetworkSpeedUnit.BinaryBytesPerSecond => FormatAsBinaryBytesPerSecString(bytesPerSec), - _ => FormatAsBitsPerSecString(bytesPerSec), + SpeedUnit.BytesPerSecond => FormatIncomingData.AsBytesPerSecString(bytesPerSec), + SpeedUnit.BinaryBytesPerSecond => FormatIncomingData.AsBinaryBytesPerSecString(bytesPerSec), + _ => FormatIncomingData.AsBitsPerSecString(bytesPerSec), }; } - private static string FormatAsBitsPerSecString(float value) - { - // Bytes to bits - value *= 8; - - // bits to Kbits - value /= 1024; - if (value < 1024) - { - if (value < 100) - { - return string.Format(CultureInfo.InvariantCulture, "{0:0.0} Kbps", value); - } - - return string.Format(CultureInfo.InvariantCulture, "{0:0} Kbps", value); - } - - // Kbits to Mbits - value /= 1024; - if (value < 1024) - { - if (value < 100) - { - return string.Format(CultureInfo.InvariantCulture, "{0:0.0} Mbps", value); - } - - return string.Format(CultureInfo.InvariantCulture, "{0:0} Mbps", value); - } - - // Mbits to Gbits - value /= 1024; - if (value < 100) - { - return string.Format(CultureInfo.InvariantCulture, "{0:0.0} Gbps", value); - } - - return string.Format(CultureInfo.InvariantCulture, "{0:0} Gbps", value); - } - - private static string FormatAsBytesPerSecString(float value) - { - // Bytes to KB - value /= 1024; - if (value < 1024) - { - if (value < 100) - { - return string.Format(CultureInfo.InvariantCulture, "{0:0.0} KB/s", value); - } - - return string.Format(CultureInfo.InvariantCulture, "{0:0} KB/s", value); - } - - // KB to MB - value /= 1024; - if (value < 1024) - { - if (value < 100) - { - return string.Format(CultureInfo.InvariantCulture, "{0:0.0} MB/s", value); - } - - return string.Format(CultureInfo.InvariantCulture, "{0:0} MB/s", value); - } - - // MB to GB - value /= 1024; - if (value < 100) - { - return string.Format(CultureInfo.InvariantCulture, "{0:0.0} GB/s", value); - } - - return string.Format(CultureInfo.InvariantCulture, "{0:0} GB/s", value); - } - - private static string FormatAsBinaryBytesPerSecString(float value) - { - // Bytes to KiB - value /= 1024; - if (value < 1024) - { - if (value < 100) - { - return string.Format(CultureInfo.InvariantCulture, "{0:0.0} KiB/s", value); - } - - return string.Format(CultureInfo.InvariantCulture, "{0:0} KiB/s", value); - } - - // KiB to MiB - value /= 1024; - if (value < 1024) - { - if (value < 100) - { - return string.Format(CultureInfo.InvariantCulture, "{0:0.0} MiB/s", value); - } - - return string.Format(CultureInfo.InvariantCulture, "{0:0} MiB/s", value); - } - - // MiB to GiB - value /= 1024; - if (value < 100) - { - return string.Format(CultureInfo.InvariantCulture, "{0:0.0} GiB/s", value); - } - - return string.Format(CultureInfo.InvariantCulture, "{0:0} GiB/s", value); - } - internal override void PushActivate() { base.PushActivate(); @@ -1327,3 +1503,117 @@ internal sealed partial class OpenTaskManagerCommand : InvokableCommand return CommandResult.Hide(); } } + +internal static class FormatIncomingData +{ + public static string AsBitsPerSecString(float value) + { + // Bytes to bits + value *= 8; + + // bits to Kbits + value /= 1024; + if (value < 1024) + { + if (value < 100) + { + return string.Format(CultureInfo.InvariantCulture, "{0:0.0} Kbps", value); + } + + return string.Format(CultureInfo.InvariantCulture, "{0:0} Kbps", value); + } + + // Kbits to Mbits + value /= 1024; + if (value < 1024) + { + if (value < 100) + { + return string.Format(CultureInfo.InvariantCulture, "{0:0.0} Mbps", value); + } + + return string.Format(CultureInfo.InvariantCulture, "{0:0} Mbps", value); + } + + // Mbits to Gbits + value /= 1024; + if (value < 100) + { + return string.Format(CultureInfo.InvariantCulture, "{0:0.0} Gbps", value); + } + + return string.Format(CultureInfo.InvariantCulture, "{0:0} Gbps", value); + } + + public static string AsBytesPerSecString(float value) + { + // Bytes to KB (SI decimal, 1000-based) + value /= 1000; + if (value < 1000) + { + if (value < 100) + { + return string.Format(CultureInfo.InvariantCulture, "{0:0.0} KB/s", value); + } + + return string.Format(CultureInfo.InvariantCulture, "{0:0} KB/s", value); + } + + // KB to MB + value /= 1000; + if (value < 1000) + { + if (value < 100) + { + return string.Format(CultureInfo.InvariantCulture, "{0:0.0} MB/s", value); + } + + return string.Format(CultureInfo.InvariantCulture, "{0:0} MB/s", value); + } + + // MB to GB + value /= 1000; + if (value < 100) + { + return string.Format(CultureInfo.InvariantCulture, "{0:0.0} GB/s", value); + } + + return string.Format(CultureInfo.InvariantCulture, "{0:0} GB/s", value); + } + + public static string AsBinaryBytesPerSecString(float value) + { + // Bytes to KiB + value /= 1024; + if (value < 1024) + { + if (value < 100) + { + return string.Format(CultureInfo.InvariantCulture, "{0:0.0} KiB/s", value); + } + + return string.Format(CultureInfo.InvariantCulture, "{0:0} KiB/s", value); + } + + // KiB to MiB + value /= 1024; + if (value < 1024) + { + if (value < 100) + { + return string.Format(CultureInfo.InvariantCulture, "{0:0.0} MiB/s", value); + } + + return string.Format(CultureInfo.InvariantCulture, "{0:0} MiB/s", value); + } + + // MiB to GiB + value /= 1024; + if (value < 100) + { + return string.Format(CultureInfo.InvariantCulture, "{0:0.0} GiB/s", value); + } + + return string.Format(CultureInfo.InvariantCulture, "{0:0} GiB/s", value); + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Properties/AssemblyInfo.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..c9f8493e56 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Properties/AssemblyInfo.cs @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests")] diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/SettingsManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/SettingsManager.cs index fd975a65e7..8adf7120c4 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/SettingsManager.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/SettingsManager.cs @@ -20,15 +20,30 @@ internal sealed class SettingsManager : JsonSettingsManager Resources.GetResource("Network_Speed_Unit_Setting_Title"), Resources.GetResource("Network_Speed_Unit_Setting_Description"), [ - new ChoiceSetSetting.Choice(Resources.GetResource("Network_Speed_Unit_BitsPerSec"), NetworkSpeedUnit.BitsPerSecond.ToString("G")), - new ChoiceSetSetting.Choice(Resources.GetResource("Network_Speed_Unit_BytesPerSec"), NetworkSpeedUnit.BytesPerSecond.ToString("G")), - new ChoiceSetSetting.Choice(Resources.GetResource("Network_Speed_Unit_BinaryBytesPerSec"), NetworkSpeedUnit.BinaryBytesPerSecond.ToString("G")), + new ChoiceSetSetting.Choice(Resources.GetResource("Network_Speed_Unit_BitsPerSec"), SpeedUnit.BitsPerSecond.ToString("G")), + new ChoiceSetSetting.Choice(Resources.GetResource("Network_Speed_Unit_BytesPerSec"), SpeedUnit.BytesPerSecond.ToString("G")), + new ChoiceSetSetting.Choice(Resources.GetResource("Network_Speed_Unit_BinaryBytesPerSec"), SpeedUnit.BinaryBytesPerSecond.ToString("G")), ]); - public NetworkSpeedUnit NetworkSpeedUnit => - Enum.TryParse(_networkSpeedUnit.Value, out var unit) + public SpeedUnit NetworkSpeedUnit => + Enum.TryParse(_networkSpeedUnit.Value, out var unit) ? unit - : NetworkSpeedUnit.BitsPerSecond; + : SpeedUnit.BitsPerSecond; + + private readonly ChoiceSetSetting _diskSpeedUnit = new( + Namespaced(nameof(DiskSpeedUnit)), + Resources.GetResource("Disk_Speed_Unit_Setting_Title"), + Resources.GetResource("Disk_Speed_Unit_Setting_Description"), + [ + new ChoiceSetSetting.Choice(Resources.GetResource("Disk_Speed_Unit_BitsPerSec"), SpeedUnit.BitsPerSecond.ToString("G")), + new ChoiceSetSetting.Choice(Resources.GetResource("Disk_Speed_Unit_BytesPerSec"), SpeedUnit.BytesPerSecond.ToString("G")), + new ChoiceSetSetting.Choice(Resources.GetResource("Disk_Speed_Unit_BinaryBytesPerSec"), SpeedUnit.BinaryBytesPerSecond.ToString("G")), + ]); + + public SpeedUnit DiskSpeedUnit => + Enum.TryParse(_diskSpeedUnit.Value, out var unit) + ? unit + : SpeedUnit.BytesPerSecond; private static string SettingsJsonPath() { @@ -42,6 +57,7 @@ internal sealed class SettingsManager : JsonSettingsManager FilePath = SettingsJsonPath(); Settings.Add(_networkSpeedUnit); + Settings.Add(_diskSpeedUnit); LoadSettings(); diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/NetworkSpeedUnit.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/SpeedUnit.cs similarity index 86% rename from src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/NetworkSpeedUnit.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/SpeedUnit.cs index 0578bb1d88..2bfaa994d4 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/NetworkSpeedUnit.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/SpeedUnit.cs @@ -5,9 +5,9 @@ namespace Microsoft.CmdPal.Ext.PerformanceMonitor; /// -/// Controls the unit used to display network transmission speed. +/// Controls the unit used to display network or disk transmission speed. /// -internal enum NetworkSpeedUnit +internal enum SpeedUnit { /// Bits per second (Kbps, Mbps, Gbps) — SI decimal prefixes. BitsPerSecond, diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Strings/en-US/Resources.resw b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Strings/en-US/Resources.resw index 5d88b51a9a..0ed930d5e4 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Strings/en-US/Resources.resw +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Strings/en-US/Resources.resw @@ -1,5 +1,64 @@  + @@ -135,6 +194,24 @@ Ethernet + + Active time + + + Read + + + Write + + + Name + + + Previous disk + + + Next disk + Utilization @@ -183,6 +260,9 @@ Network + + Disk + GPU @@ -257,6 +337,19 @@ Network Usage: ??? + + Disk Usage + + + Disk ({0}): {1} + {0} is the disk interface name, {1} is the usage percentage + + + ??? + + + Disk Usage: ??? + GPU Usage @@ -331,6 +424,15 @@ Receive + + Read + + + Write + + + Active time + Network speed unit @@ -346,4 +448,19 @@ Binary bytes per second (KiB/s, MiB/s, GiB/s) - + + Disk speed unit + + + Choose the unit used to display disk transfer speed + + + Bits per second (Kbps, Mbps, Gbps) + + + Bytes per second (KB/s, MB/s, GB/s) + + + Binary bytes per second (KiB/s, MiB/s, GiB/s) + + \ No newline at end of file