Skip to content

Commit adea895

Browse files
committed
Enhance background scheduling using Coravel
1 parent 595541c commit adea895

12 files changed

Lines changed: 330 additions & 54 deletions

File tree

CompactGUI.Core/Settings/Settings.cs

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
11
using CommunityToolkit.Mvvm.ComponentModel;
2-
using CompactGUI.Logging;
32
using Microsoft.Extensions.Logging;
4-
using System;
5-
using System.Collections.Generic;
6-
using System.Linq;
7-
using System.Runtime;
8-
using System.Text;
9-
using System.Threading.Tasks;
10-
using static System.Net.Mime.MediaTypeNames;
113

124
namespace CompactGUI.Core.Settings;
135

@@ -34,7 +26,12 @@ public partial class Settings : ObservableRecipient
3426
[ObservableProperty] private bool estimateCompressionForNonSteamFolders = false;
3527

3628
[ObservableProperty] private bool enableBackgroundWatcher = true;
37-
[ObservableProperty] private bool enableBackgroundAutoCompression = true;
29+
[ObservableProperty] private BackgroundMode backgroundModeSelection = BackgroundMode.IdleOnly;
30+
[ObservableProperty][NotifyPropertyChangedFor(nameof(NextScheduledBackgroundRun))] private int scheduledBackgroundInterval = 1; // in days
31+
[ObservableProperty][NotifyPropertyChangedFor(nameof(NextScheduledBackgroundRun))] private int scheduledBackgroundHour = 0; // 0-23
32+
[ObservableProperty][NotifyPropertyChangedFor(nameof(NextScheduledBackgroundRun))] private int scheduledBackgroundMinute = 0; // 0-59
33+
[ObservableProperty][NotifyPropertyChangedFor(nameof(NextScheduledBackgroundRun))] private DateTime scheduledBackgroundLastRan = DateTime.MinValue;
34+
[ObservableProperty] private DateTime nextScheduledBackgroundRun = DateTime.MaxValue;
3835

3936
[ObservableProperty] private bool allowMultiInstance = false;
4037
[ObservableProperty] private bool enablePreReleaseUpdates = true;
@@ -49,21 +46,33 @@ public partial class Settings : ObservableRecipient
4946
[ObservableProperty] private WindowState windowState = WindowState.Normal;
5047
[ObservableProperty] private bool alwaysShowDetailedCompressionMode = false;
5148

52-
//partial void OnEnableBackgroundWatcherChanged(bool value)
53-
//{
54-
// Watcher.Watcher.IsWatchingEnabled = value;
55-
//}
5649

57-
//partial void OnEnableBackgroundAutoCompressionChanged(bool value)
58-
//{
59-
// Watcher.Watcher.IsBackgroundCompactingEnabled = value;
60-
//}
50+
partial void OnScheduledBackgroundIntervalChanged(int value) => UpdateNextScheduledBackgroundRun();
51+
52+
partial void OnScheduledBackgroundHourChanged(int value) => UpdateNextScheduledBackgroundRun();
53+
54+
partial void OnScheduledBackgroundMinuteChanged(int value) => UpdateNextScheduledBackgroundRun();
55+
56+
partial void OnScheduledBackgroundLastRanChanged(DateTime value) => UpdateNextScheduledBackgroundRun();
57+
58+
private void UpdateNextScheduledBackgroundRun()
59+
{
60+
DateTime nextRun = scheduledBackgroundLastRan.Date
61+
.AddDays(scheduledBackgroundInterval)
62+
.AddHours(scheduledBackgroundHour)
63+
.AddMinutes(scheduledBackgroundMinute);
64+
DateTime now = DateTime.Now;
65+
while (nextRun <= now)
66+
{
67+
nextRun = nextRun.AddDays(scheduledBackgroundInterval == 0 ? 1 : scheduledBackgroundInterval);
68+
}
69+
70+
NextScheduledBackgroundRun = nextRun;
71+
}
72+
73+
6174

6275

63-
//public static void Save()
64-
//{
65-
// SettingsHandler.WriteToFile();
66-
//}
6776
}
6877

