using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using CycleGUI;
using Newtonsoft.Json;
using StandardScene.InterLock;
using StandardScene.Utils;
namespace LoopViewerApp
{
///
/// 交通联锁区域管理界面(CycleGUI 版,替代原 WinForms TrafficInterlockViewer 窗体)。
///
/// - 维护 的增 / 改 / 删,并写入 Config/traffic.json。
/// - 单实例:再次打开则把已有面板置前。
/// - 勾选多行后「删除选中」可批量删除;每行「编辑」按钮加载下方编辑区。
/// - 文件写入放后台线程,绝不阻塞渲染线程。
///
/// 沿用 LoopViewer / DeliveryViewer 同套模式(单实例面板、pb.Table、CycleUiHelper.ConfirmThen)。
/// 保留可实例化 + 以兼容既有调用 new TrafficInterlockViewer().Show()。
///
public class TrafficInterlockViewer
{
private const string TableId = "traffic-area-list";
private static string JsonPath =>
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config", "traffic.json");
private static readonly object SaveLock = new object();
private static Panel _panel;
private static string _editingAreaId = "";
private static readonly HashSet _selected = new HashSet(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 = "";
/// 打开(或置前)区域管理面板。兼容原 new TrafficInterlockViewer().Show() 调用方式。
public void Show() => Open();
/// 打开(或置前)区域管理面板。
public static void Open()
{
if (_panel != null)
{
try
{
_panel.BringToFront();
return;
}
catch
{
_panel = null;
}
}
ClearPanelInputs();
var panel = GUI.DeclarePanel()
.ShowTitle("交通联锁区域管理")
.SetDefaultDocking(Panel.Docking.None)
.InitSize(800, 680)
.InitPos(false, 0, 0, 0.5f, 0.5f, 0.5f, 0.5f);
_panel = panel;
panel.IfTerminalQuit(() => { if (_panel == panel) _panel = null; });
panel.Define(pb =>
{
if (pb.Closing())
{
panel.Exit();
_panel = null;
return;
}
List areas;
lock (TrafficInterlockMission.TrafficAreaList)
{
EnsureAreaIdsLocked();
areas = TrafficInterlockMission.TrafficAreaList.ToList();
}
_selected.RemoveWhere(id => areas.All(a => a.Id != id));
if (!string.IsNullOrEmpty(_editingAreaId) && areas.All(a => a.Id != _editingAreaId))
ClearPanelInputs();
if (pb.Button("删除选中", distinct: "traffic-del-selected"))
ConfirmDeleteSelected();
pb.SameLine(12);
if (pb.Button("刷新", distinct: "traffic-refresh"))
{
_status = "";
_editErr = "";
}
pb.SameLine(12);
pb.Label($"共 {areas.Count} 个区域,已选 {_selected.Count} 个");
pb.Table(TableId,
new[] { "选择", "区域名称", "区域站点集合", "控制权", "是否被占用", "是否启用", "操作" },
areas.Count, (row, i) =>
{
var a = areas[i];
var areaId = EnsureAreaId(a);
var sel = _selected.Contains(areaId);
if (row.Checkbox(ref sel, "勾选以批量删除"))
{
if (sel) _selected.Add(areaId);
else _selected.Remove(areaId);
}
row.Label(a.AreaName ?? "");
row.Label(a.SiteList != null && a.SiteList.Count > 0
? string.Join(", ", a.SiteList)
: "");
row.Label(a.ControllerName ?? "");
row.Label(a.IsOccupy ? "是" : "否");
row.Label(a.IsEnable ? "是" : "否");
if (row.ButtonGroup(new[] { "编辑" }, new[] { "编辑该区域" }) == 0)
{
_editingAreaId = areaId;
LoadAreaToFields(a);
}
}, height: 14, enableSearch: true);
pb.Separator();
pb.Label("数据新增/编辑(点击表格行「编辑」可在此查看并修改该行数据)");
pb.Label(_editingHint);
var (name, _) = pb.TextInput("1. 区域名称", _areaName, alwaysReturnString: true);
_areaName = name;
var (sites, _) = pb.TextInput("2. 站点集合 (逗号/分号/空格分隔)", _stationIds, alwaysReturnString: true);
_stationIds = sites;
var (ctrl, _) = pb.TextInput("3. 控制权", _controlRight, alwaysReturnString: true);
_controlRight = ctrl;
pb.CheckBox("4. 是否被占用", ref _isOccupied);
pb.SameLine(16);
pb.CheckBox("5. 是否启用", ref _isEnabled);
if (!string.IsNullOrEmpty(_editErr))
{
pb.Separator();
pb.Label(_editErr);
}
pb.Separator();
if (pb.Button("保存", distinct: "traffic-save"))
SaveArea();
pb.SameLine(12);
if (pb.Button("新增", distinct: "traffic-new"))
{
_selected.Clear();
ClearPanelInputs();
}
if (!string.IsNullOrEmpty(_status))
{
pb.Separator();
pb.Label(_status);
}
pb.Panel.Repaint(repaintTimeMs: 500);
});
}
private static void ConfirmDeleteSelected()
{
if (_selected.Count == 0)
{
_status = "请先在上方列表中选择要删除的区域。";
_panel?.Repaint();
return;
}
string prompt = _selected.Count == 1
? "确定要删除选中的区域吗?"
: $"确定要删除所选 {_selected.Count} 个区域吗?";
CycleUiHelper.ConfirmThen(prompt, DeleteSelected);
}
private static void DeleteSelected()
{
var selectedIds = _selected.ToHashSet(StringComparer.Ordinal);
var removed = 0;
lock (TrafficInterlockMission.TrafficAreaList)
{
removed = TrafficInterlockMission.TrafficAreaList.RemoveAll(a => selectedIds.Contains(a.Id));
}
_selected.Clear();
SaveToConfig();
ClearPanelInputs();
_status = $"已删除 {removed} 个区域";
_panel?.Repaint();
}
private static void SaveArea()
{
try
{
string areaName = (_areaName ?? "").Trim();
if (string.IsNullOrEmpty(areaName))
{
_editErr = "请输入区域名称。";
_panel?.Repaint();
return;
}
var stationIds = ParseStationIds(_stationIds ?? "");
string controlRight = (_controlRight ?? "").Trim();
bool isOccupied = _isOccupied;
bool isEnabled = _isEnabled;
if (!string.IsNullOrEmpty(_editingAreaId))
{
lock (TrafficInterlockMission.TrafficAreaList)
{
var existing = TrafficInterlockMission.TrafficAreaList
.FirstOrDefault(a => string.Equals(a.Id, _editingAreaId, StringComparison.Ordinal));
if (existing == null)
{
_editErr = "正在编辑的区域已不存在,请刷新后重试。";
_panel?.Repaint();
return;
}
existing.AreaName = areaName;
existing.SiteList = stationIds;
existing.ControllerName = controlRight;
existing.IsOccupy = isOccupied;
existing.IsEnable = isEnabled;
}
_status = $"已保存区域:{areaName}";
}
else
{
lock (TrafficInterlockMission.TrafficAreaList)
{
TrafficInterlockMission.TrafficAreaList.Add(new TrafficArea
{
Id = NewAreaId(),
AreaName = areaName,
SiteList = stationIds,
ControllerName = controlRight,
IsOccupy = isOccupied,
IsEnable = isEnabled
});
}
_status = "已新增区域";
}
SaveToConfig();
ClearPanelInputs();
_editErr = "";
_panel?.Repaint();
}
catch (Exception ex)
{
_editErr = "操作失败:" + ex.Message;
_panel?.Repaint();
}
}
private static void LoadAreaToFields(TrafficArea a)
{
if (a == null) return;
_editingHint = $"编辑:{a.AreaName}";
_areaName = a.AreaName ?? "";
_stationIds = a.SiteList != null && a.SiteList.Count > 0
? string.Join(", ", a.SiteList)
: "";
_controlRight = a.ControllerName ?? "";
_isOccupied = a.IsOccupy;
_isEnabled = a.IsEnable;
_editErr = "";
}
private static void ClearPanelInputs()
{
_editingAreaId = "";
_editingHint = "新增区域";
_areaName = "";
_stationIds = "";
_controlRight = "";
_isOccupied = false;
_isEnabled = true;
_editErr = "";
}
private static void SaveToConfig()
{
string json;
try
{
lock (TrafficInterlockMission.TrafficAreaList)
json = JsonConvert.SerializeObject(TrafficInterlockMission.TrafficAreaList, Formatting.Indented);
}
catch (Exception ex)
{
_status = "保存失败:" + ex.Message;
_panel?.Repaint();
return;
}
var path = JsonPath;
var dir = Path.GetDirectoryName(path);
Task.Run(() =>
{
try
{
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
Directory.CreateDirectory(dir);
lock (SaveLock)
File.WriteAllText(path, json);
}
catch (Exception ex)
{
_status = "保存失败:" + ex.Message;
_panel?.Repaint();
}
});
}
/// 解析站点集合字符串,如 "1,2,3" → of int。
private static List ParseStationIds(string text)
{
var list = new List();
if (string.IsNullOrWhiteSpace(text)) return list;
foreach (var part in text.Split(new[] { ',', ';', ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries))
{
if (int.TryParse(part.Trim(), out int id))
list.Add(id);
}
return list;
}
private static void EnsureAreaIdsLocked()
{
foreach (var area in TrafficInterlockMission.TrafficAreaList)
EnsureAreaId(area);
}
private static string EnsureAreaId(TrafficArea area)
{
if (string.IsNullOrWhiteSpace(area.Id))
area.Id = NewAreaId();
return area.Id;
}
private static string NewAreaId() => Guid.NewGuid().ToString("N");
}
}