refactor: 插件窗体由 static 单例状态改为实例字段(对齐 TextViewer)

将 10 个 CycleGUI 窗体/管理器从进程级 static 单例状态改为实例字段 + 实例方法,
消除本地端/Web 端共享同一可变状态的隐患(对齐 TextViewer 正例)。

每个类保留一个 private static _instance 单实例持有者;静态入口 Open()/OpenViewer()
与实例 Show() 均转发到该实例,调用方零改动、单实例“置前”行为不变。

涉及:ChargeStationManagementForm、ChargeStrategyConfigForm、CommunicationMonitorForm、
AlarmConfigManagementForm、ButtonBoxManager、DoorManager、DoorMonitor、
LoopViewer、DeliveryViewer、TrafficInterlockViewer。

构建:dotnet build StandardScene.sln --no-incremental → 0 错误,30 警告(与基线一致,无新增)。
This commit is contained in:
zhaowei.huang
2026-06-26 15:31:02 +08:00
parent a0dc1e6cd0
commit 54cda958db
10 changed files with 297 additions and 257 deletions
+23 -19
View File
@@ -28,26 +28,30 @@ namespace StandardScene.Chained
private const int OverdueMinutesThreshold = 10000; // 约 7 天视为超时
private const string TableId = "delivery-task-list";
private static readonly HttpClient SharedHttpClient = new HttpClient();
private readonly HttpClient SharedHttpClient = new HttpClient();
/// <summary>超时任务整行底色(深色主题下的暗红,醒目但不刺眼)。</summary>
private static readonly Color OverdueRowColor = Color.FromArgb(255, 90, 36, 36);
private readonly Color OverdueRowColor = Color.FromArgb(255, 90, 36, 36);
private static Panel _panel;
private static bool _showFinished = true; // 显示已完成任务
private static bool _showAbolished = true; // 显示废止任务(Error / Canceled / Terminated
private Panel _panel;
private bool _showFinished = true; // 显示已完成任务
private bool _showAbolished = true; // 显示废止任务(Error / Canceled / Terminated
// 渲染快照:由后台线程按 FlushInterval 刷新,渲染线程只读引用;锁/文件 IO 绝不放在渲染线程,避免界面卡死。
private static volatile List<Delivery> _snapshot = new List<Delivery>();
private static volatile bool _refreshing;
private static DateTime _lastFlush = DateTime.MinValue;
private static readonly TimeSpan FlushInterval = TimeSpan.FromSeconds(1);
private static volatile string _status = "";
private volatile List<Delivery> _snapshot = new List<Delivery>();
private volatile bool _refreshing;
private DateTime _lastFlush = DateTime.MinValue;
private readonly TimeSpan FlushInterval = TimeSpan.FromSeconds(1);
private volatile string _status = "";
/// <summary>打开(或置前)任务管理面板。兼容原 <c>new DeliveryViewer().Show()</c> 调用方式。</summary>
public void Show() => Open();
/// <summary>打开(或置前)任务管理面板。</summary>
public static void Open()
public static void Open() => (_instance ??= new DeliveryViewer()).OpenCore();
private static DeliveryViewer _instance;
private void OpenCore()
{
if (_panel != null)
{
@@ -127,14 +131,14 @@ namespace StandardScene.Chained
});
}
private static bool IsOverdue(Delivery dd) =>
private bool IsOverdue(Delivery dd) =>
(DateTime.Now - dd.CreateTime).TotalMinutes > OverdueMinutesThreshold;
/// <summary>
/// 渲染线程调用:到达刷新间隔且无在途刷新时,<b>在后台线程</b>重新拉取任务快照(超时任务置顶)。
/// 业务侧的锁与文件 IO 一律放到后台,渲染线程只读 <see cref="_snapshot"/> 引用,避免界面卡死。
/// </summary>
private static void EnsureSnapshotFresh()
private void EnsureSnapshotFresh()
{
if (_refreshing) return;
if (DateTime.Now - _lastFlush < FlushInterval) return;
@@ -165,14 +169,14 @@ namespace StandardScene.Chained
});
}
private static string SafeSiteName(int siteId)
private string SafeSiteName(int siteId)
{
try { return SimpleLib.GetSite(siteId)?.name ?? ""; }
catch { return ""; }
}
/// <summary>将任务标记为已取消(Canceled)。</summary>
private static void MarkDeliveryCanceled(Delivery d)
private void MarkDeliveryCanceled(Delivery d)
{
if (d == null) return;
lock (d.SyncStatus)
@@ -187,7 +191,7 @@ namespace StandardScene.Chained
/// 当 clearCarForChange=true 时,仅当状态为 Suspended 或 Waiting 且未处于放货阶段时,
/// 才会清空 UsingCar 并返回 true;否则返回 false。
/// </summary>
private static bool MarkDeliveryWaiting(Delivery d, bool clearCarForChange)
private bool MarkDeliveryWaiting(Delivery d, bool clearCarForChange)
{
if (d == null) return false;
lock (d.SyncStatus)
@@ -217,7 +221,7 @@ namespace StandardScene.Chained
}
}
private static void ResendDelivery(string taskCode)
private void ResendDelivery(string taskCode)
{
try
{
@@ -247,7 +251,7 @@ namespace StandardScene.Chained
}
}
private static void CancelDelivery(string taskCode)
private void CancelDelivery(string taskCode)
{
try
{
@@ -286,7 +290,7 @@ namespace StandardScene.Chained
}
}
private static void ChangeCarResendDelivery(string taskCode)
private void ChangeCarResendDelivery(string taskCode)
{
try
{
+22 -18
View File
@@ -27,24 +27,28 @@ namespace LoopViewerApp
private const string TableId = "loop-task-list";
// 与 AbstractLoopMission 完全一致的读取路径,保证“写哪儿、它就读哪儿”。
private static string JsonPath => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "tasklist.json");
private string JsonPath => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "tasklist.json");
private static readonly object SaveLock = new object();
private readonly object SaveLock = new object();
// 直接取自枚举,自动与 TaskKind / TaskStartType 保持同步(含 Charge),无需手写列表。
private static readonly string[] KindNames = Enum.GetNames(typeof(TaskKind));
private static readonly string[] StartTypeNames = Enum.GetNames(typeof(TaskStartType));
private readonly string[] KindNames = Enum.GetNames(typeof(TaskKind));
private readonly string[] StartTypeNames = Enum.GetNames(typeof(TaskStartType));
private static Panel _panel;
private static Panel _dialog; // 新增/编辑对话框,限单实例
private static List<LoopTask> _tasks = new List<LoopTask>(); // 仅渲染线程读写
private static readonly HashSet<int> _selected = new HashSet<int>(); // 仅渲染线程读写,存被勾选任务的 Id
private static volatile string _status = "";
private Panel _panel;
private Panel _dialog; // 新增/编辑对话框,限单实例
private List<LoopTask> _tasks = new List<LoopTask>(); // 仅渲染线程读写
private readonly HashSet<int> _selected = new HashSet<int>(); // 仅渲染线程读写,存被勾选任务的 Id
private volatile string _status = "";
/// <summary>打开(或置前)任务管理面板。兼容原 <c>new LoopViewer().Show()</c> 调用方式。</summary>
public void Show() => Open();
/// <summary>打开(或置前)任务管理面板。</summary>
public static void Open()
public static void Open() => (_instance ??= new LoopViewer()).OpenCore();
private static LoopViewer _instance;
private void OpenCore()
{
if (_panel != null)
{
@@ -126,7 +130,7 @@ namespace LoopViewerApp
}
/// <summary>对选中项发起二次确认后删除(保留原多选删除的提示文案)。</summary>
private static void ConfirmDeleteSelected()
private void ConfirmDeleteSelected()
{
if (_selected.Count == 0)
{
@@ -145,7 +149,7 @@ namespace LoopViewerApp
CycleUiHelper.ConfirmThen(prompt, DeleteSelected);
}
private static void DeleteSelected()
private void DeleteSelected()
{
var removed = _tasks.RemoveAll(t => _selected.Contains(t.Id));
_selected.Clear();
@@ -158,7 +162,7 @@ namespace LoopViewerApp
/// 打开「新增 / 编辑」对话框(置顶非模态、限单实例)。<paramref name="existing"/> 为 null 表示新增,否则编辑该任务(保留其 Id)。
/// 每次打开都是全新面板:<c>defaultText</c> 能正确初始化,规避立即模式下文本框缓冲难以重置的问题。
/// </summary>
private static void OpenEditDialog(LoopTask existing)
private void OpenEditDialog(LoopTask existing)
{
if (_dialog != null)
{
@@ -270,9 +274,9 @@ namespace LoopViewerApp
}
/// <summary>下一个可用任务 Id(当前最大 Id + 1,空表则为 1)。</summary>
private static int GetNextTaskId() => _tasks.Count == 0 ? 1 : _tasks.Max(t => t.Id) + 1;
private int GetNextTaskId() => _tasks.Count == 0 ? 1 : _tasks.Max(t => t.Id) + 1;
private static void LoadTasks()
private void LoadTasks()
{
try
{
@@ -292,7 +296,7 @@ namespace LoopViewerApp
}
/// <summary>序列化在渲染线程完成(极快),文件写入放后台线程,避免阻塞渲染线程。</summary>
private static void SaveTasks()
private void SaveTasks()
{
string json;
try
@@ -323,9 +327,9 @@ namespace LoopViewerApp
});
}
private static int Clamp(int v, int min, int max) => v < min ? min : (v > max ? max : v);
private int Clamp(int v, int min, int max) => v < min ? min : (v > max ? max : v);
private static bool TryParseClamp(string s, int min, int max, out int value)
private bool TryParseClamp(string s, int min, int max, out int value)
{
if (int.TryParse((s ?? "").Trim(), out value))
{
@@ -14,28 +14,32 @@ namespace StandardScene.Charge
{
private const string TableId = "alarm-config-list";
private static readonly string[] LevelNames = { "无", "低", "中", "高", "严重" };
private static readonly string[] LevelFilterNames = { "全部", "无", "低", "中", "高", "严重" };
private readonly string[] LevelNames = { "无", "低", "中", "高", "严重" };
private readonly string[] LevelFilterNames = { "全部", "无", "低", "中", "高", "严重" };
private static readonly Color CriticalRowColor = Color.FromArgb(255, 235, 238);
private static readonly Color HighRowColor = Color.FromArgb(255, 243, 224);
private static readonly Color MediumRowColor = Color.FromArgb(255, 249, 196);
private static readonly Color LowRowColor = Color.FromArgb(232, 245, 233);
private static readonly Color DisabledRowColor = Color.FromArgb(238, 238, 238);
private readonly Color CriticalRowColor = Color.FromArgb(255, 235, 238);
private readonly Color HighRowColor = Color.FromArgb(255, 243, 224);
private readonly Color MediumRowColor = Color.FromArgb(255, 249, 196);
private readonly Color LowRowColor = Color.FromArgb(232, 245, 233);
private readonly Color DisabledRowColor = Color.FromArgb(238, 238, 238);
private static readonly AlarmConfigDataService DataService = AlarmConfigDataService.Instance;
private readonly AlarmConfigDataService DataService = AlarmConfigDataService.Instance;
private static Panel _panel;
private static Panel _dialog;
private static List<AlarmConfig> _allAlarms = new List<AlarmConfig>();
private static int _levelFilterIdx;
private static string _status = "";
private Panel _panel;
private Panel _dialog;
private List<AlarmConfig> _allAlarms = new List<AlarmConfig>();
private int _levelFilterIdx;
private string _status = "";
/// <summary>打开(或置前)报警配置管理面板。兼容原 <c>new AlarmConfigManagementForm().Show()</c> 调用方式。</summary>
public void Show() => Open();
/// <summary>打开(或置前)报警配置管理面板。</summary>
public static void Open()
public static void Open() => (_instance ??= new AlarmConfigManagementForm()).OpenCore();
private static AlarmConfigManagementForm _instance;
private void OpenCore()
{
if (_panel != null)
{
@@ -132,7 +136,7 @@ namespace StandardScene.Charge
});
}
private static void OpenEditDialog(AlarmConfig existing)
private void OpenEditDialog(AlarmConfig existing)
{
if (_dialog != null)
{
@@ -276,7 +280,7 @@ namespace StandardScene.Charge
});
}
private static void LoadAlarms()
private void LoadAlarms()
{
try
{
@@ -289,7 +293,7 @@ namespace StandardScene.Charge
}
}
private static List<AlarmConfig> GetFilteredAlarms()
private List<AlarmConfig> GetFilteredAlarms()
{
IEnumerable<AlarmConfig> query = _allAlarms;
if (_levelFilterIdx > 0)
@@ -297,7 +301,7 @@ namespace StandardScene.Charge
return query.ToList();
}
private static string GetStatisticsText()
private string GetStatisticsText()
{
var total = _allAlarms.Count;
var enabled = _allAlarms.Count(a => a.Enabled);
@@ -307,7 +311,7 @@ namespace StandardScene.Charge
return $"启用: {enabled} | 禁用: {disabled} | 严重: {critical} | 高级: {high}";
}
private static string GetLevelText(AlarmLevel level)
private string GetLevelText(AlarmLevel level)
{
switch (level)
{
@@ -18,29 +18,33 @@ namespace StandardScene.Charge
public class ChargeStationManagementForm
{
private const string TableId = "charge-station-list";
private static readonly string[] StatusFilterNames = { "全部状态", "空闲", "充电中", "故障", "离线" };
private static readonly string[] TypeNames = { "FRLD高款充电桩", "FRLD矮款充电桩", "牧星充电桩" };
private static readonly string[] MethodNames = { "地充", "尾充", "侧充" };
private static readonly string[] CarTypeNames = { "FRLD充电", "牧星充电桩充电" };
private readonly string[] StatusFilterNames = { "全部状态", "空闲", "充电中", "故障", "离线" };
private readonly string[] TypeNames = { "FRLD高款充电桩", "FRLD矮款充电桩", "牧星充电桩" };
private readonly string[] MethodNames = { "地充", "尾充", "侧充" };
private readonly string[] CarTypeNames = { "FRLD充电", "牧星充电桩充电" };
private static readonly ChargeStationDataService DataService = ChargeStationDataService.Instance;
private static readonly Ping Ping = new Ping();
private readonly ChargeStationDataService DataService = ChargeStationDataService.Instance;
private readonly Ping Ping = new Ping();
private static Panel _panel;
private static Panel _editDialog;
private static List<ChargeStation> _snapshot = new List<ChargeStation>();
private static volatile bool _refreshing;
private static DateTime _lastFlush = DateTime.MinValue;
private static readonly TimeSpan FlushInterval = TimeSpan.FromSeconds(3);
private Panel _panel;
private Panel _editDialog;
private List<ChargeStation> _snapshot = new List<ChargeStation>();
private volatile bool _refreshing;
private DateTime _lastFlush = DateTime.MinValue;
private readonly TimeSpan FlushInterval = TimeSpan.FromSeconds(3);
private static int _statusFilterIdx;
private static string _searchText = "";
private static string _statsText = "";
private static string _status = "";
private int _statusFilterIdx;
private string _searchText = "";
private string _statsText = "";
private string _status = "";
public void Show() => Open();
public static void Open()
public static void Open() => (_instance ??= new ChargeStationManagementForm()).OpenCore();
private static ChargeStationManagementForm _instance;
private void OpenCore()
{
if (_panel != null)
{
@@ -137,7 +141,7 @@ namespace StandardScene.Charge
});
}
private static void EnsureSnapshotFresh()
private void EnsureSnapshotFresh()
{
if (_refreshing) return;
if (DateTime.Now - _lastFlush < FlushInterval) return;
@@ -164,7 +168,7 @@ namespace StandardScene.Charge
});
}
private static List<ChargeStation> FilterSnapshot(List<ChargeStation> src)
private List<ChargeStation> FilterSnapshot(List<ChargeStation> src)
{
IEnumerable<ChargeStation> q = src;
if (_statusFilterIdx > 0)
@@ -183,7 +187,7 @@ namespace StandardScene.Charge
return q.ToList();
}
private static void UpdateStats(List<ChargeStation> all)
private void UpdateStats(List<ChargeStation> all)
{
_statsText = $"总数: {all.Count} | 空闲: {all.Count(s => s.Status == ChargeStationStatus.Idle)} | " +
$"充电中: {all.Count(s => s.Status == ChargeStationStatus.Charging)} | " +
@@ -191,7 +195,7 @@ namespace StandardScene.Charge
$"AGV电池已接入: {all.Count(s => s.Status == ChargeStationStatus.Battery)}";
}
private static void OpenEditDialog(ChargeStation existing)
private void OpenEditDialog(ChargeStation existing)
{
if (_editDialog != null)
{
@@ -296,7 +300,7 @@ namespace StandardScene.Charge
});
}
private static bool TryBuildStation(bool isAdd, ChargeStation existing, string stationId, string name,
private bool TryBuildStation(bool isAdd, ChargeStation existing, string stationId, string name,
int typeIdx, int methodIdx, int carTypeIdx, string ip, string port, string voltage, string current,
string siteIdText, string remarks, bool enabled, bool shield,
out ChargeStation station, out string err)
@@ -335,7 +339,7 @@ namespace StandardScene.Charge
return true;
}
private static void ConfirmDelete(ChargeStation station)
private void ConfirmDelete(ChargeStation station)
{
CycleUiHelper.ConfirmThen($"确定删除充电桩 [{station.StationId}] {station.Name}", () =>
{
@@ -359,7 +363,7 @@ namespace StandardScene.Charge
});
}
private static void ExportData(PanelBuilder pb)
private void ExportData(PanelBuilder pb)
{
if (!pb.SaveFile("导出充电桩", "*.json;*.csv", out var path) || string.IsNullOrEmpty(path)) return;
try
@@ -385,7 +389,7 @@ namespace StandardScene.Charge
catch (Exception ex) { CycleUiHelper.Alert("错误", $"导出失败: {ex.Message}"); }
}
private static void ApplyRowColor(PanelBuilder.Row row, ChargeStation s)
private void ApplyRowColor(PanelBuilder.Row row, ChargeStation s)
{
if (s.HasAlarm) row.SetColor(Color.FromArgb(255, 205, 210));
else if (s.Status == ChargeStationStatus.Idle) row.SetColor(Color.FromArgb(232, 245, 233));
@@ -394,7 +398,7 @@ namespace StandardScene.Charge
else if (s.Status == ChargeStationStatus.Battery) row.SetColor(Color.FromArgb(238, 238, 238));
}
private static ChargeStationStatus GetStatusFromFilterIndex(int idx) => idx switch
private ChargeStationStatus GetStatusFromFilterIndex(int idx) => idx switch
{
1 => ChargeStationStatus.Idle,
2 => ChargeStationStatus.Charging,
@@ -403,7 +407,7 @@ namespace StandardScene.Charge
_ => ChargeStationStatus.Idle
};
private static string GetStatusText(ChargeStationStatus status) => status switch
private string GetStatusText(ChargeStationStatus status) => status switch
{
ChargeStationStatus.Idle => "空闲",
ChargeStationStatus.Charging => "充电中",
@@ -412,7 +416,7 @@ namespace StandardScene.Charge
_ => "未知"
};
private static string GetTypeText(ChargeStationType type) => type switch
private string GetTypeText(ChargeStationType type) => type switch
{
ChargeStationType.FRLDTall => "FRLD高款充电桩",
ChargeStationType.FRLDShort => "FRLD矮款充电桩",
@@ -420,7 +424,7 @@ namespace StandardScene.Charge
_ => "未知"
};
private static string GetMethodText(ChargeMethodType m) => m switch
private string GetMethodText(ChargeMethodType m) => m switch
{
ChargeMethodType.Ground => "地充",
ChargeMethodType.Rear => "尾充",
@@ -428,7 +432,7 @@ namespace StandardScene.Charge
_ => "未知"
};
private static string FormatComm(CommunicationStatus s) => s switch
private string FormatComm(CommunicationStatus s) => s switch
{
CommunicationStatus.Normal => "✓ 正常",
CommunicationStatus.Delayed => "⚠ 延迟",
@@ -438,7 +442,7 @@ namespace StandardScene.Charge
_ => "? 未知"
};
private static string FormatMech(MechanismStatus s) => s switch
private string FormatMech(MechanismStatus s) => s switch
{
MechanismStatus.Extended => "◆ 伸出",
MechanismStatus.Retracted => "◇ 缩回",
@@ -446,7 +450,7 @@ namespace StandardScene.Charge
_ => "? 未知"
};
private static string FormatAlarm(ChargeStation s)
private string FormatAlarm(ChargeStation s)
{
if (!s.HasAlarm) return "正常";
return string.IsNullOrWhiteSpace(s.AlarmMessage) ? $"【{s.AlarmLevel}】" : s.AlarmMessage;
@@ -9,39 +9,43 @@ namespace StandardScene.Charge
/// </summary>
public class ChargeStrategyConfigForm
{
private static Panel _panel;
private static readonly ChargeStrategyConfigService ConfigService = ChargeStrategyConfigService.Instance;
private Panel _panel;
private readonly ChargeStrategyConfigService ConfigService = ChargeStrategyConfigService.Instance;
private static ChargeStrategyConfig _config;
private static string _status = "";
private ChargeStrategyConfig _config;
private string _status = "";
// SOC 参数
private static float _mustChargeSoc;
private static float _idleChargeSoc;
private static float _taskAvailableSoc;
private static float _fullChargeSoc;
private static float _allowInterruptSoc;
private float _mustChargeSoc;
private float _idleChargeSoc;
private float _taskAvailableSoc;
private float _fullChargeSoc;
private float _allowInterruptSoc;
// 时间参数
private static float _idleChargeSeconds;
private static float _idleSeconds;
private static float _mustChargeSeconds;
private static float _topUpMinutes;
private float _idleChargeSeconds;
private float _idleSeconds;
private float _mustChargeSeconds;
private float _topUpMinutes;
// 任务参数
private static int _minAllowFreeCarToChargeTaskCnt;
private int _minAllowFreeCarToChargeTaskCnt;
// 开关参数
private static bool _allowInterruptTask;
private static bool _useLowerSocForCharge;
private static bool _enableErrorChargeDetection;
private static bool _useChargeSiteFilter;
private bool _allowInterruptTask;
private bool _useLowerSocForCharge;
private bool _enableErrorChargeDetection;
private bool _useChargeSiteFilter;
/// <summary>打开(或置前)充电策略配置面板。兼容原 <c>new ChargeStrategyConfigForm().Show()</c> 调用方式。</summary>
public void Show() => Open();
/// <summary>打开(或置前)充电策略配置面板。</summary>
public static void Open()
public static void Open() => (_instance ??= new ChargeStrategyConfigForm()).OpenCore();
private static ChargeStrategyConfigForm _instance;
private void OpenCore()
{
if (_panel != null)
{
@@ -120,7 +124,7 @@ namespace StandardScene.Charge
});
}
private static bool TryLoadConfig()
private bool TryLoadConfig()
{
try
{
@@ -137,7 +141,7 @@ namespace StandardScene.Charge
}
}
private static void ApplyConfigToUi(ChargeStrategyConfig config)
private void ApplyConfigToUi(ChargeStrategyConfig config)
{
_mustChargeSoc = (float)config.MustChargeSoc;
_idleChargeSoc = (float)config.IdleChargeSoc;
@@ -158,7 +162,7 @@ namespace StandardScene.Charge
_useChargeSiteFilter = config.UseChargeSiteFilter;
}
private static void ApplyUiToConfig()
private void ApplyUiToConfig()
{
_config.MustChargeSoc = _mustChargeSoc;
_config.IdleChargeSoc = _idleChargeSoc;
@@ -180,7 +184,7 @@ namespace StandardScene.Charge
}
/// <summary>验证 SOC 阈值之间的逻辑关系(保存前)。</summary>
private static bool ValidateSocRanges(out string errorMessage)
private bool ValidateSocRanges(out string errorMessage)
{
if (_mustChargeSoc >= _idleChargeSoc)
{
@@ -210,7 +214,7 @@ namespace StandardScene.Charge
return true;
}
private static void TrySave(bool closeAfterSave)
private void TrySave(bool closeAfterSave)
{
ApplyUiToConfig();
@@ -245,7 +249,7 @@ namespace StandardScene.Charge
}
}
private static void RestoreDefaults()
private void RestoreDefaults()
{
CycleUiHelper.ConfirmThen("确定要恢复默认配置吗?当前配置将被覆盖。", () =>
{
@@ -24,33 +24,37 @@ namespace StandardScene.Charge
private const int StatsRefreshMs = 500;
private const string TableId = "comm-monitor-msgs";
private static readonly Color SendRowColor = Color.FromArgb(232, 245, 233);
private static readonly Color ReceiveRowColor = Color.FromArgb(227, 242, 253);
private static readonly Color SelectedRowColor = Color.FromArgb(255, 249, 196);
private readonly Color SendRowColor = Color.FromArgb(232, 245, 233);
private readonly Color ReceiveRowColor = Color.FromArgb(227, 242, 253);
private readonly Color SelectedRowColor = Color.FromArgb(255, 249, 196);
private static readonly CommunicationMessageService MessageService = CommunicationMessageService.Instance;
private readonly CommunicationMessageService MessageService = CommunicationMessageService.Instance;
private static Panel _panel;
private static bool _subscribed;
private static bool _paused;
private static int _selectedIpIndex;
private static string[] _ipOptions = { "全部" };
private static int _selectedRowIndex = -1;
private static string _parsedText = "";
private static string _statsText = "";
private Panel _panel;
private bool _subscribed;
private bool _paused;
private int _selectedIpIndex;
private string[] _ipOptions = { "全部" };
private int _selectedRowIndex = -1;
private string _parsedText = "";
private string _statsText = "";
private static List<CommunicationMessage> _displayMessages = new List<CommunicationMessage>();
private static readonly Queue<CommunicationMessage> PendingMessages = new Queue<CommunicationMessage>();
private static readonly object PendingLock = new object();
private List<CommunicationMessage> _displayMessages = new List<CommunicationMessage>();
private readonly Queue<CommunicationMessage> PendingMessages = new Queue<CommunicationMessage>();
private readonly object PendingLock = new object();
private static DateTime _lastStatsRefresh = DateTime.MinValue;
private static bool _pendingStatsRefresh;
private DateTime _lastStatsRefresh = DateTime.MinValue;
private bool _pendingStatsRefresh;
/// <summary>打开(或置前)通讯监控面板。兼容原 <c>new CommunicationMonitorForm().Show()</c> 调用方式。</summary>
public void Show() => Open();
/// <summary>打开(或置前)通讯监控面板。</summary>
public static void Open()
public static void Open() => (_instance ??= new CommunicationMonitorForm()).OpenCore();
private static CommunicationMonitorForm _instance;
private void OpenCore()
{
if (_panel != null)
{
@@ -169,7 +173,7 @@ namespace StandardScene.Charge
});
}
private static void Subscribe()
private void Subscribe()
{
if (_subscribed)
return;
@@ -177,7 +181,7 @@ namespace StandardScene.Charge
_subscribed = true;
}
private static void Unsubscribe()
private void Unsubscribe()
{
if (!_subscribed)
return;
@@ -187,7 +191,7 @@ namespace StandardScene.Charge
PendingMessages.Clear();
}
private static void OnMessageAdded(object sender, CommunicationMessage message)
private void OnMessageAdded(object sender, CommunicationMessage message)
{
if (!_subscribed || message == null)
return;
@@ -199,7 +203,7 @@ namespace StandardScene.Charge
}
/// <summary>定时批量刷新 UI,避免每条报文都抢占渲染线程。</summary>
private static void FlushPendingBatch()
private void FlushPendingBatch()
{
if (_paused)
return;
@@ -236,7 +240,7 @@ namespace StandardScene.Charge
RequestStatisticsRefresh();
}
private static void InsertMessageAtTop(CommunicationMessage msg)
private void InsertMessageAtTop(CommunicationMessage msg)
{
_displayMessages.Insert(0, msg);
while (_displayMessages.Count > MaxDisplayRows)
@@ -246,7 +250,7 @@ namespace StandardScene.Charge
_selectedRowIndex++;
}
private static void ReloadFromService()
private void ReloadFromService()
{
try
{
@@ -267,7 +271,7 @@ namespace StandardScene.Charge
}
}
private static void RefreshIpFilter()
private void RefreshIpFilter()
{
try
{
@@ -302,7 +306,7 @@ namespace StandardScene.Charge
}
}
private static void EnsureIpInFilter(string ipAddress)
private void EnsureIpInFilter(string ipAddress)
{
if (string.IsNullOrWhiteSpace(ipAddress))
return;
@@ -315,7 +319,7 @@ namespace StandardScene.Charge
_ipOptions = list.ToArray();
}
private static string SelectedIpFilter()
private string SelectedIpFilter()
{
if (_ipOptions == null || _ipOptions.Length == 0)
return "全部";
@@ -324,13 +328,13 @@ namespace StandardScene.Charge
return _ipOptions[_selectedIpIndex];
}
private static void RequestStatisticsRefresh()
private void RequestStatisticsRefresh()
{
_pendingStatsRefresh = true;
}
/// <summary>统计信息低频刷新(500ms)。</summary>
private static void MaybeRefreshStatistics()
private void MaybeRefreshStatistics()
{
if (!_pendingStatsRefresh)
return;
@@ -342,7 +346,7 @@ namespace StandardScene.Charge
UpdateStatistics(_displayMessages.Count);
}
private static void UpdateStatistics(int displayCount)
private void UpdateStatistics(int displayCount)
{
try
{
@@ -364,14 +368,14 @@ namespace StandardScene.Charge
}
}
private static string TruncateRawData(string rawData, int maxLen = 48)
private string TruncateRawData(string rawData, int maxLen = 48)
{
if (string.IsNullOrEmpty(rawData))
return "";
return rawData.Length <= maxLen ? rawData : rawData.Substring(0, maxLen) + "…";
}
private static string BuildParsedText(CommunicationMessage message)
private string BuildParsedText(CommunicationMessage message)
{
if (message == null)
return "";
@@ -17,32 +17,36 @@ namespace StandardScene.ExtendDevice.ButtonBox
public class ButtonBoxManager
{
private const string DataFileName = "ButtonBoxConfig.json";
private static string DataFilePath => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DataFileName);
private static readonly object SaveLock = new object();
private string DataFilePath => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DataFileName);
private readonly object SaveLock = new object();
private static Panel _panel;
private static List<ButtonBoxModel> _boxes = new List<ButtonBoxModel>();
private static int _selectedBoxIdx = -1;
private static int _selectedBtnIdx = -1;
private static string _status = "";
private Panel _panel;
private List<ButtonBoxModel> _boxes = new List<ButtonBoxModel>();
private int _selectedBoxIdx = -1;
private int _selectedBtnIdx = -1;
private string _status = "";
private static string _boxIp = "";
private static string _boxPort = "502";
private static string _boxIndex = "";
private static int _typeIdx;
private static string[] _typeNames = Array.Empty<string>();
private string _boxIp = "";
private string _boxPort = "502";
private string _boxIndex = "";
private int _typeIdx;
private string[] _typeNames = Array.Empty<string>();
private static string _btnIndex = "";
private static string _triggerMission = "";
private static string _triggerMethod = "";
private static string _triggerParams = "";
private static string _triggerDelay = "0";
private static int _triggerStateIdx;
private static readonly string[] _triggerStateNames = Enum.GetNames(typeof(ButtonState));
private string _btnIndex = "";
private string _triggerMission = "";
private string _triggerMethod = "";
private string _triggerParams = "";
private string _triggerDelay = "0";
private int _triggerStateIdx;
private readonly string[] _triggerStateNames = Enum.GetNames(typeof(ButtonState));
private static ButtonBoxManager _instance;
public static void OpenViewer() => Open();
public static void Open()
public static void Open() => (_instance ??= new ButtonBoxManager()).OpenCore();
private void OpenCore()
{
if (_panel != null)
{
@@ -155,17 +159,17 @@ namespace StandardScene.ExtendDevice.ButtonBox
});
}
private static ButtonBoxModel GetSelectedBox() =>
private ButtonBoxModel GetSelectedBox() =>
_selectedBoxIdx >= 0 && _selectedBoxIdx < _boxes.Count ? _boxes[_selectedBoxIdx] : null;
private static ButtonModel GetSelectedButton()
private ButtonModel GetSelectedButton()
{
var box = GetSelectedBox();
return box != null && _selectedBtnIdx >= 0 && _selectedBtnIdx < box.Buttons.Count
? box.Buttons[_selectedBtnIdx] : null;
}
private static string[] DiscoverTypes()
private string[] DiscoverTypes()
{
try
{
@@ -184,7 +188,7 @@ namespace StandardScene.ExtendDevice.ButtonBox
}
}
private static void LoadData()
private void LoadData()
{
try
{
@@ -201,7 +205,7 @@ namespace StandardScene.ExtendDevice.ButtonBox
}
}
private static void SaveData()
private void SaveData()
{
var json = _boxes.ToJson();
var path = DataFilePath;
@@ -220,7 +224,7 @@ namespace StandardScene.ExtendDevice.ButtonBox
});
}
private static void LoadBoxFields(ButtonBoxModel b)
private void LoadBoxFields(ButtonBoxModel b)
{
_boxIp = b.Ip;
_boxPort = b.Port.ToString();
@@ -229,7 +233,7 @@ namespace StandardScene.ExtendDevice.ButtonBox
if (_typeIdx < 0) _typeIdx = 0;
}
private static void ClearBoxFields()
private void ClearBoxFields()
{
_boxIp = "";
_boxPort = "502";
@@ -237,7 +241,7 @@ namespace StandardScene.ExtendDevice.ButtonBox
_typeIdx = 0;
}
private static void LoadButtonFields(ButtonModel b)
private void LoadButtonFields(ButtonModel b)
{
_btnIndex = b.Index.ToString();
_triggerMission = b.TriggerMission ?? "";
@@ -247,7 +251,7 @@ namespace StandardScene.ExtendDevice.ButtonBox
_triggerStateIdx = Math.Max(0, Array.IndexOf(_triggerStateNames, b.TriggerState));
}
private static void ClearButtonFields()
private void ClearButtonFields()
{
_btnIndex = "";
_triggerMission = "";
@@ -257,7 +261,7 @@ namespace StandardScene.ExtendDevice.ButtonBox
_triggerStateIdx = 0;
}
private static void AddBox()
private void AddBox()
{
if (!TryParseBoxInput(out var index, out var ip, out var port, out var type, false, out var err))
{ CycleUiHelper.Alert("错误", err); return; }
@@ -272,7 +276,7 @@ namespace StandardScene.ExtendDevice.ButtonBox
_panel?.Repaint();
}
private static void SaveBox()
private void SaveBox()
{
var cur = GetSelectedBox();
if (cur == null) { CycleUiHelper.Alert("提示", "请先选择按钮盒"); return; }
@@ -287,7 +291,7 @@ namespace StandardScene.ExtendDevice.ButtonBox
_panel?.Repaint();
}
private static void ConfirmDeleteBox()
private void ConfirmDeleteBox()
{
var cur = GetSelectedBox();
if (cur == null) { CycleUiHelper.Alert("提示", "请选择要删除的按钮盒"); return; }
@@ -304,7 +308,7 @@ namespace StandardScene.ExtendDevice.ButtonBox
});
}
private static void AddButton()
private void AddButton()
{
var box = GetSelectedBox();
if (box == null) { CycleUiHelper.Alert("提示", "请先选择按钮盒"); return; }
@@ -327,7 +331,7 @@ namespace StandardScene.ExtendDevice.ButtonBox
_panel?.Repaint();
}
private static void SaveButton()
private void SaveButton()
{
var box = GetSelectedBox();
var btn = GetSelectedButton();
@@ -347,7 +351,7 @@ namespace StandardScene.ExtendDevice.ButtonBox
_panel?.Repaint();
}
private static void ConfirmDeleteButton()
private void ConfirmDeleteButton()
{
var box = GetSelectedBox();
var btn = GetSelectedButton();
@@ -363,7 +367,7 @@ namespace StandardScene.ExtendDevice.ButtonBox
});
}
private static bool TryParseBoxInput(out int index, out string ip, out int port, out string type, bool requireSelection, out string err)
private bool TryParseBoxInput(out int index, out string ip, out int port, out string type, bool requireSelection, out string err)
{
index = 0; ip = ""; port = 502; type = ""; err = "";
ip = string.IsNullOrWhiteSpace(_boxIp) ? "192.168.1.100" : _boxIp.Trim();
@@ -379,7 +383,7 @@ namespace StandardScene.ExtendDevice.ButtonBox
return true;
}
private static bool TryParseButtonInput(out int index, out ushort delay, out string err)
private bool TryParseButtonInput(out int index, out ushort delay, out string err)
{
index = 0; delay = 0; err = "";
var box = GetSelectedBox();
@@ -393,7 +397,7 @@ namespace StandardScene.ExtendDevice.ButtonBox
return true;
}
private static bool IsValidIp(string ip)
private bool IsValidIp(string ip)
{
if (string.IsNullOrWhiteSpace(ip)) return false;
var pattern = @"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
@@ -18,29 +18,33 @@ namespace StandardScene.ExtendDevice.Door
public class DoorManager
{
private const string DataFileName = "DoorConfig.json";
private static string DataFilePath => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DataFileName);
private static readonly object SaveLock = new object();
private string DataFilePath => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DataFileName);
private readonly object SaveLock = new object();
private static Panel _panel;
private static List<DoorControllerModel> _controllers = new List<DoorControllerModel>();
private static int _selectedCtrlIdx = -1;
private static int _selectedDoorIdx = -1;
private static string _status = "";
private Panel _panel;
private List<DoorControllerModel> _controllers = new List<DoorControllerModel>();
private int _selectedCtrlIdx = -1;
private int _selectedDoorIdx = -1;
private string _status = "";
private static string _ctrlIp = "";
private static string _ctrlPort = "502";
private static string _ctrlIndex = "";
private static int _typeIdx;
private static string[] _typeNames = Array.Empty<string>();
private string _ctrlIp = "";
private string _ctrlPort = "502";
private string _ctrlIndex = "";
private int _typeIdx;
private string[] _typeNames = Array.Empty<string>();
private static string _doorIndex = "";
private static string _doorCtrlAddr = "";
private static string _doorOpenAddr = "";
private static bool _doorNoControl;
private string _doorIndex = "";
private string _doorCtrlAddr = "";
private string _doorOpenAddr = "";
private bool _doorNoControl;
public static void OpenViewer() => Open();
public static void Open()
public static void Open() => (_instance ??= new DoorManager()).OpenCore();
private static DoorManager _instance;
private void OpenCore()
{
if (_panel != null)
{
@@ -148,17 +152,17 @@ namespace StandardScene.ExtendDevice.Door
});
}
private static DoorControllerModel GetSelectedController() =>
private DoorControllerModel GetSelectedController() =>
_selectedCtrlIdx >= 0 && _selectedCtrlIdx < _controllers.Count ? _controllers[_selectedCtrlIdx] : null;
private static DoorModel GetSelectedDoor()
private DoorModel GetSelectedDoor()
{
var ctrl = GetSelectedController();
return ctrl != null && _selectedDoorIdx >= 0 && _selectedDoorIdx < ctrl.Doors.Count
? ctrl.Doors[_selectedDoorIdx] : null;
}
private static string[] DiscoverTypes()
private string[] DiscoverTypes()
{
try
{
@@ -179,7 +183,7 @@ namespace StandardScene.ExtendDevice.Door
}
}
private static void LoadData()
private void LoadData()
{
try
{
@@ -196,7 +200,7 @@ namespace StandardScene.ExtendDevice.Door
}
}
private static void SaveData()
private void SaveData()
{
var json = _controllers.ToJson();
var path = DataFilePath;
@@ -215,7 +219,7 @@ namespace StandardScene.ExtendDevice.Door
});
}
private static void LoadControllerFields(DoorControllerModel c)
private void LoadControllerFields(DoorControllerModel c)
{
_ctrlIp = c.Ip;
_ctrlPort = c.Port.ToString();
@@ -224,7 +228,7 @@ namespace StandardScene.ExtendDevice.Door
if (_typeIdx < 0) _typeIdx = 0;
}
private static void ClearControllerFields()
private void ClearControllerFields()
{
_ctrlIp = "";
_ctrlPort = "502";
@@ -232,7 +236,7 @@ namespace StandardScene.ExtendDevice.Door
_typeIdx = 0;
}
private static void LoadDoorFields(DoorModel d)
private void LoadDoorFields(DoorModel d)
{
_doorIndex = d.Index.ToString();
_doorCtrlAddr = d.ControlAddress.ToString();
@@ -240,7 +244,7 @@ namespace StandardScene.ExtendDevice.Door
_doorNoControl = d.NoControl;
}
private static void ClearDoorFields()
private void ClearDoorFields()
{
_doorIndex = "";
_doorCtrlAddr = "";
@@ -248,7 +252,7 @@ namespace StandardScene.ExtendDevice.Door
_doorNoControl = false;
}
private static void AddController()
private void AddController()
{
if (!TryParseControllerInput(out var index, out var ip, out var port, out var type, false, out var err))
{
@@ -266,7 +270,7 @@ namespace StandardScene.ExtendDevice.Door
_panel?.Repaint();
}
private static void SaveController()
private void SaveController()
{
var cur = GetSelectedController();
if (cur == null) { CycleUiHelper.Alert("提示", "请先选择要保存的门控制器"); return; }
@@ -288,7 +292,7 @@ namespace StandardScene.ExtendDevice.Door
_panel?.Repaint();
}
private static void ConfirmDeleteController()
private void ConfirmDeleteController()
{
var cur = GetSelectedController();
if (cur == null) { CycleUiHelper.Alert("提示", "请选择要删除的门控制器"); return; }
@@ -305,7 +309,7 @@ namespace StandardScene.ExtendDevice.Door
});
}
private static void AddDoor()
private void AddDoor()
{
var ctrl = GetSelectedController();
if (ctrl == null) { CycleUiHelper.Alert("提示", "请先选择门控制器"); return; }
@@ -330,7 +334,7 @@ namespace StandardScene.ExtendDevice.Door
_panel?.Repaint();
}
private static void SaveDoor()
private void SaveDoor()
{
var ctrl = GetSelectedController();
var door = GetSelectedDoor();
@@ -352,7 +356,7 @@ namespace StandardScene.ExtendDevice.Door
_panel?.Repaint();
}
private static void ConfirmDeleteDoor()
private void ConfirmDeleteDoor()
{
var ctrl = GetSelectedController();
var door = GetSelectedDoor();
@@ -368,7 +372,7 @@ namespace StandardScene.ExtendDevice.Door
});
}
private static bool TryParseControllerInput(out int index, out string ip, out int port, out string type, bool requireSelection, out string err)
private bool TryParseControllerInput(out int index, out string ip, out int port, out string type, bool requireSelection, out string err)
{
index = 0; ip = ""; port = 502; type = ""; err = "";
ip = string.IsNullOrWhiteSpace(_ctrlIp) ? "192.168.1.100" : _ctrlIp.Trim();
@@ -384,7 +388,7 @@ namespace StandardScene.ExtendDevice.Door
return true;
}
private static bool TryParseDoorInput(out int index, out ushort cAddr, out ushort oAddr, bool requireSelection, out string err)
private bool TryParseDoorInput(out int index, out ushort cAddr, out ushort oAddr, bool requireSelection, out string err)
{
index = 0; cAddr = 0; oAddr = 0; err = "";
var ctrl = GetSelectedController();
@@ -401,7 +405,7 @@ namespace StandardScene.ExtendDevice.Door
return true;
}
private static bool IsValidIp(string ip)
private bool IsValidIp(string ip)
{
if (string.IsNullOrWhiteSpace(ip)) return false;
var pattern = @"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
@@ -20,13 +20,17 @@ namespace StandardScene.ExtendDevice.Door
{
private const string TableId = "door-monitor-list";
private static readonly Color SelectedRowColor = Color.FromArgb(230, 240, 255);
private readonly Color SelectedRowColor = Color.FromArgb(230, 240, 255);
private static Panel _panel;
private static (int ControllerIndex, int DoorIndex)? _selectedDoor;
private Panel _panel;
private (int ControllerIndex, int DoorIndex)? _selectedDoor;
/// <summary>打开(或置前)门控监控面板。</summary>
public static void Open()
public static void Open() => (_instance ??= new DoorMonitor()).OpenCore();
private static DoorMonitor _instance;
private void OpenCore()
{
if (_panel != null)
{
@@ -105,21 +109,21 @@ namespace StandardScene.ExtendDevice.Door
});
}
private static void LabelCell(PanelBuilder.Row row, DoorMission.DoorMonitorSnapshotItem door, string text)
private void LabelCell(PanelBuilder.Row row, DoorMission.DoorMonitorSnapshotItem door, string text)
{
if (row.Label(text))
_selectedDoor = (door.ControllerIndex, door.DoorIndex);
}
private static DoorMission GetMission() =>
private DoorMission GetMission() =>
SimpleProject.proj?.Missions?.OfType<DoorMission>().FirstOrDefault();
private static bool IsRowSelected(DoorMission.DoorMonitorSnapshotItem door) =>
private bool IsRowSelected(DoorMission.DoorMonitorSnapshotItem door) =>
_selectedDoor.HasValue
&& _selectedDoor.Value.ControllerIndex == door.ControllerIndex
&& _selectedDoor.Value.DoorIndex == door.DoorIndex;
private static string GetDoorInfoText(IReadOnlyList<DoorMission.DoorMonitorSnapshotItem> snapshot)
private string GetDoorInfoText(IReadOnlyList<DoorMission.DoorMonitorSnapshotItem> snapshot)
{
if (!_selectedDoor.HasValue)
return "请选择要控制的门";
@@ -133,7 +137,7 @@ namespace StandardScene.ExtendDevice.Door
return $"控制器编码: {door.ControllerIndex}, 门编码: {door.DoorIndex}";
}
private static bool CanCloseSelected(DoorMission mission)
private bool CanCloseSelected(DoorMission mission)
{
if (!_selectedDoor.HasValue || mission == null)
return true;
@@ -142,18 +146,18 @@ namespace StandardScene.ExtendDevice.Door
return cars.Count == 0;
}
private static string FormatState(DoorState state) =>
private string FormatState(DoorState state) =>
state == DoorState.Open ? "打开" : state == DoorState.Closed ? "关闭" : "未知";
private static string FormatManualRemain(DoorMission.DoorMonitorSnapshotItem door) =>
private string FormatManualRemain(DoorMission.DoorMonitorSnapshotItem door) =>
door.Source == DoorMission.ControlSource.Manual && door.ManualRemainingSeconds.HasValue
? Math.Ceiling(door.ManualRemainingSeconds.Value).ToString()
: "-";
private static string FormatCars(DoorMission.DoorMonitorSnapshotItem door) =>
private string FormatCars(DoorMission.DoorMonitorSnapshotItem door) =>
door.CarsInArea.Count > 0 ? string.Join(", ", door.CarsInArea) : "无";
private static void OpenSelectedDoor()
private void OpenSelectedDoor()
{
if (!_selectedDoor.HasValue)
{
@@ -180,7 +184,7 @@ namespace StandardScene.ExtendDevice.Door
}
}
private static void CloseSelectedDoor()
private void CloseSelectedDoor()
{
if (!_selectedDoor.HasValue)
{
@@ -213,7 +217,7 @@ namespace StandardScene.ExtendDevice.Door
}
}
private static void ClearSelectedCars()
private void ClearSelectedCars()
{
if (!_selectedDoor.HasValue)
{
@@ -25,29 +25,33 @@ namespace LoopViewerApp
{
private const string TableId = "traffic-area-list";
private static string JsonPath =>
private string JsonPath =>
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config", "traffic.json");
private static readonly object SaveLock = new object();
private readonly object SaveLock = new object();
private static Panel _panel;
private static string _editingAreaId = "";
private static readonly HashSet<string> _selected = new HashSet<string>(StringComparer.Ordinal);
private Panel _panel;
private string _editingAreaId = "";
private readonly HashSet<string> _selected = new HashSet<string>(StringComparer.Ordinal);
private static string _areaName = "";
private static string _stationIds = "";
private static string _controlRight = "";
private static bool _isOccupied;
private static bool _isEnabled = true;
private static string _editingHint = "新增区域";
private static string _editErr = "";
private static volatile string _status = "";
private string _areaName = "";
private string _stationIds = "";
private string _controlRight = "";
private bool _isOccupied;
private bool _isEnabled = true;
private string _editingHint = "新增区域";
private string _editErr = "";
private volatile string _status = "";
/// <summary>打开(或置前)区域管理面板。兼容原 <c>new TrafficInterlockViewer().Show()</c> 调用方式。</summary>
public void Show() => Open();
/// <summary>打开(或置前)区域管理面板。</summary>
public static void Open()
public static void Open() => (_instance ??= new TrafficInterlockViewer()).OpenCore();
private static TrafficInterlockViewer _instance;
private void OpenCore()
{
if (_panel != null)
{
@@ -171,7 +175,7 @@ namespace LoopViewerApp
});
}
private static void ConfirmDeleteSelected()
private void ConfirmDeleteSelected()
{
if (_selected.Count == 0)
{
@@ -187,7 +191,7 @@ namespace LoopViewerApp
CycleUiHelper.ConfirmThen(prompt, DeleteSelected);
}
private static void DeleteSelected()
private void DeleteSelected()
{
var selectedIds = _selected.ToHashSet(StringComparer.Ordinal);
var removed = 0;
@@ -203,7 +207,7 @@ namespace LoopViewerApp
_panel?.Repaint();
}
private static void SaveArea()
private void SaveArea()
{
try
{
@@ -270,7 +274,7 @@ namespace LoopViewerApp
}
}
private static void LoadAreaToFields(TrafficArea a)
private void LoadAreaToFields(TrafficArea a)
{
if (a == null) return;
_editingHint = $"编辑:{a.AreaName}";
@@ -284,7 +288,7 @@ namespace LoopViewerApp
_editErr = "";
}
private static void ClearPanelInputs()
private void ClearPanelInputs()
{
_editingAreaId = "";
_editingHint = "新增区域";
@@ -296,7 +300,7 @@ namespace LoopViewerApp
_editErr = "";
}
private static void SaveToConfig()
private void SaveToConfig()
{
string json;
try
@@ -331,7 +335,7 @@ namespace LoopViewerApp
}
/// <summary>解析站点集合字符串,如 "1,2,3" → <see cref="List{T}"/> of int。</summary>
private static List<int> ParseStationIds(string text)
private List<int> ParseStationIds(string text)
{
var list = new List<int>();
if (string.IsNullOrWhiteSpace(text)) return list;
@@ -343,19 +347,19 @@ namespace LoopViewerApp
return list;
}
private static void EnsureAreaIdsLocked()
private void EnsureAreaIdsLocked()
{
foreach (var area in TrafficInterlockMission.TrafficAreaList)
EnsureAreaId(area);
}
private static string EnsureAreaId(TrafficArea area)
private string EnsureAreaId(TrafficArea area)
{
if (string.IsNullOrWhiteSpace(area.Id))
area.Id = NewAreaId();
return area.Id;
}
private static string NewAreaId() => Guid.NewGuid().ToString("N");
private string NewAreaId() => Guid.NewGuid().ToString("N");
}
}