feat: 车站数据分享

This commit is contained in:
denglihong2007
2025-12-07 02:46:53 +08:00
parent 27fafe9ad5
commit ee7d6ca500
29 changed files with 311 additions and 229 deletions

View File

@@ -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);

View File

@@ -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);

View File

@@ -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
{

View File

@@ -4,6 +4,7 @@
<TargetFramework>net9.0-windows10.0.19041.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>x64</Platforms>
</PropertyGroup>
<ItemGroup>

View File

@@ -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

View File

@@ -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);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 B

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 B

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 456 B

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -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>

View File

@@ -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>

View File

@@ -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;
}
}
}

View File

@@ -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);

View File

@@ -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]

View File

@@ -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

View File

@@ -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()
{

View 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="&#xE8E5;" 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="&#xE8E5;" FontSize="16"/>
</Button>
</Grid>
</ContentControl>
</StackPanel>
</Page>

View 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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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()
{

View File

@@ -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;
}
}

View File

@@ -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="&#xE74E;"/>
</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="&#xE72D;"/>
</controls:SettingsCard.HeaderIcon>
<Button Content="导出" Command="{x:Bind ViewModel.ExportStationCommand}" Style="{StaticResource AccentButtonStyle}" />
</controls:SettingsCard>
<controls:SettingsCard Header="保存配置" Description="验证并保存数据配置">
<controls:SettingsCard.HeaderIcon>
<FontIcon Glyph="&#xE74E;"/>
</controls:SettingsCard.HeaderIcon>
<Button Content="保存" Command="{x:Bind ViewModel.SaveChangesCommand}" Style="{StaticResource AccentButtonStyle}" />
</controls:SettingsCard>
</StackPanel>
</StackPanel>
</ContentControl>
</ScrollViewer>

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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()
{