feat: 车站数据分享
@@ -9,9 +9,7 @@ namespace CRSim.Core.Abstractions
|
||||
List<TrainNumber> GetAllTrainNumbers();
|
||||
Station GetStationByName(string name);
|
||||
TrainNumber GetTrainNumberByNumber(string number);
|
||||
void AddStationByName(string name);
|
||||
void AddStation(Station station);
|
||||
Task ImportStationFrom7D(string path);
|
||||
void AddTrainNumber(TrainNumber trainNumber);
|
||||
void DeleteStation(Station station);
|
||||
void DeleteTrainNumber(TrainNumber trainNumber);
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using CRSim.Core.Models;
|
||||
using CRSim.Core.Utils;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace CRSim.Core.Services
|
||||
{
|
||||
@@ -39,13 +38,6 @@ namespace CRSim.Core.Services
|
||||
{
|
||||
return _trainNumbers.FirstOrDefault(x => x.Number == number);
|
||||
}
|
||||
public void AddStationByName(string name)
|
||||
{
|
||||
_stations.Add(new Station()
|
||||
{
|
||||
Name = name,
|
||||
});
|
||||
}
|
||||
|
||||
public void DeleteStation(Station station)
|
||||
{
|
||||
@@ -110,81 +102,6 @@ namespace CRSim.Core.Services
|
||||
_trainNumbers.Add(trainNumber);
|
||||
}
|
||||
|
||||
public async Task ImportStationFrom7D(string path)
|
||||
{
|
||||
Station station = new() { Platforms = [], WaitingAreas = [], TrainStops = [] };
|
||||
var stationName = (await File.ReadAllTextAsync(path.Replace("车站广播系统.exe", "info.ini"))).Split("|")[0];
|
||||
if (_stations.Any(x => x.Name == stationName))
|
||||
{
|
||||
_stations.Remove(GetStationByName(stationName));
|
||||
}
|
||||
var lines = await File.ReadAllLinesAsync(path.Replace("车站广播系统.exe", "data.csv"));
|
||||
station.Name = stationName;
|
||||
foreach (var line in lines)
|
||||
{
|
||||
var data = line.Split(',');
|
||||
var waitingAreaName = string.IsNullOrWhiteSpace(data.Last()) ? "未知" : data.Last();
|
||||
var platform = string.IsNullOrWhiteSpace(data[5]) ? "未知" : data[5];
|
||||
var ticketCheck = string.IsNullOrWhiteSpace(data[4]) ? string.Empty : data[4];
|
||||
|
||||
var existingPlatform = station.Platforms.FirstOrDefault(x => x.Name == platform);
|
||||
if (existingPlatform == null)
|
||||
{
|
||||
station.Platforms.Add(new Platform { Name = platform, Length = 20 });
|
||||
}
|
||||
|
||||
|
||||
var waitingArea = station.WaitingAreas.FirstOrDefault(x => x.Name == waitingAreaName);
|
||||
if (waitingArea == null)
|
||||
{
|
||||
waitingArea = new WaitingArea() { Name = waitingAreaName };
|
||||
station.WaitingAreas.Add(waitingArea);
|
||||
}
|
||||
|
||||
foreach (var ticketCheckName in ticketCheck.Split('|'))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(ticketCheckName) && !waitingArea.TicketChecks.Any(x => x.Name == ticketCheckName))
|
||||
{
|
||||
waitingArea.TicketChecks.Add(new TicketCheck { Name = ticketCheckName });
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(data[0]) && !station.TrainStops.Any(x => x.Number == data[0]))
|
||||
{
|
||||
TimeSpan? arrivalTime = null;
|
||||
TimeSpan? departureTime = null;
|
||||
|
||||
if (data[2] == stationName)
|
||||
{
|
||||
departureTime = TimeSpan.Parse(lines.FirstOrDefault(x => x.Split(",")[0] == data[0] && x.Split(",")[1].Contains("送车"))?.Split(",")[7] ?? string.Empty);
|
||||
}
|
||||
else if (data[3] == stationName)
|
||||
{
|
||||
arrivalTime = TimeSpan.Parse(data[7]);
|
||||
}
|
||||
else
|
||||
{
|
||||
arrivalTime = TimeSpan.Parse(data[7]);
|
||||
departureTime = TimeSpan.Parse(lines.FirstOrDefault(x => x.Split(",")[0] == data[0] && x.Split(",")[1].Contains("送车"))?.Split(",")[7] ?? string.Empty);
|
||||
}
|
||||
|
||||
station.TrainStops.Add(new TrainStop
|
||||
{
|
||||
Number = data[0],
|
||||
Terminal = data[3],
|
||||
Origin = data[2],
|
||||
TicketCheckIds = [.. waitingArea.TicketChecks.Select(x=>x.Id)],
|
||||
ArrivalTime = arrivalTime,
|
||||
DepartureTime = departureTime,
|
||||
Platform = platform,
|
||||
Length = data[0].StartsWith('G') || data[0].StartsWith('D') || data[0].StartsWith('C') ? Math.Abs(data[0].GetHashCode()) % 3 == 0 ? 8 : 16 : 18,
|
||||
Landmark = data[8] + "色" ?? null,
|
||||
});
|
||||
}
|
||||
}
|
||||
AddStation(station);
|
||||
}
|
||||
|
||||
public void AddStation(Station station)
|
||||
{
|
||||
_stations.Add(station);
|
||||
|
||||
@@ -3,6 +3,7 @@ using CRSim.Core.Models;
|
||||
using CRSim.Core.Models.Plugin;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace CRSim.Core.Services
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<TargetFramework>net9.0-windows10.0.19041.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Platforms>x64</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
11
CRSim.sln
@@ -1,7 +1,7 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.12.35424.110
|
||||
# Visual Studio Version 18
|
||||
VisualStudioVersion = 18.3.11218.70 d18.3
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CRSim.Core", "CRSim.Core\CRSim.Core.csproj", "{C72B16CC-AA3E-4E0F-A424-07D3987A9FD9}"
|
||||
EndProject
|
||||
@@ -31,10 +31,9 @@ Global
|
||||
{94927020-7AD7-4F6F-A456-F34A8E0BC40A}.Debug|x64.Build.0 = Debug|x64
|
||||
{94927020-7AD7-4F6F-A456-F34A8E0BC40A}.Release|x64.ActiveCfg = Release|x64
|
||||
{94927020-7AD7-4F6F-A456-F34A8E0BC40A}.Release|x64.Build.0 = Release|x64
|
||||
{895EE8B3-1A15-4CA8-9AFE-246585732AA5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{895EE8B3-1A15-4CA8-9AFE-246585732AA5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{895EE8B3-1A15-4CA8-9AFE-246585732AA5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{895EE8B3-1A15-4CA8-9AFE-246585732AA5}.Release|x64.Build.0 = Release|Any CPU
|
||||
{895EE8B3-1A15-4CA8-9AFE-246585732AA5}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{895EE8B3-1A15-4CA8-9AFE-246585732AA5}.Debug|x64.Build.0 = Debug|x64
|
||||
{895EE8B3-1A15-4CA8-9AFE-246585732AA5}.Release|x64.ActiveCfg = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -6,23 +6,38 @@ namespace CRSim
|
||||
{
|
||||
public partial class App : Application
|
||||
{
|
||||
public static IHost AppHost { get; private set; }
|
||||
public IHost AppHost { get; private set; }
|
||||
|
||||
public static MainWindow MainWindow;
|
||||
public string[] commandLineArgs;
|
||||
public CommandLineOptions parsedOptions;
|
||||
public static string AppVersion { get; set; } = Assembly.GetAssembly(typeof(App)).GetName().Version.ToString();
|
||||
private readonly Mutex mutex;
|
||||
public static string AppVersion { get; set; }
|
||||
|
||||
private Mutex mutex;
|
||||
|
||||
public App()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public static T GetService<T>()
|
||||
where T : class
|
||||
{
|
||||
if ((Current as App)!.AppHost.Services.GetService(typeof(T)) is not T service)
|
||||
{
|
||||
throw new ArgumentException($"{typeof(T)} needs to be registered in ConfigureServices within App.xaml.cs.");
|
||||
}
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
protected override async void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
AppVersion = Assembly.GetAssembly(typeof(App)).GetName().Version.ToString();
|
||||
mutex = new Mutex(true, "CRSim", out bool isNewInstance);
|
||||
if (!isNewInstance)
|
||||
{
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
var args = Environment.GetCommandLineArgs();
|
||||
parsedOptions = CommandLineParser.Parse(args);
|
||||
var parsedOptions = CommandLineParser.Parse(Environment.GetCommandLineArgs());
|
||||
if (parsedOptions.Debug)
|
||||
{
|
||||
LaunchDebugConsole();
|
||||
@@ -57,11 +72,6 @@ namespace CRSim
|
||||
PluginService.InitializePlugins(context, services, parsedOptions.ExternalPluginPath);
|
||||
})
|
||||
.Build();
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override async void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
var splashScreenWindow = AppHost.Services.GetRequiredService<StartWindow>();
|
||||
splashScreenWindow.Activate();
|
||||
await PerformInitializationAsync(splashScreenWindow);
|
||||
@@ -76,10 +86,10 @@ namespace CRSim
|
||||
{
|
||||
if(!Debugger.IsAttached) await Task.Delay(2000);
|
||||
startWindow.Status = "正在加载设置...";
|
||||
AppHost.Services.GetService<ISettingsService>().LoadSettings();
|
||||
GetService<ISettingsService>().LoadSettings();
|
||||
if (!Debugger.IsAttached) await Task.Delay(500);
|
||||
startWindow.Status = "正在加载数据库...";
|
||||
AppHost.Services.GetService<IDatabaseService>().ImportData(AppPaths.ConfigFilePath);
|
||||
GetService<IDatabaseService>().ImportData(AppPaths.ConfigFilePath);
|
||||
if (!Debugger.IsAttached) await Task.Delay(500);
|
||||
startWindow.Status = new[] { "正在控票…", "正在关闭垃圾桶盖…", "正在刷绿车底…", "正在准备降弓用刑…" }[new Random().Next(4)];
|
||||
if (!Debugger.IsAttached) await Task.Delay(500);
|
||||
|
||||
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 637 B After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 283 B After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 456 B After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 19 KiB |
@@ -11,7 +11,6 @@
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<EnableMsixTooling>true</EnableMsixTooling>
|
||||
<Nullable>enable</Nullable>
|
||||
<WindowsPackageType>None</WindowsPackageType>
|
||||
<LangVersion>preview</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -45,13 +44,10 @@
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Converters" Version="8.2.250402" />
|
||||
<PackageReference Include="CRSim.WebsiteSimulator" Version="3.1.2" />
|
||||
<PackageReference Include="EPPlus" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.8" />
|
||||
<PackageReference Include="Microsoft.UI.Xaml" Version="2.8.7" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4654" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250606001" />
|
||||
<PackageReference Include="CRSim.WebsiteSimulator" Version="3.1.3" />
|
||||
<PackageReference Include="EPPlus" Version="8.3.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
|
||||
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="3.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -152,15 +148,20 @@
|
||||
|
||||
<!-- Publish Properties -->
|
||||
<PropertyGroup>
|
||||
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
|
||||
<PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun>
|
||||
<PublishTrimmed Condition="'$(Configuration)' == 'Debug'">False</PublishTrimmed>
|
||||
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">False</PublishTrimmed>
|
||||
<PublishReadyToRun>False</PublishReadyToRun>
|
||||
<PublishTrimmed>False</PublishTrimmed>
|
||||
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
|
||||
<ApplicationIcon>Assets\CRSimIcon.ico</ApplicationIcon>
|
||||
<SignAssembly>False</SignAssembly>
|
||||
<PackageIcon>CRSimIcon.png</PackageIcon>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<PublishAot>False</PublishAot>
|
||||
<GenerateAppInstallerFile>False</GenerateAppInstallerFile>
|
||||
<AppxPackageSigningEnabled>False</AppxPackageSigningEnabled>
|
||||
<AppxPackageSigningTimestampDigestAlgorithm>SHA256</AppxPackageSigningTimestampDigestAlgorithm>
|
||||
<AppxAutoIncrementPackageRevision>False</AppxAutoIncrementPackageRevision>
|
||||
<GenerateTestArtifacts>True</GenerateTestArtifacts>
|
||||
<AppxBundle>Never</AppxBundle>
|
||||
<GenerateTemporaryStoreCertificate>True</GenerateTemporaryStoreCertificate>
|
||||
<HoursBetweenUpdateChecks>0</HoursBetweenUpdateChecks>
|
||||
<DefaultLanguage>zh-CN</DefaultLanguage>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -8,15 +8,15 @@
|
||||
IgnorableNamespaces="uap rescap">
|
||||
|
||||
<Identity
|
||||
Name="dce25a3c-6edb-4272-ab6b-496e3069c38f"
|
||||
Publisher="CN=电排骨"
|
||||
Version="1.0.0.0" />
|
||||
Name="53D4FFDC.CRSim"
|
||||
Publisher="CN=A0D23678-D7D4-4933-B369-1877DD38719C"
|
||||
Version="3.1.3.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="dce25a3c-6edb-4272-ab6b-496e3069c38f" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
<mp:PhoneIdentity PhoneProductId="947762ab-9c52-4d97-843d-eeea6b5d81ce" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
<Properties>
|
||||
<DisplayName>国铁信息显示模拟</DisplayName>
|
||||
<PublisherDisplayName>电排骨</PublisherDisplayName>
|
||||
<DisplayName>CRSim</DisplayName>
|
||||
<PublisherDisplayName>电排骨工坊</PublisherDisplayName>
|
||||
<Logo>Assets\CRSimIcon.png</Logo>
|
||||
</Properties>
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
Executable="$targetnametoken$.exe"
|
||||
EntryPoint="$targetentrypoint$">
|
||||
<uap:VisualElements
|
||||
DisplayName="国铁信息显示模拟"
|
||||
DisplayName="CRSim"
|
||||
Description="CRSim"
|
||||
BackgroundColor="transparent"
|
||||
Square150x150Logo="Assets\Square150x150Logo.png"
|
||||
@@ -47,5 +47,6 @@
|
||||
|
||||
<Capabilities>
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
<Capability Name="internetClient"/>
|
||||
</Capabilities>
|
||||
</Package>
|
||||
|
||||
@@ -252,5 +252,28 @@ namespace CRSim.Services
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<Station?> CreateStationAsync()
|
||||
{
|
||||
ContentDialog dialog = new()
|
||||
{
|
||||
XamlRoot = XamlRoot,
|
||||
Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style,
|
||||
Title = "新增车站",
|
||||
PrimaryButtonText = "确定",
|
||||
CloseButtonText = "取消",
|
||||
DefaultButton = ContentDialogButton.Primary,
|
||||
IsPrimaryButtonEnabled = false
|
||||
};
|
||||
var createStationDialog = new CreateStationDialog(isValid => dialog.IsPrimaryButtonEnabled = isValid);
|
||||
dialog.Content = createStationDialog;
|
||||
var result = await dialog.ShowAsync();
|
||||
if (result == ContentDialogResult.Primary)
|
||||
{
|
||||
var inputDialog = dialog.Content as CreateStationDialog;
|
||||
return inputDialog.GeneratedStation;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
{
|
||||
string? GetFile(string[] filter);
|
||||
string? SaveFile(string filter,string fileName);
|
||||
Task<Station?> CreateStationAsync();
|
||||
Task<string?> GetInputAsync(string title,string placeholder);
|
||||
Task<bool> GetConfirmAsync(string title);
|
||||
Task ShowMessageAsync(string title, string message);
|
||||
|
||||
@@ -3,6 +3,8 @@ using System.Diagnostics;
|
||||
using System.Diagnostics.Metrics;
|
||||
using System.IO.Packaging;
|
||||
using System.Reflection;
|
||||
using System.Security.Policy;
|
||||
using Windows.System;
|
||||
|
||||
namespace CRSim.ViewModels
|
||||
{
|
||||
@@ -134,71 +136,8 @@ namespace CRSim.ViewModels
|
||||
}
|
||||
else
|
||||
{
|
||||
await _dialogService.ShowTextAsync("发现新版本 " + update.Name, update.Body + "\n系统即将下载安装新版本。");
|
||||
DispatcherQueue dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
var downloader = new DownloadService();
|
||||
UpdateProgress = 0;
|
||||
try
|
||||
{
|
||||
int lastProgress = 0;
|
||||
var lastUpdate = DateTime.MinValue;
|
||||
downloader.DownloadProgressChanged += (s, e) =>
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
if ((e.ProgressPercentage - lastProgress >= 1) || (now - lastUpdate).TotalMilliseconds >= 100)
|
||||
{
|
||||
lastProgress = (int)e.ProgressPercentage;
|
||||
lastUpdate = now;
|
||||
|
||||
dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
UpdateProgress = (int)e.ProgressPercentage;
|
||||
});
|
||||
}
|
||||
};
|
||||
var path = Path.Combine(AppPaths.TempPath, "update.zip");
|
||||
await downloader.DownloadFileTaskAsync(update.Assets[0].BrowserDownloadUrl, path);
|
||||
UpdateProgress = 0;
|
||||
|
||||
if (!FileHashHelper.VerifySHA256(path, update.Assets[0].Digest.Split(':')[1]))
|
||||
{
|
||||
await _dialogService.ShowMessageAsync("错误", "下载的文件校验失败,请重试。");
|
||||
return;
|
||||
}
|
||||
|
||||
var programDirectory = AppDomain.CurrentDomain.BaseDirectory;
|
||||
var appExePath = Environment.ProcessPath;
|
||||
var appName = Path.GetFileName(appExePath);
|
||||
string batchScript = $@"
|
||||
@echo off
|
||||
taskkill /F /IM ""{appName}"" >nul 2>&1
|
||||
timeout /t 2 >nul
|
||||
cd /d ""{programDirectory}""
|
||||
powershell -Command ""Expand-Archive -Path '{path}' -DestinationPath '{programDirectory}' -Force""
|
||||
del /f /q ""{path}""
|
||||
start """" ""{appExePath}""
|
||||
exit /b 0
|
||||
";
|
||||
string batchFilePath = Path.Combine(Path.GetTempPath(), "CRSimUpdate.bat");
|
||||
File.WriteAllText(batchFilePath, batchScript);
|
||||
var processInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "cmd.exe",
|
||||
Arguments = $"/c \"{batchFilePath}\"",
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
Process.Start(processInfo);
|
||||
Application.Current.Exit();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _dialogService.ShowTextAsync("错误", "更新失败。若使用“官方站”,可尝试更换到“镜像站”。\n" + e);
|
||||
UpdateProgress = 0;
|
||||
return;
|
||||
}
|
||||
await _dialogService.ShowTextAsync("发现新版本 " + update.Name, update.Body + "\n请前往微软商店下载新版本。");
|
||||
await Launcher.LaunchUriAsync(new Uri("https://apps.microsoft.com/detail/9n4xhrrmph8v"));
|
||||
}
|
||||
}
|
||||
[RelayCommand]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using CRSim.Converters;
|
||||
using CRSim.Core.Models;
|
||||
using CRSim.Core.Models.Plugin;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
@@ -34,11 +35,11 @@ public partial class StationManagementPageViewModel : ObservableObject
|
||||
public ObservableCollection<TrainStop> TrainStops { get; private set; } = [];
|
||||
public ObservableCollection<TrainStop> FilteredTrainStops =>[.. TrainStops.Where(x => x.Number.ToUpper().Contains(SearchTrainNumberText.ToUpper()))];
|
||||
|
||||
private readonly IDatabaseService _databaseService = App.AppHost.Services.GetService<IDatabaseService>();
|
||||
private readonly IDatabaseService _databaseService = App.GetService<IDatabaseService>();
|
||||
|
||||
private readonly IDialogService _dialogService = App.AppHost.Services.GetService<IDialogService>();
|
||||
private readonly IDialogService _dialogService = App.GetService<IDialogService>();
|
||||
|
||||
private readonly INetworkService _networkService = App.AppHost.Services.GetService<INetworkService>();
|
||||
private readonly INetworkService _networkService = App.GetService<INetworkService>();
|
||||
public StationManagementPageViewModel()
|
||||
{
|
||||
TrainStops.CollectionChanged += (sender, e) =>
|
||||
@@ -121,15 +122,15 @@ public partial class StationManagementPageViewModel : ObservableObject
|
||||
[RelayCommand]
|
||||
public async Task AddStation()
|
||||
{
|
||||
string? station = await _dialogService.GetInputAsync("请输入车站名称", string.Empty);
|
||||
var station = await _dialogService.CreateStationAsync();
|
||||
if (station != null)
|
||||
{
|
||||
if (StationNames.Contains(station))
|
||||
if (StationNames.Contains(station.Name))
|
||||
{
|
||||
await _dialogService.ShowMessageAsync("添加失败", $"车站 {station} 已存在。");
|
||||
await _dialogService.ShowMessageAsync("添加失败", $"车站 {station.Name} 已存在。");
|
||||
return;
|
||||
}
|
||||
_databaseService.AddStationByName(station);
|
||||
_databaseService.AddStation(station);
|
||||
await _databaseService.SaveData();
|
||||
RefreshStations();
|
||||
}
|
||||
@@ -167,20 +168,18 @@ public partial class StationManagementPageViewModel : ObservableObject
|
||||
return station;
|
||||
}
|
||||
[RelayCommand]
|
||||
public async Task ImportFrom7D()
|
||||
public async Task ExportStation()
|
||||
{
|
||||
var path = _dialogService.GetFile([".exe"]);
|
||||
if (!await Validate()) return;
|
||||
var path = _dialogService.SaveFile(".json", $"{SelectedStation.Name}车站配置");
|
||||
if (path == null) return;
|
||||
try
|
||||
string json = JsonSerializer.Serialize(new Json()
|
||||
{
|
||||
await _databaseService.ImportStationFrom7D(path);
|
||||
await _databaseService.SaveData();
|
||||
RefreshStations();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _dialogService.ShowMessageAsync("导入失败", "文件格式错误或被占用。\n" + e.Message);
|
||||
Stations = [GenerateStation(SelectedStation.Name, WaitingAreas, TrainStops, Platforms) ],
|
||||
TrainNumbers = []
|
||||
}
|
||||
, JsonContext.Default.Json);
|
||||
await File.WriteAllTextAsync(path, json);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ namespace CRSim.Views;
|
||||
|
||||
public sealed partial class DashboardPage : Page
|
||||
{
|
||||
public DashboardPageViewModel ViewModel { get; } = App.AppHost.Services.GetService<DashboardPageViewModel>();
|
||||
public DashboardPageViewModel ViewModel { get; } = App.GetService<DashboardPageViewModel>();
|
||||
|
||||
public DashboardPage()
|
||||
{
|
||||
|
||||
41
CRSim/Views/DialogContents/CreateStationDialog.xaml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Page
|
||||
x:Class="CRSim.Views.CreateStationDialog"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:CRSim.Views"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
<StackPanel Orientation="Vertical" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
|
||||
<RadioButton x:Name="Blank" GroupName="options" Content="新建空白车站" Margin="0,0,0,4" IsChecked="True" Checked="Validate"/>
|
||||
<TextBox x:Name="textBoxBlank" IsEnabled="{x:Bind ToBoolean(Blank.IsChecked), Mode=OneWay}"
|
||||
PlaceholderText="车站名" Margin="0,0,0,8" TextChanged="Validate"/>
|
||||
<RadioButton x:Name="btnFile" GroupName="options" Content="从车站文件导入" Margin="0,0,0,4" Checked="Validate"/>
|
||||
<ContentControl IsEnabled="{x:Bind ToBoolean(btnFile.IsChecked), Mode=OneWay}" HorizontalContentAlignment="Stretch">
|
||||
<Grid Margin="0,0,0,8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox x:Name="textBoxFile" Grid.Column="0" PlaceholderText="文件路径"/>
|
||||
<Button Grid.Column="1" VerticalAlignment="Stretch" Margin="8,0,0,0" Click="ImportFromFileAsync">
|
||||
<FontIcon Glyph="" FontSize="16"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
</ContentControl>
|
||||
<RadioButton x:Name="Broadcast" GroupName="options" Content="从模拟广播系统导入" Margin="0,0,0,4" Checked="Validate"/>
|
||||
<ContentControl IsEnabled="{x:Bind ToBoolean(Broadcast.IsChecked), Mode=OneWay}" HorizontalContentAlignment="Stretch">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox x:Name="textBoxBroadcast" Grid.Column="0" PlaceholderText="文件路径"/>
|
||||
<Button Grid.Column="1" VerticalAlignment="Stretch" Margin="8,0,0,0" Click="ImportFromSS7DSAsync">
|
||||
<FontIcon Glyph="" FontSize="16"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
</ContentControl>
|
||||
</StackPanel>
|
||||
</Page>
|
||||
144
CRSim/Views/DialogContents/CreateStationDialog.xaml.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
namespace CRSim.Views;
|
||||
public sealed partial class CreateStationDialog : Page
|
||||
{
|
||||
|
||||
private readonly IDialogService _dialogService = App.GetService<IDialogService>();
|
||||
|
||||
private Station? _parsedStation = null;
|
||||
|
||||
public Station? GeneratedStation => ToBoolean(Blank.IsChecked) ? new Station
|
||||
{
|
||||
Name = textBoxBlank.Text,
|
||||
Platforms = [],
|
||||
WaitingAreas = [],
|
||||
TrainStops = []
|
||||
} : _parsedStation;
|
||||
|
||||
private readonly Action<bool> _onValidityChanged;
|
||||
|
||||
public bool ToBoolean(bool? value)
|
||||
{
|
||||
return value == true;
|
||||
}
|
||||
|
||||
private void Validate(object sender, object e)
|
||||
{
|
||||
if (textBoxBlank == null) return;
|
||||
bool isValid;
|
||||
if (ToBoolean(Blank.IsChecked))
|
||||
{
|
||||
isValid = !string.IsNullOrWhiteSpace(textBoxBlank.Text);
|
||||
}
|
||||
else
|
||||
{
|
||||
isValid = _parsedStation != null;
|
||||
}
|
||||
_onValidityChanged?.Invoke(isValid);
|
||||
}
|
||||
|
||||
public CreateStationDialog(Action<bool> onValidityChanged)
|
||||
{
|
||||
_onValidityChanged = onValidityChanged;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private async void ImportFromSS7DSAsync(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var path = _dialogService.GetFile([".exe"]);
|
||||
textBoxBroadcast.Text = path;
|
||||
if (path == null) return;
|
||||
try
|
||||
{
|
||||
Station station = new() { Platforms = [], WaitingAreas = [], TrainStops = [] };
|
||||
var stationName = (await File.ReadAllTextAsync(path.Replace("车站广播系统.exe", "info.ini"))).Split("|")[0];
|
||||
var lines = await File.ReadAllLinesAsync(path.Replace("车站广播系统.exe", "data.csv"));
|
||||
station.Name = stationName;
|
||||
foreach (var line in lines)
|
||||
{
|
||||
var data = line.Split(',');
|
||||
var waitingAreaName = string.IsNullOrWhiteSpace(data.Last()) ? "未知" : data.Last();
|
||||
var platform = string.IsNullOrWhiteSpace(data[5]) ? "未知" : data[5];
|
||||
var ticketCheck = string.IsNullOrWhiteSpace(data[4]) ? string.Empty : data[4];
|
||||
|
||||
var existingPlatform = station.Platforms.FirstOrDefault(x => x.Name == platform);
|
||||
if (existingPlatform == null)
|
||||
{
|
||||
station.Platforms.Add(new Platform { Name = platform, Length = 20 });
|
||||
}
|
||||
|
||||
|
||||
var waitingArea = station.WaitingAreas.FirstOrDefault(x => x.Name == waitingAreaName);
|
||||
if (waitingArea == null)
|
||||
{
|
||||
waitingArea = new WaitingArea() { Name = waitingAreaName };
|
||||
station.WaitingAreas.Add(waitingArea);
|
||||
}
|
||||
|
||||
foreach (var ticketCheckName in ticketCheck.Split('|'))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(ticketCheckName) && !waitingArea.TicketChecks.Any(x => x.Name == ticketCheckName))
|
||||
{
|
||||
waitingArea.TicketChecks.Add(new TicketCheck { Name = ticketCheckName });
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(data[0]) && !station.TrainStops.Any(x => x.Number == data[0]))
|
||||
{
|
||||
TimeSpan? arrivalTime = null;
|
||||
TimeSpan? departureTime = null;
|
||||
|
||||
if (data[2] == stationName)
|
||||
{
|
||||
departureTime = TimeSpan.Parse(lines.FirstOrDefault(x => x.Split(",")[0] == data[0] && x.Split(",")[1].Contains("送车"))?.Split(",")[7] ?? string.Empty);
|
||||
}
|
||||
else if (data[3] == stationName)
|
||||
{
|
||||
arrivalTime = TimeSpan.Parse(data[7]);
|
||||
}
|
||||
else
|
||||
{
|
||||
arrivalTime = TimeSpan.Parse(data[7]);
|
||||
departureTime = TimeSpan.Parse(lines.FirstOrDefault(x => x.Split(",")[0] == data[0] && x.Split(",")[1].Contains("送车"))?.Split(",")[7] ?? string.Empty);
|
||||
}
|
||||
|
||||
station.TrainStops.Add(new TrainStop
|
||||
{
|
||||
Number = data[0],
|
||||
Terminal = data[3],
|
||||
Origin = data[2],
|
||||
TicketCheckIds = [.. waitingArea.TicketChecks.Select(x => x.Id)],
|
||||
ArrivalTime = arrivalTime,
|
||||
DepartureTime = departureTime,
|
||||
Platform = platform,
|
||||
Length = data[0].StartsWith('G') || data[0].StartsWith('D') || data[0].StartsWith('C') ? Math.Abs(data[0].GetHashCode()) % 3 == 0 ? 8 : 16 : 18,
|
||||
Landmark = data[8] + "色" ?? null,
|
||||
});
|
||||
}
|
||||
}
|
||||
_parsedStation = station;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_parsedStation = null;
|
||||
}
|
||||
Validate(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private async void ImportFromFileAsync(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var path = _dialogService.GetFile([".json"]);
|
||||
textBoxFile.Text = path;
|
||||
if (path == null) return;
|
||||
try
|
||||
{
|
||||
var json = File.ReadAllText(path).Replace("StationStop", "TrainStop");
|
||||
var data = JsonSerializer.Deserialize<Json>(json, JsonContext.Default.Json);
|
||||
_parsedStation = data?.Stations.FirstOrDefault();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_parsedStation = null;
|
||||
}
|
||||
Validate(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ namespace CRSim.Views;
|
||||
|
||||
public sealed partial class PlatformDiagramPage : Page
|
||||
{
|
||||
public PlatformDiagramPageViewModel ViewModel { get; } = App.AppHost.Services.GetService<PlatformDiagramPageViewModel>();
|
||||
public PlatformDiagramPageViewModel ViewModel { get; } = App.GetService<PlatformDiagramPageViewModel>();
|
||||
|
||||
public PlatformDiagramPage()
|
||||
{
|
||||
@@ -12,6 +12,6 @@ public sealed partial class PlatformDiagramPage : Page
|
||||
|
||||
private void Page_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
App.AppHost.Services.GetService<IDialogService>().XamlRoot = this.XamlRoot;
|
||||
App.GetService<IDialogService>().XamlRoot = this.XamlRoot;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace CRSim.Views;
|
||||
|
||||
public sealed partial class PluginManagementPage : Page
|
||||
{
|
||||
public PluginManagementPageViewModel ViewModel { get; } = App.AppHost.Services.GetService<PluginManagementPageViewModel>();
|
||||
public PluginManagementPageViewModel ViewModel { get; } = App.GetService<PluginManagementPageViewModel>();
|
||||
|
||||
public PluginManagementPage()
|
||||
{
|
||||
@@ -18,6 +18,6 @@ public sealed partial class PluginManagementPage : Page
|
||||
|
||||
private void Page_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
App.AppHost.Services.GetService<IDialogService>().XamlRoot = this.XamlRoot;
|
||||
App.GetService<IDialogService>().XamlRoot = this.XamlRoot;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ namespace CRSim.Views;
|
||||
|
||||
public sealed partial class ScreenSimulatorPage : Page
|
||||
{
|
||||
public ScreenSimulatorPageViewModel ViewModel { get; } = App.AppHost.Services.GetService<ScreenSimulatorPageViewModel>();
|
||||
public ScreenSimulatorPageViewModel ViewModel { get; } = App.GetService<ScreenSimulatorPageViewModel>();
|
||||
|
||||
public ScreenSimulatorPage()
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace CRSim.Views;
|
||||
|
||||
public sealed partial class SettingsPage : Page
|
||||
{
|
||||
public SettingsPageViewModel ViewModel { get; } = App.AppHost.Services.GetService<SettingsPageViewModel>();
|
||||
public SettingsPageViewModel ViewModel { get; } = App.GetService<SettingsPageViewModel>();
|
||||
|
||||
public SettingsPage()
|
||||
{
|
||||
@@ -18,6 +18,6 @@ public sealed partial class SettingsPage : Page
|
||||
|
||||
private void Page_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
App.AppHost.Services.GetService<IDialogService>().XamlRoot = this.XamlRoot;
|
||||
App.GetService<IDialogService>().XamlRoot = this.XamlRoot;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,6 @@
|
||||
<AppBarButton Icon="Clear" Label="删除" Command="{x:Bind ViewModel.DeleteStationCommand}" CommandParameter="{x:Bind StationsList.SelectedItem, Mode=OneWay}" />
|
||||
<CommandBar.SecondaryCommands>
|
||||
<AppBarButton Label="清空所有车站" Command="{x:Bind ViewModel.DeleteAllStationsCommand}" />
|
||||
<AppBarButton Label="从模拟广播系统导入" Command="{x:Bind ViewModel.ImportFrom7DCommand}" />
|
||||
</CommandBar.SecondaryCommands>
|
||||
</CommandBar>
|
||||
<AutoSuggestBox Grid.Row="1" PlaceholderText="搜索车站" QueryIcon="Find" Text="{x:Bind ViewModel.SearchText,Mode=TwoWay}"
|
||||
@@ -239,12 +238,20 @@
|
||||
</ListView>
|
||||
</Border>
|
||||
<TextBlock Text="杂项" Style="{StaticResource BodyStrongTextBlockStyle}" Margin="0,24,0,0"/>
|
||||
<controls:SettingsCard Header="保存车站配置" Description="验证并保存数据配置" Margin="0,12,0,0">
|
||||
<controls:SettingsCard.HeaderIcon>
|
||||
<FontIcon Glyph=""/>
|
||||
</controls:SettingsCard.HeaderIcon>
|
||||
<Button Content="保存配置" Command="{x:Bind ViewModel.SaveChangesCommand}" Style="{StaticResource AccentButtonStyle}" />
|
||||
</controls:SettingsCard>
|
||||
<StackPanel Orientation="Vertical" Spacing="4" Margin="0,12,0,0">
|
||||
<controls:SettingsCard Header="导出车站" Description="验证并导出车站文件">
|
||||
<controls:SettingsCard.HeaderIcon>
|
||||
<FontIcon Glyph=""/>
|
||||
</controls:SettingsCard.HeaderIcon>
|
||||
<Button Content="导出" Command="{x:Bind ViewModel.ExportStationCommand}" Style="{StaticResource AccentButtonStyle}" />
|
||||
</controls:SettingsCard>
|
||||
<controls:SettingsCard Header="保存配置" Description="验证并保存数据配置">
|
||||
<controls:SettingsCard.HeaderIcon>
|
||||
<FontIcon Glyph=""/>
|
||||
</controls:SettingsCard.HeaderIcon>
|
||||
<Button Content="保存" Command="{x:Bind ViewModel.SaveChangesCommand}" Style="{StaticResource AccentButtonStyle}" />
|
||||
</controls:SettingsCard>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</ContentControl>
|
||||
</ScrollViewer>
|
||||
|
||||
@@ -2,7 +2,7 @@ namespace CRSim.Views;
|
||||
|
||||
public sealed partial class StationManagementPage : Page
|
||||
{
|
||||
public StationManagementPageViewModel ViewModel { get; } = App.AppHost.Services.GetService<StationManagementPageViewModel>();
|
||||
public StationManagementPageViewModel ViewModel { get; } = App.GetService<StationManagementPageViewModel>();
|
||||
|
||||
public StationManagementPage()
|
||||
{
|
||||
@@ -12,6 +12,6 @@ public sealed partial class StationManagementPage : Page
|
||||
|
||||
private void Page_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
App.AppHost.Services.GetService<IDialogService>().XamlRoot = this.XamlRoot;
|
||||
App.GetService<IDialogService>().XamlRoot = this.XamlRoot;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ namespace CRSim.Views;
|
||||
|
||||
public sealed partial class TrainNumberManagementPage : Page
|
||||
{
|
||||
public TrainNumberManagementPageViewModel ViewModel { get; } = App.AppHost.Services.GetService<TrainNumberManagementPageViewModel>();
|
||||
public TrainNumberManagementPageViewModel ViewModel { get; } = App.GetService<TrainNumberManagementPageViewModel>();
|
||||
|
||||
public TrainNumberManagementPage()
|
||||
{
|
||||
@@ -12,6 +12,6 @@ public sealed partial class TrainNumberManagementPage : Page
|
||||
|
||||
private void Page_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
App.AppHost.Services.GetService<IDialogService>().XamlRoot = this.XamlRoot;
|
||||
App.GetService<IDialogService>().XamlRoot = this.XamlRoot;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ namespace CRSim.Views;
|
||||
|
||||
public sealed partial class WebsiteSimulatorPage : Page
|
||||
{
|
||||
public WebsiteSimulatorPageViewModel ViewModel { get; } = App.AppHost.Services.GetService<WebsiteSimulatorPageViewModel>();
|
||||
public WebsiteSimulatorPageViewModel ViewModel { get; } = App.GetService<WebsiteSimulatorPageViewModel>();
|
||||
|
||||
public WebsiteSimulatorPage()
|
||||
{
|
||||
|
||||