项目 7 情景控制:虚拟时钟、项目资源、Windows Media Player、TrackBar

- 虚拟时钟:实训台时钟面板可拨时间、1×/10×/60× 倍速、恢复真实时间;编译时把
  DateTime.Now / Today / UtcNow 改写成 SimClock;评分规则加 clock 步骤
- 项目资源(Properties.Resources):平台素材库(自绘开关图片、合成门铃声),"项目资源"对话框勾选,
  平台生成 Properties/Resources.Designer.cs;设计器图片框的"图片"属性可选资源;程序目录下的 Resources
  指向素材库;未添加资源时给中文提示
- Windows Media Player(AxWMPLib):设置 URL 自动播放、Ctlcontrols.play/stop,浏览器里播放声音,
  实训台加广播喇叭;编译时给窗体嵌空资源,OcxState 取到 null 不报错;图片和声音统一按内容缓存(blob)
- TrackBar:拖动、点轨道、方向键,先 ValueChanged 再 Scroll;设计器加"滑动条""媒体播放器"
- 评分规则加 value、media 检查;示例项目 7 任务 1–3(修正书上开关灯判断写反、漏填串口列表)
- 修正:Properties/ 下的资源类不当作窗体;带命名空间的 System.AppDomain.CurrentDomain.BaseDirectory 放行
- 测试:虚拟时钟、TrackBar、项目 7 示例评分、作业起点带资源类

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-10-23 14:11:05 +08:00
1 parent a9af26c2d6
commit eedf1aa2fb
66 files changed
+3538 -43

No files matched your search

@@ -0,0 +1,20 @@
using System.ComponentModel;
using System.Drawing;
using System.IO;
namespace Sim.Host;
/// <summary>
/// 项目资源(Properties.Resources)的素材库。平台为学生工程生成的 Properties/Resources.Designer.cs 通过这里读取素材,
/// 和 VS 一样每次取图片都得到一个新的 Bitmap。
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class ProjectResources
{
/// <summary>素材文件所在目录(运行器目录下的 Resources/gallery)</summary>
public static string Directory { get; } = Path.Combine(Path.GetDirectoryName(typeof(ProjectResources).Assembly.Location)!, "Resources", "gallery");
public static Bitmap Image(string file) => (Bitmap)System.Drawing.Image.FromStream(new MemoryStream(Bytes(file)));
public static byte[] Bytes(string file) => File.ReadAllBytes(Path.Combine(Directory, Path.GetFileName(file)));
}
+46
View File
@@ -0,0 +1,46 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.Text.Json.Nodes;
namespace Sim.Host;
/// <summary>
/// 虚拟时钟(实训台上可以拨时间、调倍速;教材项目 7 按时刻切换情景模式)。
/// 编译时把学生代码里的 DateTime.Now / Today / UtcNow 改写成这里的属性(见 Sim.Runner/ClockRewriter.cs)。
/// 没有拨过时就是真实时间。
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class SimClock
{
private sealed record Setting(DateTime Wall, long RealAt, double Speed);
private static volatile Setting? _setting;
public static DateTime Now
{
get
{
var setting = _setting;
if (setting == null) return DateTime.Now;
var elapsed = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - setting.RealAt;
return setting.Wall.AddMilliseconds(elapsed * setting.Speed);
}
}
public static DateTime Today => Now.Date;
public static DateTime UtcNow => Now.ToUniversalTime();
/// <summary>平台内部使用:应用服务器推送的 {"wall":"2024-01-16T06:40:00","realAt":…,"speed":60},null 恢复真实时间</summary>
public static void Update(JsonNode? clock)
{
if (clock is not JsonObject json
|| !DateTime.TryParseExact((string?)json["wall"], "yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out var wall))
{
_setting = null;
return;
}
_setting = new Setting(DateTime.SpecifyKind(wall, DateTimeKind.Local), (long?)json["realAt"] ?? 0, (double?)json["speed"] ?? 1);
}
}
+9 -5
View File
@@ -86,19 +86,23 @@ public static class SimHost
public static string AppBaseDirectory { get; set; } = AppContext.BaseDirectory;
/// <summary>图片在浏览器端的编号;第一次用到时先把图片数据发过去,浏览器缓存后同一张图不再重发</summary>
public static string ImageKey(System.Drawing.Image image)
public static string ImageKey(System.Drawing.Image image) => DataKey(image.SimKey, image.Data, image.MimeType);
/// <summary>
/// 图片、声音等数据在浏览器端的编号(key 通常是内容的哈希):第一次用到时先把数据发过去,之后只发编号。
/// </summary>
public static string DataKey(string key, byte[] data, string mime)
{
var key = image.SimKey;
bool isNew;
lock (SentImages) isNew = SentImages.Add(key);
if (isNew)
{
Send(new JsonObject
{
["type"] = "image",
["type"] = "blob",
["key"] = key,
["mime"] = image.MimeType,
["data"] = Convert.ToBase64String(image.Data),
["mime"] = mime,
["data"] = Convert.ToBase64String(data),
});
}
return key;