6978
public enum WindowState
@@ -72,3 +81,11 @@ public enum WindowState
7281
Minimized,
7382
Maximized
7483
}
84+
85+
public enum BackgroundMode
86+
{
87+
Never,
88+
IdleOnly,
89+
Scheduled,
90+
ScheduledAndIdle
91+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using Microsoft.Extensions.Logging;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
8+
namespace CompactGUI.Logging
9+
{
10+
public static partial class SchedulerServiceLog
11+
{
12+
13+
[LoggerMessage(Level = LogLevel.Debug, Message = "Checking if scheduler should run...")]
14+
public static partial void CheckingSchedulerRunnable(ILogger logger);
15+
16+
[LoggerMessage(Level = LogLevel.Information, Message = "Scheduler not running: background watcher is disabled.")]
17+
public static partial void SchedulerDisabled(ILogger logger);
18+
19+
[LoggerMessage(Level = LogLevel.Information, Message = "Scheduler not running: next scheduled run is in the future. Next run: {NextRun}")]
20+
public static partial void SchedulerNextRunInFuture(ILogger logger, DateTime nextRun);
21+
22+
[LoggerMessage(Level = LogLevel.Information, Message = "Scheduler running: system is idle.")]
23+
public static partial void SchedulerRunningIdle(ILogger logger);
24+
25+
[LoggerMessage(Level = LogLevel.Information, Message = "Scheduler not running: system is not idle.")]
26+
public static partial void SchedulerNotIdle(ILogger logger);
27+
28+
[LoggerMessage(Level = LogLevel.Information, Message = "Scheduler running: scheduled mode, idle not required.")]
29+
public static partial void SchedulerRunningScheduled(ILogger logger);
30+
31+
[LoggerMessage(Level = LogLevel.Information, Message = "Scheduler not running: scheduler is disabled.")]
32+
public static partial void SchedulerModeDisabled(ILogger logger);
33+
34+
[LoggerMessage(Level = LogLevel.Error, Message = "Scheduler error: {ExceptionMessage}")]
35+
public static partial void SchedulerError(ILogger logger, string exceptionMessage, Exception ex);
36+
37+
}
38+
}

CompactGUI.Watcher/IdleDetector.vb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ Public Class IdleDetector
4646
Private Async Function IdleTimerDoWorkAsync() As Task
4747
Try
4848
While Await _idletimer.WaitForNextTickAsync(_cts.Token) AndAlso Not _cts.Token.IsCancellationRequested
49-
Trace.WriteLine("Doing work!!!")
5049

5150
If GetIdleTime() > _settings.IdleThresholdSeconds Then
5251
If State <> IdleState.Idle OrElse DateTime.Now.AddSeconds(-_settings.IdleRepeatTimeSeconds) > LastIdleTime Then
@@ -71,7 +70,7 @@ Public Class IdleDetector
7170
End Function
7271

7372
Public Shared Function GetIdleTime() As Double
74-
Dim lastInputInfo As New LASTINPUTINFO() With {.cbSize = CType(Marshal.SizeOf(GetType(LASTINPUTINFO)), UInteger)}
73+
Dim lastInputInfo As New LASTINPUTINFO() With {.cbSize = CType(Marshal.SizeOf(Of LASTINPUTINFO)(), UInteger)}
7574
If Not GetLastInputInfo(lastInputInfo) Then Return 0
7675

7776
Dim idleTicks = Environment.TickCount64 - CLng(lastInputInfo.dwTime)

CompactGUI.Watcher/Watcher.vb

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,26 +46,24 @@ Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipi
4646
End Property
4747

4848

49-
Sub New(excludedFiletypes As String(), logger As ILogger(Of Watcher), settingsService As ISettingsService)
49+
Sub New(logger As ILogger(Of Watcher), settingsService As ISettingsService, idleDetector As IdleDetector)
5050
_logger = logger
5151
_settingsService = settingsService
5252
_DataFolder = settingsService.DataFolder
5353
WatcherJSONFile = New IO.FileInfo(IO.Path.Combine(_DataFolder.FullName, "watcher.json"))
5454

5555
IdleSettings = New IdleSettings
56-
_idleDetector = New IdleDetector(IdleSettings)
56+
_idleDetector = idleDetector
5757

5858
WatcherLog.WatcherStarted(logger)
5959
IsActive = True
6060

61-
62-
_idleDetector.Start()
6361
AddHandler _idleDetector.IsIdle, _idleHandler
6462
AddHandler _idleDetector.IsNotIdle, AddressOf OnSystemNotIdle
6563
AddHandler WatchedFolders.CollectionChanged, AddressOf WatchedFolders_CollectionChanged
6664

6765

68-
BGCompactor = New BackgroundCompactor(excludedFiletypes, _logger)
66+
BGCompactor = New BackgroundCompactor(Array.Empty(Of String), _logger)
6967

7068

7169
AddHandler BGCompactor.IsCompactingEvent, Sub(sender, isCompacting)
@@ -83,6 +81,11 @@ Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipi
8381
Private Async Sub OnSystemIdle()
8482
_isSystemIdle = True
8583
WatcherLog.SystemIdleDetected(_logger)
84+
85+
'Skip idle analysis if the background mode is not set to IdleOnly
86+
Dim bgMode = _settingsService.AppSettings.BackgroundModeSelection
87+
If bgMode <> BackgroundMode.IdleOnly Then Return
88+
8689
BGCompactor.ResumeCompacting()
8790

8891
Await RunWatcher(False)
@@ -97,11 +100,13 @@ Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipi
97100
Public Async Function RunWatcher(Optional runAll As Boolean = True) As Task(Of Boolean)
98101
RemoveHandler _idleDetector.IsIdle, _idleHandler
99102

103+
Trace.WriteLine("Watcher: RunWatcher called")
100104
For Each watcher In WatchedFolders
101105
watcher.PauseMonitoring()
102106
Next
103107

104108
Try
109+
Dim now = DateTime.Now
105110
If Not IsWatchingEnabled Then Return False
106111
Dim recentThresholdDate As DateTime = DateTime.Now.AddSeconds(-IdleSettings.LastSystemModifiedTimeThresholdSeconds)
107112
If Not runAll AndAlso WatchedFolders.Any(Function(x) x.LastChangedDate > recentThresholdDate) Then Return False
@@ -112,6 +117,7 @@ Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipi
112117
If _parseWatchersSemaphore.CurrentCount <> 0 AndAlso (IsBackgroundCompactingEnabled OrElse runAll) Then
113118
Await BackgroundCompact(runAll)
114119
End If
120+
_settingsService.AppSettings.ScheduledBackgroundLastRan = now
115121
Return True
116122
Finally
117123

@@ -129,6 +135,10 @@ Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipi
129135
_isSystemIdle = False
130136
WatcherLog.SystemNotIdle(_logger)
131137

138+
'Skip idle analysis if the background mode is not set to IdleOnly
139+
Dim bgMode = _settingsService.AppSettings.BackgroundModeSelection
140+
If bgMode <> BackgroundMode.IdleOnly Then Return
141+
132142
BGCompactor.PauseCompacting()
133143
End Sub
134144

@@ -464,7 +474,7 @@ Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipi
464474
If (message.Sender.GetType() IsNot GetType(Settings)) Then Return
465475

466476
If message.PropertyName = NameOf(Settings.EnableBackgroundWatcher) Then : IsWatchingEnabled = message.NewValue
467-
ElseIf message.PropertyName = NameOf(Settings.EnableBackgroundAutoCompression) Then : IsBackgroundCompactingEnabled = message.NewValue
477+
ElseIf message.PropertyName = NameOf(Settings.BackgroundModeSelection) Then : IsBackgroundCompactingEnabled = (CType(message.NewValue, BackgroundMode) = BackgroundMode.IdleOnly)
468478
End If
469479

470480

CompactGUI/Application.xaml.vb

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ Imports Microsoft.Extensions.DependencyInjection
1010
Imports Microsoft.Extensions.Configuration
1111
Imports System.Drawing
1212
Imports CompactGUI.Core.Settings
13+
Imports Coravel
14+
Imports CompactGUI.Watcher
15+
Imports Coravel.Scheduling.Schedule.Interfaces
16+
Imports Coravel.Scheduling.Schedule
1317

1418
Partial Public Class Application
1519

@@ -88,22 +92,24 @@ Partial Public Class Application
8892
services.AddTransient(Of DatabaseViewModel)()
8993

9094
'Other services
91-
services.AddSingleton(Of Watcher.Watcher)(Function(s)
92-
Return New Watcher.Watcher(Array.Empty(Of String)(), s.GetRequiredService(Of ILogger(Of Watcher.Watcher)), SettingsService)
93-
End Function)
9495
services.AddSingleton(Of TrayNotifierService)(Function(sp)
9596
Return New TrayNotifierService(sp.GetRequiredService(Of MainWindow)(), Icon.ExtractAssociatedIcon(Environment.ProcessPath), "CompactGUI")
9697
End Function)
9798

9899
services.AddSingleton(Of CompressableFolderService)
99100

100-
End Sub) _
101-
.Build()
102-
103-
104-
101+
services.AddSingleton(Of IdleDetector)(Function()
102+
Dim idleDetector = New IdleDetector(New IdleSettings)
103+
idleDetector.Start()
104+
Return idleDetector
105+
End Function)
106+
services.AddSingleton(Of SchedulerService)()
107+
services.AddSingleton(Of Watcher.Watcher)()
105108

