refactor: 插件 UI 从 WinForms 迁移到 CycleGUI,并修复代码质量问题

将 StandardScene 各插件的配置/监控窗体从 WinForms 迁移到 CycleGUI(删除 .Designer.cs/.resx,重写为 PanelBuilder 立即模式 UI,新增 CycleUiHelper 统一对话框)。

同时修复代码审核中的问题:
- 后台文件写入加锁 + try/catch(ButtonBoxManager / DoorManager,对齐 LoopViewer.SaveTasks 模式)
- CoderFieldsMetadata.cs 启用 #nullable enable,消除 CS8632 警告
- DummyCar 移除已废弃的 rightClickAction()/SetPosition()
- CarRemoteHelper.OpenVehicleWebPage 的 Process.Start 加 try/catch
- 重命名名不副实的 Mstsc()(现为打开网页)
- 统一弃元命名为 _
- TrafficInterlockViewer 改用稳定 Id(GUID)做选择/编辑,替代行索引
- csproj 改用 $(CGUILibDir) 解析 CycleGUI,绝对路径收敛到 Directory.Build.props

构建:dotnet build StandardScene.sln → 0 错误,30 警告(均为历史遗留)。
注:static 单例状态重构(审核第 8 项)暂未处理,留待单独任务。
This commit is contained in:
zhaowei.huang
2026-06-26 15:00:53 +08:00
parent c8e540d272
commit a0dc1e6cd0
91 changed files with 3946 additions and 15419 deletions
+3 -292
View File
@@ -1,294 +1,5 @@
using Newtonsoft.Json;
using SimpleLite;
using SimpleLite.RCS;
using SimpleLite.RCS.CarTypes;
using SimpleLite.CADTools;
using SimpleLite.Props;
using SimpleLite.UI;
using SimpleCore;
using SimpleCore.Library;
using SimpleCore.PropType;
using StandardScene.Model;
using System;
using System.Collections.Generic;
using System.Diagnostics.Eventing.Reader;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace StandardScene
namespace StandardScene
{
[CADToolDescriptor(name = "复制目标站点所有字段")]
public class CopySiteFieldsFromTarget : CADTool
{
public override async void Invoke()
{
var sel = SimpleMonitor.selected.OfType<UISite>().ToList();
var targetPoint = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true });
var targetSite = SimpleLib.GetSite(targetPoint.site);
var ignoreKey = new string[] { "mustFree" };
if (sel.Count <= 0) return;
foreach (var site in sel)
{
foreach (var field in targetSite.fields)
{
if (ignoreKey.Contains(field.Key)) continue;
site.fields[field.Key] = field.Value;
}
}
}
}
[CADToolDescriptor(name = "区域流量管控-限制进入区域的车数量")]
public class RegionalTrafficControl : CADTool
{
// 重写调用方法:执行区域流量管控的车辆数量限制配置
public override async void Invoke()
{
// 获取选中的所有站点UI对象
var selectedSites = SimpleMonitor.selected.OfType<UISite>().ToList();
// 弹出输入框,提示用户按「区域编号,限制数量」格式输入,取消则直接返回
var inputDialogResult = InputBox.ShowDialog("请输入区域编号和限制数量,格式:1,3");
if (inputDialogResult != SimpleLite.DialogResult.OK) return;
// 拆分输入的区域编号和限制数量
var inputValues = InputBox.ResultValue.Split(',');
// 若无配置信息,直接返回
if (inputValues.Length <= 1) return;
// 解析区域编号(浮点型保留原类型,兼容后续扩展)
var areaNumber = float.Parse(inputValues[0]);
// 解析区域车辆限制数量(浮点型保留原类型,兼容非整数配置)
var limitVehicleCount = float.Parse(inputValues[1]);
// 遍历所有选中站点,为其添加区域流量管控的字段配置
foreach (var currentSite in selectedSites)
{
// 配置字段:area+区域编号 作为键,限制数量作为值
if (!currentSite.fields.ContainsKey($"Region{areaNumber}"))
{
currentSite.fields.Add($"Region{areaNumber}", limitVehicleCount.ToString());
}
}
}
}
[CADToolDescriptor(name = "复制目标站点所有字段(不覆盖)")]
public class CopySiteFieldsFromTargetNoOverwrite : CADTool
{
public override async void Invoke()
{
var sel = SimpleMonitor.selected.OfType<UISite>().ToList();
var targetPoint = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true });
var targetSite = SimpleLib.GetSite(targetPoint.site);
var ignore = new string[] { "mustFree" };
if (sel.Count <= 0) return;
foreach (var site in sel)
{
foreach (var field in targetSite.fields)
{
if (!site.fields.ContainsKey(field.Key))
{
site.fields[field.Key] = field.Value;
}
}
}
}
}
[CADToolDescriptor(name = "复制目标站点颜色")]
public class BatchChangeSiteColor : CADTool
{
public override async void Invoke()
{
var sel = SimpleMonitor.selected.OfType<UISite>().ToList();
var targetPoint = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true });
var targetSite = (UISite)SimpleLib.GetSite(targetPoint.site);
if (sel.Count <= 0) return;
foreach (var site in sel)
{
site.color = targetSite.color;
}
}
}
[CADToolDescriptor(name = "导入FASS地图")]
public class ImportFASSMap : CADTool
{
public override void Invoke()
{
//打开文件选择框
using (var ofd = new System.Windows.Forms.OpenFileDialog())
{
ofd.InitialDirectory = "C:\\";
ofd.Filter = "FASS地图文件(*.json)|*.json";
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string filePath = ofd.FileName;
//解析文件内容
string jsonString = File.ReadAllText(filePath);
MapStructure mapStructure = JsonConvert.DeserializeObject<MapStructure>(jsonString);
Model.Configuration configuration = ConvertMapStructureToConfiguration(mapStructure);
// 输出 JSON 字符串到文本文件
// 将 MapStructure 对象序列化为 JSON 字符串
// 设置格式化选项
string jsonSsring = JsonConvert.SerializeObject(configuration);
// 获取桌面路径
string desktopPath =
AppDomain.CurrentDomain
.BaseDirectory; // Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string outPath = Path.Combine(desktopPath, "output.json"); // 输出文件路径
// 输出 JSON 字符串到桌面上的文本文件
File.WriteAllText(outPath, jsonSsring);
Console.WriteLine($"JSON 数据已成功写入到 {outPath}");
//todo:分析文件内容,并导入站点、路径信息
}
}
}
public static Model.Configuration ConvertMapStructureToConfiguration(MapStructure mapStructure)
{
Model.Configuration config = new Model.Configuration();
List<int> index = new List<int>();
// 处理 Sites
foreach (var node in mapStructure.Nodes)
{
SimpleSite site = new SimpleSite
{
id = int.Parse(node.Code.Text), // 将 node.Code.Text 赋值给 SimpleSite 的 Id
name = node.Name.Text, // 将 node.Name.Text 赋值给 SimpleSite 的 Name
x = node.Base.Point.X, // 将 node.Base.Point.X 赋值给 SimpleSite 的 X
y = node.Base.Point.Y, // 将 node.Base.Point.Y 赋值给 SimpleSite 的 Y
color = "defaultColor", // 默认颜色示例,您可以根据需要更改
displaySetting = "defaultDisplay", // 默认显示设置示例,您可以根据需要更改
fields = new Dictionary<string, string>(), // Assuming fields is an empty object
mustFree = new List<object>() // Assuming mustFree is an empty array
};
config.Sites[node.Code.Text] = site; // 将 SimpleSite 添加到 Sites 字典中
AddInDescendingOrder(index, int.Parse(node.Code.Text));
}
// 处理 Tracks
foreach (var edge in mapStructure.Edges)
{
int id = index[0] + 1;
AddInDescendingOrder(index, id);
StandardScene.Model.Track track = new StandardScene.Model.Track
{
id = id, // 将 edge.Index 赋值给 Track 的 Id
name = "NoName", // 将 edge.Name.Text 赋值给 Track 的 Name
siteA = int.Parse(mapStructure.Nodes.Find(n => n.Id == edge.Data.StartNodeId).Code
.Text), // 找到 StartNode 的索引
siteB = int.Parse(mapStructure.Nodes.Find(n => n.Id == edge.Data.EndNodeId).Code
.Text), // 找到 EndNode 的索引
_siteA = int.Parse(mapStructure.Nodes.Find(n => n.Id == edge.Data.StartNodeId).Code.Text),
_siteB = int.Parse(mapStructure.Nodes.Find(n => n.Id == edge.Data.EndNodeId).Code.Text),
fields = new Dictionary<string, string>(),
typeInfo = "0",
layerName = "g",
displaySetting = ""
};
config.Tracks[id.ToString()] = track; // 将 Track 添加到 Tracks 字典中
}
return config;
}
static void AddInDescendingOrder(List<int> list, int number)
{
// 找到插入位置
int i = 0;
while (i < list.Count && list[i] >= number)
{
i++;
}
list.Insert(i, number); // 在找到的位置插入
}
}
[CADToolDescriptor(name = "批量间距生成站点")]
public class BulkIntervalSiteCreator : CADTool
{
public override async void Invoke()
{
try
{
G.pushStatus("请选择参考点");
var target = await Program.UI.getPoint();
var templateSite = SimpleLib.GetAllSites().OrderBy(p => LessMath.dist(target.x, target.y, p.x, p.y))
.FirstOrDefault();
// 检查是否找到模板站点
if (templateSite == null)
{
G.pushStatus("未找到参考站点");
return;
}
// 存储所有生成的站点(用于处理组间连接)
List<UISite> generatedSites = new List<UISite>();
if (InputBox.ShowDialog("请输入需要生成的二维码数量(必须是偶数数量)以及站点间距和延伸角度,,格式为\"6,1000,0\"。") !=
SimpleLite.DialogResult.OK) return;
generatedSites.Add((UISite)templateSite);
var values = InputBox.ResultValue.Split(',').Select(ss => float.Parse(ss)).ToArray();
// 验证数量是否为偶数
if (values[0] % 2 != 0)
{
G.pushStatus("数量必须是偶数");
return;
}
for (int i = 0; i < values[0] / 2; i = i + 2)
{
var curPos = Tuple.Create(templateSite.x, templateSite.y, values[2]);
var targetPos = LessMath.Transform2D(curPos, Tuple.Create(values[1] * (i + 1), 0f, 0f));
var targetPos2 = LessMath.Transform2D(curPos, Tuple.Create(values[1] * (i + 2), 0f, 0f));
var siteA = new UISite() { id = Prop.GenerateID(), x = targetPos.Item1, y = targetPos.Item2 };
((UISite)siteA).color = ""; //
var siteB = new UISite() { id = Prop.GenerateID(), x = targetPos2.Item1, y = targetPos2.Item2 };
SimpleLib.SetSite(siteA);
SimpleLib.SetSite(siteB);
generatedSites.Add(siteA);
generatedSites.Add(siteB);
}
for (int i = 0; i < generatedSites.Count - 1; i++)
{
Commons.AddOrUpdateSiteField(generatedSites[i], "tag", "0");
// 每两个相邻站点都创建路径(包含组内和组间)
SimpleLib.SetTrack(new UITrack(
generatedSites[i].id,
generatedSites[i + 1].id
));
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
// SyncQrMap(同步二维码地图到小车)已迁出至 StandardScene.QrLidar\Cad\SyncQrMap.csscene.qrlidar 平台)。
// StandardScene 的 CAD 工具已迁移到 SimpleLite.CADTools.StandardSceneTools。
// 保留此文件作为迁移记录,避免后续误以为遗漏了 StandardScene 侧工具。
}