diff --git a/CRSim.Core/Abstractions/ITimeService.cs b/CRSim.Core/Abstractions/ITimeService.cs index 3889ef7..293e8b6 100644 --- a/CRSim.Core/Abstractions/ITimeService.cs +++ b/CRSim.Core/Abstractions/ITimeService.cs @@ -1,16 +1,16 @@ -using System; -using System.Timers; - -namespace CRSim.Core.Abstractions +namespace CRSim.Core.Abstractions { public interface ITimeService { - event EventHandler OneSecondElapsed; // 1秒触发的事件 - event EventHandler RefreshSecondsElapsed; // 20秒触发的事件 + event EventHandler OneSecondElapsed; + event EventHandler RefreshSecondsElapsed; void Start(); void Stop(); DateTime GetDateTimeNow(); + + DateTime SimulateTime { get; set; } // 初始模拟时间 + double Speed { get; set; } // 模拟倍速 } } \ No newline at end of file diff --git a/CRSim.Core/Models/Settings.cs b/CRSim.Core/Models/Settings.cs index 0af445a..4b4b8ef 100644 --- a/CRSim.Core/Models/Settings.cs +++ b/CRSim.Core/Models/Settings.cs @@ -5,8 +5,6 @@ namespace CRSim.Core.Models { public class Settings { - public TimeSpan TimeOffset { get; set; } = TimeSpan.Zero; - public TimeSpan DepartureCheckInAdvanceDuration { get; set; } = TimeSpan.FromMinutes(20); public TimeSpan PassingCheckInAdvanceDuration { get; set; } = TimeSpan.FromMinutes(10); diff --git a/CRSim.Core/Services/SettingsService.cs b/CRSim.Core/Services/SettingsService.cs index 29b00d0..7bc6022 100644 --- a/CRSim.Core/Services/SettingsService.cs +++ b/CRSim.Core/Services/SettingsService.cs @@ -15,7 +15,6 @@ namespace CRSim.Core.Services } public void SaveSettings() { - _key.SetValue("TimeOffset", (int)_settings.TimeOffset.TotalMinutes); _key.SetValue("SwitchPageSeconds", _settings.SwitchPageSeconds); _key.SetValue("ApiUri", _settings.ApiUri); _key.SetValue("MaxPages", _settings.MaxPages); @@ -40,7 +39,6 @@ namespace CRSim.Core.Services else { _settings = new Settings(); - if (_key.GetValue("TimeOffset") != null) _settings.TimeOffset = TimeSpan.FromMinutes((int)_key.GetValue("TimeOffset")); if (_key.GetValue("SwitchPageSeconds") != null) _settings.SwitchPageSeconds = (int)_key.GetValue("SwitchPageSeconds"); if (_key.GetValue("ApiUri") != null) _settings.ApiUri = (string)_key.GetValue("ApiUri"); if (_key.GetValue("MaxPages") != null) _settings.MaxPages = (int)_key.GetValue("MaxPages"); diff --git a/CRSim.Core/Services/TimeService.cs b/CRSim.Core/Services/TimeService.cs index e802530..a36baab 100644 --- a/CRSim.Core/Services/TimeService.cs +++ b/CRSim.Core/Services/TimeService.cs @@ -1,5 +1,4 @@ - -using CRSim.Core.Abstractions; +using CRSim.Core.Abstractions; using CRSim.Core.Models; using System.Timers; @@ -7,49 +6,82 @@ namespace CRSim.Core.Services { public class TimeService : ITimeService { - private readonly Settings _settings; - private readonly System.Timers.Timer _oneSecondTimer; - private readonly System.Timers.Timer _twentySecondsTimer; + public DateTime SimulateTime { get; set; } - public event EventHandler OneSecondElapsed; - public event EventHandler RefreshSecondsElapsed; + private double _speed = 1.0; + public double Speed + { + get => _speed; + set + { + UpdateSimulatedOffset(); // 切换前先结算旧速率下的时间 + _speed = value; + _lastSpeedChangeTime = DateTime.Now; + } + } + + private DateTime _lastSpeedChangeTime; // 上一次倍速切换时的真实时间 + private TimeSpan _simulatedOffset = TimeSpan.Zero; // 累计模拟时间偏移(倍速放大之后的) + + private readonly System.Timers.Timer _oneSecondTimer; + private readonly System.Timers.Timer _twentySecondsTimer; + + public event EventHandler OneSecondElapsed; + public event EventHandler RefreshSecondsElapsed; + + private Settings _settings; public TimeService(ISettingsService settingsService) { _settings = settingsService.GetSettings(); - _oneSecondTimer = new(1000); + _oneSecondTimer = new(1000); _oneSecondTimer.Elapsed += OnOneSecondElapsed; - _twentySecondsTimer = new(_settings.SwitchPageSeconds*1000); + _twentySecondsTimer = new(_settings.SwitchPageSeconds * 1000); _twentySecondsTimer.Elapsed += OnTwentySecondsElapsed; + + _lastSpeedChangeTime = DateTime.Now; } public void Start() { - _oneSecondTimer.Start(); - _twentySecondsTimer.Start(); + _oneSecondTimer.Start(); + _twentySecondsTimer.Start(); + _lastSpeedChangeTime = DateTime.Now; // 启动时刷新时间 } public void Stop() { - _oneSecondTimer.Stop(); - _twentySecondsTimer.Stop(); + _oneSecondTimer.Stop(); + _twentySecondsTimer.Stop(); } private void OnOneSecondElapsed(object sender, ElapsedEventArgs e) { - OneSecondElapsed?.Invoke(this, EventArgs.Empty); + OneSecondElapsed?.Invoke(this, EventArgs.Empty); } private void OnTwentySecondsElapsed(object sender, ElapsedEventArgs e) { - RefreshSecondsElapsed?.Invoke(this, EventArgs.Empty); + RefreshSecondsElapsed?.Invoke(this, EventArgs.Empty); } public DateTime GetDateTimeNow() { - return DateTime.Now+ _settings.TimeOffset; + var now = DateTime.Now; + var elapsed = now - _lastSpeedChangeTime; + return SimulateTime + _simulatedOffset + TimeSpan.FromTicks((long)(elapsed.Ticks * Speed)); + } + + /// + /// 当倍速改变时,先结算之前段的累计偏移 + /// + private void UpdateSimulatedOffset() + { + var now = DateTime.Now; + var elapsed = now - _lastSpeedChangeTime; + _simulatedOffset += TimeSpan.FromTicks((long)(elapsed.Ticks * Speed)); } } } \ No newline at end of file diff --git a/CRSim.ScreenSimulator/StyleManager.cs b/CRSim.ScreenSimulator/StyleManager.cs index 710aea7..867bcff 100644 --- a/CRSim.ScreenSimulator/StyleManager.cs +++ b/CRSim.ScreenSimulator/StyleManager.cs @@ -12,17 +12,22 @@ namespace CRSim.ScreenSimulator public static List StyleInfos => [.. IPluginService.LoadedPlugins.Where(x => x.Manifest.Type == "ScreenStyle" && x.LoadStatus == PluginLoadStatus.Loaded)]; public static IServiceProvider ServiceProvider; private static IDatabaseService _databaseService; - public StyleManager(IEnumerable pluginBases,IServiceProvider serviceProvider,IDatabaseService databaseService) + public StyleManager(IServiceProvider serviceProvider,IDatabaseService databaseService) { ServiceProvider = serviceProvider; _databaseService = databaseService; } - public static void ShowWindow(Type page, Station station, string ticketCheck, string platform,string text,int location,string video) + public static void ShowWindow(Type page, Station station, string ticketCheck, string platform,string? text,int location,string? video,DateTime dateTime) { Thread thread = new(() => { Page viewInstance = (Page)ServiceProvider.GetService(page); var viewModel = ((dynamic)viewInstance).ViewModel; + + var timeService = ((ITimeService)viewModel.TimeService); + timeService.SimulateTime = dateTime; + timeService.Start(); + viewModel.UIDispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher; if (text != null) { diff --git a/CRSim.ScreenSimulator/ViewModels/MetroPlatformScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/MetroPlatformScreenViewModel.cs index 52556db..1f4d7ef 100644 --- a/CRSim.ScreenSimulator/ViewModels/MetroPlatformScreenViewModel.cs +++ b/CRSim.ScreenSimulator/ViewModels/MetroPlatformScreenViewModel.cs @@ -7,7 +7,7 @@ namespace CRSim.ScreenSimulator.ViewModels { public partial class MetroPlatformScreenViewModel : ObservableObject { - public readonly ITimeService _timeService; + public readonly ITimeService TimeService; public readonly Settings _settings; [ObservableProperty] private DateTime _currentTime = new(); @@ -23,14 +23,14 @@ namespace CRSim.ScreenSimulator.ViewModels public List TrainInfos { get; set; } = []; public MetroPlatformScreenViewModel(ITimeService timeService, ISettingsService settingsService) { - _timeService = timeService; + TimeService = timeService; _settings = settingsService.GetSettings(); timeService.OneSecondElapsed += RefreshDisplay; } private void RefreshDisplay(object? sender, EventArgs e) { - CurrentTime = _timeService.GetDateTimeNow(); - List itemsToRemove = [.. TrainInfos.Where(info => info.DepartureTime < _timeService.GetDateTimeNow())]; + CurrentTime = TimeService.GetDateTimeNow(); + List itemsToRemove = [.. TrainInfos.Where(info => info.DepartureTime < TimeService.GetDateTimeNow())]; foreach (var item in itemsToRemove) { TrainInfos.Remove(item); @@ -48,7 +48,7 @@ namespace CRSim.ScreenSimulator.ViewModels { if (trainNumber != null && trainNumber.DepartureTime != null && trainNumber.Platform == platform) { - var now = _timeService.GetDateTimeNow(); + var now = TimeService.GetDateTimeNow(); var today = now.Date; if (_settings.LoadTodayOnly && today.Add((trainNumber.DepartureTime ?? trainNumber.ArrivalTime)!.Value) < now) { diff --git a/CRSim.ScreenSimulator/ViewModels/ScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/ScreenViewModel.cs index b289172..d6a3d38 100644 --- a/CRSim.ScreenSimulator/ViewModels/ScreenViewModel.cs +++ b/CRSim.ScreenSimulator/ViewModels/ScreenViewModel.cs @@ -9,7 +9,7 @@ namespace CRSim.ScreenSimulator.ViewModels { public partial class BaseScreenViewModel : ObservableObject { - public readonly ITimeService _timeService; + public readonly ITimeService TimeService; public readonly Settings _settings; public readonly TaskCompletionSource DataLoaded = new(); [ObservableProperty] @@ -35,13 +35,13 @@ namespace CRSim.ScreenSimulator.ViewModels protected BaseScreenViewModel(ITimeService timeService, ISettingsService settingsService) { - _timeService = timeService; + TimeService = timeService; _settings = settingsService.GetSettings(); - _timeService.OneSecondElapsed += OnTimeElapsed; + TimeService.OneSecondElapsed += OnTimeElapsed; - _timeService.RefreshSecondsElapsed += RefreshData; - _timeService.RefreshSecondsElapsed += RefreshDisplay; + TimeService.RefreshSecondsElapsed += RefreshData; + TimeService.RefreshSecondsElapsed += RefreshDisplay; Initialize(); } [ObservableProperty] @@ -74,14 +74,14 @@ namespace CRSim.ScreenSimulator.ViewModels { if (trainInfo.DepartureTime == null) { - if (trainInfo.ArrivalTime.Value.Add(_settings.StopDisplayFromArrivalDuration) < _timeService.GetDateTimeNow()) + if (trainInfo.ArrivalTime.Value.Add(_settings.StopDisplayFromArrivalDuration) < TimeService.GetDateTimeNow()) { itemsToRemove.Add(trainInfo); } } else { - if ((StationType == StationType.Arrival || StationType == StationType.Both ? trainInfo.DepartureTime.Value : trainInfo.DepartureTime.Value.Subtract(_settings.StopDisplayUntilDepartureDuration)) < _timeService.GetDateTimeNow()) + if ((StationType == StationType.Arrival || StationType == StationType.Both ? trainInfo.DepartureTime.Value : trainInfo.DepartureTime.Value.Subtract(_settings.StopDisplayUntilDepartureDuration)) < TimeService.GetDateTimeNow()) { itemsToRemove.Add(trainInfo); } @@ -96,7 +96,7 @@ namespace CRSim.ScreenSimulator.ViewModels private void OnTimeElapsed(object? sender, EventArgs e) { - CurrentTime = _timeService.GetDateTimeNow(); + CurrentTime = TimeService.GetDateTimeNow(); } public void LoadData(Station station, string ticketCheck, string platform) @@ -115,7 +115,7 @@ namespace CRSim.ScreenSimulator.ViewModels (ticketCheck == string.Empty || (trainNumber.WaitingArea==ticketCheck.Split(" - ")[0] && trainNumber.TicketChecks.Contains(ticketCheck.Split(" - ")[1]))) &&//重复检票口名称的临时解决方案 (platform == string.Empty || trainNumber.Platform == platform)) { - var now = _timeService.GetDateTimeNow(); + var now = TimeService.GetDateTimeNow(); var today = now.Date; if (_settings.LoadTodayOnly && today.Add((trainNumber.DepartureTime??trainNumber.ArrivalTime)!.Value) < now) diff --git a/CRSim/App.xaml.cs b/CRSim/App.xaml.cs index 7a3c95c..64403ac 100644 --- a/CRSim/App.xaml.cs +++ b/CRSim/App.xaml.cs @@ -24,12 +24,12 @@ services.AddSingleton(parsedOptions); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); @@ -43,10 +43,9 @@ protected override void OnLaunched(LaunchActivatedEventArgs args) { - AppHost.Services.GetRequiredService().Start(); MainWindow = AppHost.Services.GetService(); MainWindow?.Activate(); - var win = AppHost.Services.GetService(); + AppHost.Services.GetService();//init } [System.Runtime.InteropServices.DllImport("kernel32.dll")] diff --git a/CRSim/ViewModels/ScreenSimulatorPageViewModel.cs b/CRSim/ViewModels/ScreenSimulatorPageViewModel.cs index 6f4fc97..84e176b 100644 --- a/CRSim/ViewModels/ScreenSimulatorPageViewModel.cs +++ b/CRSim/ViewModels/ScreenSimulatorPageViewModel.cs @@ -1,5 +1,4 @@ using CRSim.Core.Models.Plugin; -using System.Diagnostics.CodeAnalysis; namespace CRSim.ViewModels; @@ -44,10 +43,15 @@ public partial class ScreenSimulatorPageViewModel(IServiceProvider serviceProvid public string SelectedPlatformName = ""; public int SelectedLoaction = 0; + [ObservableProperty] public partial bool IsStartSimulationAvailable { get; private set; } = false; private PluginBase SelectedStylePlugin => serviceProvider.GetServices().Where(x => x.Info == SelectedStyle).FirstOrDefault(); + public DateTimeOffset SelectedDate = DateTime.Today; + + public TimeSpan SelectedTime = DateTime.Now - DateTime.Today; + [RelayCommand] public void StyleSelected(object selectedStyle) { @@ -139,7 +143,8 @@ public partial class ScreenSimulatorPageViewModel(IServiceProvider serviceProvid PlatformNeeded ? SelectedPlatformName : string.Empty, (TextNeeded && Text != string.Empty) ? Text : null, (LocationNeeded && SelectedLoaction != 0) ? SelectedLoaction : 0, - (VideoNeeded && Video != string.Empty) ? Video : null); + (VideoNeeded && Video != string.Empty) ? Video : null, + SelectedDate.Add(SelectedTime).DateTime); } private void CheckCanStart() diff --git a/CRSim/ViewModels/SettingsPageViewModel.cs b/CRSim/ViewModels/SettingsPageViewModel.cs index 3618d87..f38cc23 100644 --- a/CRSim/ViewModels/SettingsPageViewModel.cs +++ b/CRSim/ViewModels/SettingsPageViewModel.cs @@ -20,9 +20,6 @@ namespace CRSim.ViewModels private readonly IDatabaseService _databaseService; private readonly IDialogService _dialogService; #region 偏好设置 - [ObservableProperty] - public partial string TimeOffset { get; set; } - [ObservableProperty] public partial string DepartureCheckInAdvanceDuration { get; set; } @@ -69,7 +66,6 @@ namespace CRSim.ViewModels private void LoadSettings() { _settings = _settingsService.GetSettings(); - TimeOffset = _settings.TimeOffset.TotalMinutes.ToString(); DepartureCheckInAdvanceDuration = _settings.DepartureCheckInAdvanceDuration.TotalMinutes.ToString(); PassingCheckInAdvanceDuration = _settings.PassingCheckInAdvanceDuration.TotalMinutes.ToString(); StopDisplayUntilDepartureDuration = _settings.StopDisplayUntilDepartureDuration.TotalMinutes.ToString(); @@ -86,7 +82,6 @@ namespace CRSim.ViewModels [RelayCommand] public void Unload() { - UpdateSettings(TimeOffset, true, value => _settings.TimeOffset = TimeSpan.FromMinutes(value)); UpdateSettings(DepartureCheckInAdvanceDuration, false, value => _settings.DepartureCheckInAdvanceDuration = TimeSpan.FromMinutes(value)); UpdateSettings(PassingCheckInAdvanceDuration, false, value => _settings.PassingCheckInAdvanceDuration = TimeSpan.FromMinutes(value)); UpdateSettings(StopDisplayUntilDepartureDuration, false, value => _settings.StopDisplayUntilDepartureDuration = TimeSpan.FromMinutes(value)); diff --git a/CRSim/Views/ScreenSimulatorPage.xaml b/CRSim/Views/ScreenSimulatorPage.xaml index 834faef..ba37535 100644 --- a/CRSim/Views/ScreenSimulatorPage.xaml +++ b/CRSim/Views/ScreenSimulatorPage.xaml @@ -75,7 +75,7 @@ - + @@ -102,6 +102,12 @@ + + + + + + diff --git a/CRSim/Views/SettingsPage.xaml b/CRSim/Views/SettingsPage.xaml index c668619..f314ff5 100644 --- a/CRSim/Views/SettingsPage.xaml +++ b/CRSim/Views/SettingsPage.xaml @@ -48,9 +48,6 @@ - - -