109+
services.AddScheduler()
106110

111+
End Sub) _
112+
.Build()
107113

108114

109115
End Sub
@@ -141,6 +147,10 @@ Partial Public Class Application
141147
Await _host.StartAsync()
142148
Await GetService(Of SettingsViewModel).InitializeEnvironment()
143149

150+
151+
GetService(Of SchedulerService).RegenerateSchedule()
152+
153+
144154
Dim UpdateTask = GetService(Of IUpdaterService).CheckForUpdate(SettingsService.AppSettings.EnablePreReleaseUpdates)
145155
Dim WikiTask = GetService(Of IWikiService).GetUpdatedJSONAsync()
146156
Await Task.WhenAll(UpdateTask, WikiTask)

CompactGUI/CompactGUI.vbproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444

4545
<ItemGroup>
4646
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
47+
<PackageReference Include="Coravel" Version="6.0.2" />
4748
<PackageReference Include="diskdetector-net" Version="0.3.2" />
4849
<PackageReference Include="Gameloop.Vdf" Version="0.6.2" />
4950
<PackageReference Include="IridiumIO.MVVM.VBSourceGenerators" Version="0.6.1" PrivateAssets="All" />
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Imports Microsoft.Xaml.Behaviors
2+
3+
Public Class FocusOnMouseOverBehavior : Inherits Behavior(Of ComboBox)
4+
5+
Protected Overrides Sub OnAttached()
6+
MyBase.OnAttached()
7+
AddHandler AssociatedObject.MouseEnter, AddressOf OnMouseEnter
8+
End Sub
9+
Protected Overrides Sub OnDetaching()
10+
MyBase.OnDetaching()
11+
RemoveHandler AssociatedObject.MouseEnter, AddressOf OnMouseEnter
12+
End Sub
13+
Private Sub OnMouseEnter(sender As Object, e As MouseEventArgs)
14+
AssociatedObject.Focus()
15+
End Sub
16+
17+
18+
End Class

