refactor: introduce new data struct

This commit is contained in:
2025-09-04 14:16:06 +08:00
parent 5a21245b49
commit 16a6411368
9 changed files with 460 additions and 30 deletions

View File

@ -1,6 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
</configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@ -8,10 +8,11 @@
<OutputType>WinExe</OutputType>
<RootNamespace>HFUTCourseSimulation</RootNamespace>
<AssemblyName>HFUTCourseSimulation</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@ -37,8 +38,24 @@
<HintPath>..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
</Reference>
<Reference Include="System.Collections.Immutable, Version=9.0.0.8, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Collections.Immutable.9.0.8\lib\net462\System.Collections.Immutable.dll</HintPath>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Memory, Version=4.0.1.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll</HintPath>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
@ -60,6 +77,10 @@
<Compile Include="Course.cs" />
<Compile Include="General.cs" />
<Compile Include="ImageExport.cs" />
<Compile Include="Kernel\Context.cs" />
<Compile Include="Kernel\MsgRecorder.cs" />
<Compile Include="Kernel\SimData.cs" />
<Compile Include="Kernel\UserData.cs" />
<Compile Include="Simulation.xaml.cs">
<DependentUpon>Simulation.xaml</DependentUpon>
</Compile>

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HFUTCourseSimulation.Kernel {
public class Context {
private static Context s_Instance = new Context();
public static Context GetSingleton() { return s_Instance; }
private Context() {
currentSemester = null;
currentCourse = null;
currentSchedule = null;
}
private UserData.Semester currentSemester;
private UserData.Course currentCourse;
private UserData.Schedule currentSchedule;
}
}

View File

