1111using System . ComponentModel ;
1212using System . Linq ;
1313using System . Windows ;
14+ using System . Windows . Controls ;
1415using PerformanceMonitorDashboard . Models ;
1516using PerformanceMonitorDashboard . Services ;
1617
@@ -20,6 +21,107 @@ public partial class CollectorScheduleWindow : Window
2021 {
2122 private readonly DatabaseService _databaseService ;
2223 private List < CollectorScheduleItem > ? _schedules ;
24+ private bool _suppressPresetChange ;
25+
26+ private static readonly Dictionary < string , Dictionary < string , int > > Presets = new ( StringComparer . OrdinalIgnoreCase )
27+ {
28+ [ "Aggressive" ] = new ( StringComparer . OrdinalIgnoreCase )
29+ {
30+ [ "wait_stats_collector" ] = 1 ,
31+ [ "query_stats_collector" ] = 1 ,
32+ [ "memory_stats_collector" ] = 1 ,
33+ [ "memory_pressure_events_collector" ] = 1 ,
34+ [ "system_health_collector" ] = 2 ,
35+ [ "blocked_process_xml_collector" ] = 1 ,
36+ [ "deadlock_xml_collector" ] = 1 ,
37+ [ "process_blocked_process_xml" ] = 2 ,
38+ [ "blocking_deadlock_analyzer" ] = 2 ,
39+ [ "process_deadlock_xml" ] = 2 ,
40+ [ "query_store_collector" ] = 2 ,
41+ [ "procedure_stats_collector" ] = 1 ,
42+ [ "query_snapshots_collector" ] = 1 ,
43+ [ "file_io_stats_collector" ] = 1 ,
44+ [ "memory_grant_stats_collector" ] = 1 ,
45+ [ "cpu_scheduler_stats_collector" ] = 1 ,
46+ [ "memory_clerks_stats_collector" ] = 2 ,
47+ [ "perfmon_stats_collector" ] = 1 ,
48+ [ "cpu_utilization_stats_collector" ] = 1 ,
49+ [ "trace_analysis_collector" ] = 1 ,
50+ [ "default_trace_collector" ] = 2 ,
51+ [ "configuration_issues_analyzer" ] = 1 ,
52+ [ "latch_stats_collector" ] = 1 ,
53+ [ "spinlock_stats_collector" ] = 1 ,
54+ [ "tempdb_stats_collector" ] = 1 ,
55+ [ "plan_cache_stats_collector" ] = 2 ,
56+ [ "session_stats_collector" ] = 1 ,
57+ [ "waiting_tasks_collector" ] = 1 ,
58+ [ "running_jobs_collector" ] = 2
59+ } ,
60+ [ "Balanced" ] = new ( StringComparer . OrdinalIgnoreCase )
61+ {
62+ [ "wait_stats_collector" ] = 1 ,
63+ [ "query_stats_collector" ] = 2 ,
64+ [ "memory_stats_collector" ] = 1 ,
65+ [ "memory_pressure_events_collector" ] = 1 ,
66+ [ "system_health_collector" ] = 5 ,
67+ [ "blocked_process_xml_collector" ] = 1 ,
68+ [ "deadlock_xml_collector" ] = 1 ,
69+ [ "process_blocked_process_xml" ] = 5 ,
70+ [ "blocking_deadlock_analyzer" ] = 5 ,
71+ [ "process_deadlock_xml" ] = 5 ,
72+ [ "query_store_collector" ] = 2 ,
73+ [ "procedure_stats_collector" ] = 2 ,
74+ [ "query_snapshots_collector" ] = 1 ,
75+ [ "file_io_stats_collector" ] = 1 ,
76+ [ "memory_grant_stats_collector" ] = 1 ,
77+ [ "cpu_scheduler_stats_collector" ] = 1 ,
78+ [ "memory_clerks_stats_collector" ] = 5 ,
79+ [ "perfmon_stats_collector" ] = 5 ,
80+ [ "cpu_utilization_stats_collector" ] = 1 ,
81+ [ "trace_analysis_collector" ] = 2 ,
82+ [ "default_trace_collector" ] = 5 ,
83+ [ "configuration_issues_analyzer" ] = 1 ,
84+ [ "latch_stats_collector" ] = 1 ,
85+ [ "spinlock_stats_collector" ] = 1 ,
86+ [ "tempdb_stats_collector" ] = 1 ,
87+ [ "plan_cache_stats_collector" ] = 5 ,
88+ [ "session_stats_collector" ] = 1 ,
89+ [ "waiting_tasks_collector" ] = 1 ,
90+ [ "running_jobs_collector" ] = 1
91+ } ,
92+ [ "Low-Impact" ] = new ( StringComparer . OrdinalIgnoreCase )
93+ {
94+ [ "wait_stats_collector" ] = 5 ,
95+ [ "query_stats_collector" ] = 10 ,
96+ [ "memory_stats_collector" ] = 10 ,
97+ [ "memory_pressure_events_collector" ] = 5 ,
98+ [ "system_health_collector" ] = 15 ,
99+ [ "blocked_process_xml_collector" ] = 5 ,
100+ [ "deadlock_xml_collector" ] = 5 ,
101+ [ "process_blocked_process_xml" ] = 10 ,
102+ [ "blocking_deadlock_analyzer" ] = 10 ,
103+ [ "process_deadlock_xml" ] = 10 ,
104+ [ "query_store_collector" ] = 30 ,
105+ [ "procedure_stats_collector" ] = 10 ,
106+ [ "query_snapshots_collector" ] = 5 ,
107+ [ "file_io_stats_collector" ] = 10 ,
108+ [ "memory_grant_stats_collector" ] = 5 ,
109+ [ "cpu_scheduler_stats_collector" ] = 5 ,
110+ [ "memory_clerks_stats_collector" ] = 30 ,
111+ [ "perfmon_stats_collector" ] = 5 ,
112+ [ "cpu_utilization_stats_collector" ] = 5 ,
113+ [ "trace_analysis_collector" ] = 10 ,
114+ [ "default_trace_collector" ] = 15 ,
115+ [ "configuration_issues_analyzer" ] = 5 ,
116+ [ "latch_stats_collector" ] = 5 ,
117+ [ "spinlock_stats_collector" ] = 5 ,
118+ [ "tempdb_stats_collector" ] = 5 ,
119+ [ "plan_cache_stats_collector" ] = 15 ,
120+ [ "session_stats_collector" ] = 5 ,
121+ [ "waiting_tasks_collector" ] = 5 ,
122+ [ "running_jobs_collector" ] = 30
123+ }
124+ } ;
23125
24126 public CollectorScheduleWindow ( DatabaseService databaseService )
25127 {
@@ -59,6 +161,7 @@ private async System.Threading.Tasks.Task LoadSchedulesAsync()
59161 }
60162
61163 ScheduleDataGrid . ItemsSource = _schedules ;
164+ DetectActivePreset ( ) ;
62165 }
63166 catch ( Exception ex )
64167 {
@@ -71,6 +174,101 @@ private async System.Threading.Tasks.Task LoadSchedulesAsync()
71174 }
72175 }
73176
177+ private void DetectActivePreset ( )
178+ {
179+ if ( _schedules == null ) return ;
180+
181+ _suppressPresetChange = true ;
182+ try
183+ {
184+ var currentIntervals = _schedules
185+ . Where ( s => s . FrequencyMinutes < 1440 )
186+ . ToDictionary ( s => s . CollectorName , s => s . FrequencyMinutes , StringComparer . OrdinalIgnoreCase ) ;
187+
188+ foreach ( var ( presetName , presetIntervals ) in Presets )
189+ {
190+ bool matches = true ;
191+ foreach ( var ( collector , freq ) in presetIntervals )
192+ {
193+ if ( currentIntervals . TryGetValue ( collector , out int current ) && current != freq )
194+ {
195+ matches = false ;
196+ break ;
197+ }
198+ }
199+
200+ if ( matches )
201+ {
202+ for ( int i = 0 ; i < PresetComboBox . Items . Count ; i ++ )
203+ {
204+ if ( PresetComboBox . Items [ i ] is ComboBoxItem item &&
205+ string . Equals ( item . Content ? . ToString ( ) , presetName , StringComparison . OrdinalIgnoreCase ) )
206+ {
207+ PresetComboBox . SelectedIndex = i ;
208+ return ;
209+ }
210+ }
211+ }
212+ }
213+
214+ /* No preset matched */
215+ PresetComboBox . SelectedIndex = 0 ;
216+ }
217+ finally
218+ {
219+ _suppressPresetChange = false ;
220+ }
221+ }
222+
223+ private async void PresetComboBox_SelectionChanged ( object sender , SelectionChangedEventArgs e )
224+ {
225+ if ( _suppressPresetChange ) return ;
226+ if ( PresetComboBox . SelectedItem is not ComboBoxItem selected ) return ;
227+
228+ string presetName = selected . Content ? . ToString ( ) ?? "" ;
229+ if ( presetName == "Custom" ) return ;
230+
231+ var result = MessageBox . Show (
232+ $ "Apply the \" { presetName } \" preset?\n \n This will change all collector frequencies. Enabled/disabled state and retention settings are not affected.",
233+ "Apply Collection Preset" ,
234+ MessageBoxButton . YesNo ,
235+ MessageBoxImage . Question
236+ ) ;
237+
238+ if ( result != MessageBoxResult . Yes )
239+ {
240+ DetectActivePreset ( ) ;
241+ return ;
242+ }
243+
244+ try
245+ {
246+ await _databaseService . ApplyCollectionPresetAsync ( presetName ) ;
247+
248+ /* Unsubscribe, reload, resubscribe */
249+ if ( _schedules != null )
250+ {
251+ foreach ( var schedule in _schedules )
252+ {
253+ schedule . PropertyChanged -= Schedule_PropertyChanged ;
254+ }
255+ }
256+
257+ await LoadSchedulesAsync ( ) ;
258+ }
259+ catch ( Exception ex )
260+ {
261+ MessageBox . Show (
262+ $ "Failed to apply preset:\n \n { ex . Message } ",
263+ "Error Applying Preset" ,
264+ MessageBoxButton . OK ,
265+ MessageBoxImage . Error
266+ ) ;
267+
268+ DetectActivePreset ( ) ;
269+ }
270+ }
271+
74272 private async void Schedule_PropertyChanged ( object ? sender , PropertyChangedEventArgs e )
75273 {
76274 if ( sender is CollectorScheduleItem schedule )
@@ -88,6 +286,11 @@ await _databaseService.UpdateCollectorScheduleAsync(
88286 schedule . FrequencyMinutes ,
89287 schedule . RetentionDays
90288 ) ;
289+
290+ if ( e . PropertyName == nameof ( CollectorScheduleItem . FrequencyMinutes ) )
291+ {
292+ DetectActivePreset ( ) ;
293+ }
91294 }
92295 catch ( Exception ex )
93296 {
0 commit comments