CompactGUI/Components/Converters/Converters.xaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
<local:FolderActionStateWorkingToVisibilityConverter x:Key="FolderActionStateWorkingToVisibilityConverter" />
3030
<local:ZeroCountToVisibilityConverter x:Key="ZeroCountToVisibilityConverter"/>
3131
<local:NumberWithSpacesConverter x:Key="NumberWithSpacesConverter"/>
32+
<local:BackgroundModeToVisibilityConverter x:Key="BackgroundModeToVisibilityConverter"/>
33+
<local:EnumToIntConverter x:Key="EnumToIntConverter" />
3234

3335
<converters:ValueConverterGroup x:Key="IsSteamFolderToVisibilityConverter">
3436
<local:IsSteamFolderConverter/>

CompactGUI/Components/Converters/IValueConverters.vb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,4 +454,33 @@ Public Class NumberWithSpacesConverter
454454
End If
455455
Return 0
456456
End Function
457+
End Class
458+
459+
460+
Public Class BackgroundModeToVisibilityConverter
461+
Implements IValueConverter
462+
463+
Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
464+
Dim index As Integer = If(value, 0)
465+
Return If(index > 1, Visibility.Visible, Visibility.Collapsed)
466+
467+
End Function
468+
469+
Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
470+
Throw New NotImplementedException
471+
End Function
472+
End Class
473+
474+
Public Class EnumToIntConverter
475+
Implements IValueConverter
476+
477+
Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
478+
If value Is Nothing OrElse Not value.GetType().IsEnum Then Return 0
479+
Return CType(value, [Enum]).GetHashCode()
480+
End Function
481+
482+
Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
483+
If targetType Is Nothing OrElse Not targetType.IsEnum OrElse value Is Nothing Then Return Binding.DoNothing
484+
Return [Enum].ToObject(targetType, value)
485+
End Function
457486
End Class

0 commit comments

Comments
 (0)