@ -0,0 +1,91 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HFUTCourseSimulation.Kernel.MsgRecorder {
/// <summary>
/// 消息的类型
/// </summary>
public enum Kind {
Info,
Warning,
Error
}
/// <summary>
/// 被记录的消息条目
/// </summary>
public class Entry {
public Entry(Kind kind, string message) {
this.kind = kind;
this.message = message;
}
public readonly Kind kind;
public readonly string message;
}
/// <summary>
/// 用于记录模拟过程中的消息(例如错误,警告等)
/// </summary>
public class Recorder : IEnumerable<Entry> {
public Recorder() {
entries = new List<Entry>();
hasError = false;
}
private List<Entry> entries;
private bool hasError;
/// <summary>
/// 记录一条提示消息
/// </summary>
/// <param name="message">消息内容</param>
public void Info(string message) {
entries.Add(new Entry(Kind.Info, message));
}
/// <summary>
/// 记录一条警告消息
/// </summary>
/// <param name="message">消息内容</param>
public void Warning(string message) {
entries.Add(new Entry(Kind.Warning, message));
}
/// <summary>
/// 记录一条错误消息
/// </summary>
/// <param name="message">消息内容</param>
public void Error(string message) {
this.hasError = true;
entries.Add(new Entry(Kind.Error, message));
}
public IEnumerator<Entry> GetEnumerator() {
return entries.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
/// <summary>
/// 检查过程中是否有错误类型的消息被记录
/// </summary>
/// <returns>如果有则返回true否则为false</returns>
public bool HasError() {
return hasError;
}
/// <summary>
/// 重置记录器状态
/// </summary>
public void Clear() {
entries.Clear();
hasError = false;
}
}
}

View File

@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
namespace HFUTCourseSimulation.Kernel.SimData {
/// <summary>
/// 节次的类型
/// </summary>
public enum IndexKind {
/// <summary>
/// 破晓(太阳没升起前)
/// </summary>
Dawn,
/// <summary>
/// 上午(太阳升起后到正午)
/// </summary>
Morning,
/// <summary>
/// 下午(正午到太阳落山)
/// </summary>
Afternoon,
/// <summary>
/// 晚上(太阳落山后)
/// </summary>
Night
}
public class Semester {
/// <summary>
/// 学期开始日期。
/// 该类保证该日期一定是星期一,且没有时间数据。
/// </summary>
public readonly DateTime startDate;
/// <summary>
/// 教学周个数。
/// 该类保证该数值与weeks中存储的数据个数相同。
/// </summary>
public readonly int weekCount;
/// <summary>
/// 每天课程的节次数。
/// 该类保证该数值总是大于等于1。
/// </summary>
public readonly int indexCount;
/// <summary>
/// 早餐插入在第几节次后。
/// 该类保证该数值总是位于0至indexCount之间含首尾
/// </summary>
public readonly int breakfastAt;
/// <summary>
/// 午餐插入在第几节次后。
/// 该类保证该数值总是位于0至indexCount之间含首尾
/// 且总是大于等于breakfastAt。
/// </summary>
public readonly int lunchAt;
/// <summary>
/// 晚餐插入在第几节次后。
/// 该类保证该数值总是位于0至indexCount之间含首尾
/// 且总是大于等于lunchAt。
/// </summary>
public readonly int dinnerAt;
/// <summary>
/// 每周课程数据。
/// </summary>
public readonly ImmutableList<Week> weeks;
public IndexKind GetIndexKind(int index) {
if (index <= 0) throw new ArgumentException("index out of range");
else if (index <= breakfastAt) return IndexKind.Dawn;
else if (index <= lunchAt) return IndexKind.Morning;
else if (index <= dinnerAt) return IndexKind.Afternoon;
else if (index <= indexCount) return IndexKind.Night;
else throw new ArgumentException("index out of range");
}
}
public class Week {
/// <summary>
/// 每周七天的数据。
/// 该类保证该字段总包含7项。
/// </summary>
public readonly ImmutableList<Day> days;
}
public class Day {
/// <summary>
/// 这一天的所有课程。
/// </summary>
public readonly ImmutableList<Lesson> lessons;
}
public class Lesson {
/// <summary>
/// 课程的名称。
/// </summary>
public readonly string name;
/// <summary>
/// 课程的说明,例如教室位置,教师姓名等。
/// 该值可以包含换行。
/// </summary>
public readonly string description;
/// <summary>
/// 课程的颜色
/// </summary>
public readonly Color color;
/// <summary>
/// 课程的起始节次。
/// 该类保证该值位于1到indexCount之间含首尾
/// </summary>
public readonly int startIndex;
/// <summary>
/// 课程的结束节次。
/// 该类保证该值位于1到indexCount之间含首尾
/// 且大于等于startIndex。
/// </summary>
public readonly int endIndex;
}
}

View File

@ -0,0 +1,155 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Globalization;
using Newtonsoft.Json;
namespace HFUTCourseSimulation.Kernel.UserData {
/// <summary>
/// 学期数据
/// </summary>
public class Semester {
public Semester() {
startDate = DateTime.Today;
weekCount = 20.ToString();
indexCount = 11.ToString();
breakfastAt = 0.ToString();
lunchAt = 4.ToString();
dinnerAt = 8.ToString();
courses = new List<Course>();
}
/// <summary>
/// 学期开始日期(星期一)
/// </summary>
[JsonProperty("start_date")]
[JsonConverter(typeof(CustomDateTimeConverter))]
public DateTime startDate;
/// <summary>
/// 教学周个数
/// </summary>
[JsonProperty("week_count")]
public string weekCount;
/// <summary>
/// 每天课程的节次数
/// </summary>
[JsonProperty("index_count")]
public string indexCount;
/// <summary>
/// 早餐插入在第几节次后
/// </summary>
[JsonProperty("breakfast_at")]
public string breakfastAt;
/// <summary>
/// 午餐插入在第几节次后
/// </summary>
[JsonProperty("lunch_at")]
public string lunchAt;
/// <summary>
/// 晚餐插入在第几节次后
/// </summary>
[JsonProperty("dinner_at")]
public string dinnerAt;
/// <summary>
/// 课程列表
/// </summary>
[JsonProperty("courses")]
public List<Course> courses;
}
public class Course {
public Course() {
name = "";
description = "";
color = Color.LightBlue;
schedules = new List<Schedule>();
}
/// <summary>
/// 课程的名称
/// </summary>
[JsonProperty("name")]
public string name;
/// <summary>
/// 课程的说明,例如教室位置,教师姓名等。
/// </summary>
[JsonProperty("description")]
public string description;
/// <summary>
/// 课程的颜色
/// </summary>
[JsonProperty("color")]
[JsonConverter(typeof(CustomColorConverter))]
public Color color;
/// <summary>
/// 课程的所有安排
/// </summary>
[JsonProperty("schedules")]
public List<Schedule> schedules;
}
public class Schedule {
public Schedule() {
week = "";
day = "";
index = "";
}
/// <summary>
/// 安排在哪些周
/// </summary>
[JsonProperty("week")]
public string week;
/// <summary>
/// 安排在周的星期几
/// </summary>
[JsonProperty("day")]
public string day;
/// <summary>
/// 安排在这些日子的哪些节次
/// </summary>
[JsonProperty("index")]
public string index;
}
internal class CustomDateTimeConverter : JsonConverter<DateTime> {
private static readonly string DATETIME_FORMAT = "yyyy-MM-dd";
public override DateTime ReadJson(JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer) {
if (reader.TokenType == JsonToken.String) {
var value = reader.Value as string;
if (DateTime.TryParseExact(value, DATETIME_FORMAT, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime rv)) {
return rv;
} else {
throw new JsonSerializationException($"given string can not be parsed as DateTime: {value}");
}
} else {
throw new JsonSerializationException($"expect a string but got {reader.TokenType}");
}
}
public override void WriteJson(JsonWriter writer, DateTime value, JsonSerializer serializer) {
writer.WriteValue(value.ToString(DATETIME_FORMAT, CultureInfo.InvariantCulture));
}
}
internal class CustomColorConverter : JsonConverter<Color> {
public override Color ReadJson(JsonReader reader, Type objectType, Color existingValue, bool hasExistingValue, JsonSerializer serializer) {
if (reader.TokenType == JsonToken.Integer) {
var argb = Convert.ToInt32(reader.Value);
return Color.FromArgb(argb);
} else {
throw new JsonSerializationException($"expect a integer but got {reader.TokenType}");
}
}
public override void WriteJson(JsonWriter writer, Color value, JsonSerializer serializer) {
writer.WriteValue(value.ToArgb());
}
}
}

View File

@ -1,53 +1,54 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本: 4.0.30319.42000
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// 对此文件的更改可能导致不正确的行为,如果
// 重新生成代码,则所做更改将丢失。
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace HFUTCourseSimulation.Properties {
using System;
/// <summary>
/// 强类型资源类,用于查找本地化字符串等。
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// 此类是由 StronglyTypedResourceBuilder
// 类通过类似于 ResGen Visual Studio 的工具自动生成的。
// 若要添加或删除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// 返回此类使用的缓存 ResourceManager 实例。
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if ((resourceMan == null)) {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HFUTCourseSimulation.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 覆盖当前线程的 CurrentUICulture 属性
/// 使用此强类型的资源类的资源查找。
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {

View File

@ -9,14 +9,14 @@
//------------------------------------------------------------------------------
namespace HFUTCourseSimulation.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;

View File

@ -1,4 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="12.0.1" targetFramework="net45" />
<package id="System.Buffers" version="4.5.1" targetFramework="net48" />
<package id="System.Collections.Immutable" version="9.0.8" targetFramework="net48" />
<package id="System.Memory" version="4.5.5" targetFramework="net48" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net48" />
</packages>