using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using CycleGUI;
using StandardScene.Utils;
namespace StandardScene.Charge
{
///
/// 通讯监控面板(CycleGUI 版,替代原 WinForms 窗体)。
///
/// - 单实例:再次打开则把已有面板置前。
/// - 订阅 ,批量刷新 UI(500ms 节流),最多显示 100 行。
/// - 支持 IP 筛选、暂停/继续、清空(二次确认)、选中报文解析详情。
///
/// 保留可实例化 + 以兼容既有调用。
///
public class CommunicationMonitorForm
{
private const int MaxDisplayRows = 100;
private const int UiBatchSize = 20;
private const int StatsRefreshMs = 500;
private const string TableId = "comm-monitor-msgs";
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 readonly CommunicationMessageService MessageService = CommunicationMessageService.Instance;
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 List _displayMessages = new List();
private readonly Queue PendingMessages = new Queue();
private readonly object PendingLock = new object();
private DateTime _lastStatsRefresh = DateTime.MinValue;
private bool _pendingStatsRefresh;
/// 打开(或置前)通讯监控面板。兼容原 new CommunicationMonitorForm().Show() 调用方式。
public void Show() => Open();
/// 打开(或置前)通讯监控面板。
public static void Open() => (_instance ??= new CommunicationMonitorForm()).OpenCore();
private static CommunicationMonitorForm _instance;
private void OpenCore()
{
if (_panel != null)
{
try
{
_panel.BringToFront();
return;
}
catch
{
_panel = null;
}
}
_paused = false;
_selectedRowIndex = -1;
_parsedText = "";
RefreshIpFilter();
ReloadFromService();
var panel = GUI.DeclarePanel()
.ShowTitle("通讯监控")
.SetDefaultDocking(Panel.Docking.None)
.InitSize(1400, 800)
.InitPos(false, 0, 0, 0.5f, 0.5f, 0.5f, 0.5f);
_panel = panel;
panel.IfTerminalQuit(() =>
{
Unsubscribe();
_panel = null;
});
Subscribe();
panel.Define(pb =>
{
if (pb.Closing())
{
Unsubscribe();
panel.Exit();
_panel = null;
return;
}
FlushPendingBatch();
if (pb.DropdownBox("IP筛选", _ipOptions, ref _selectedIpIndex))
ReloadFromService();
pb.SameLine(16);
if (pb.Button(_paused ? "继续" : "暂停", distinct: "comm-pause"))
_paused = !_paused;
pb.SameLine(8);
if (pb.Button("刷新", distinct: "comm-refresh"))
{
RefreshIpFilter();
ReloadFromService();
RequestStatisticsRefresh();
}
pb.SameLine(8);
if (pb.Button("清空", distinct: "comm-clear"))
{
CycleUiHelper.ConfirmThen("确定要清空所有报文记录吗?", () =>
{
MessageService.Clear();
lock (PendingLock)
PendingMessages.Clear();
RefreshIpFilter();
_displayMessages.Clear();
_selectedRowIndex = -1;
_parsedText = "";
RequestStatisticsRefresh();
});
}
pb.SameLine(8);
if (pb.Button("关闭", distinct: "comm-close"))
{
Unsubscribe();
panel.Exit();
_panel = null;
return;
}
MaybeRefreshStatistics();
pb.Label(_statsText);
pb.Table(TableId,
new[] { "时间", "方向", "IP地址", "端口", "长度", "原始数据", "站点", "类型", "操作" },
_displayMessages.Count, (row, i) =>
{
var msg = _displayMessages[i];
row.SetColor(_selectedRowIndex == i
? SelectedRowColor
: msg.Direction == MessageDirection.Send ? SendRowColor : ReceiveRowColor);
row.Label($"{msg.Timestamp:HH:mm:ss.fff}");
row.Label(msg.Direction == MessageDirection.Send ? "发送" : "接收");
row.Label(msg.IpAddress ?? "");
row.Label($"{msg.Port}");
row.Label($"{msg.Length}");
row.Label(TruncateRawData(msg.RawData));
row.Label(string.IsNullOrEmpty(msg.StationId) ? "-" : msg.StationId);
row.Label(msg.Type ?? "");
if (row.ButtonGroup(new[] { "解析" }, new[] { "解析该报文" }) == 0)
{
_selectedRowIndex = i;
_parsedText = BuildParsedText(msg);
}
}, height: 20, enableSearch: true);
pb.SeparatorText("报文解析");
pb.SelectableText(null, _parsedText ?? "", copyButton: true);
pb.Panel.Repaint(repaintTimeMs: 500);
});
}
private void Subscribe()
{
if (_subscribed)
return;
MessageService.MessageAdded += OnMessageAdded;
_subscribed = true;
}
private void Unsubscribe()
{
if (!_subscribed)
return;
MessageService.MessageAdded -= OnMessageAdded;
_subscribed = false;
lock (PendingLock)
PendingMessages.Clear();
}
private void OnMessageAdded(object sender, CommunicationMessage message)
{
if (!_subscribed || message == null)
return;
lock (PendingLock)
PendingMessages.Enqueue(message);
_panel?.Repaint();
}
/// 定时批量刷新 UI,避免每条报文都抢占渲染线程。
private void FlushPendingBatch()
{
if (_paused)
return;
List batch = null;
lock (PendingLock)
{
if (PendingMessages.Count == 0)
return;
int count = Math.Min(UiBatchSize, PendingMessages.Count);
batch = new List(count);
for (int i = 0; i < count; i++)
batch.Add(PendingMessages.Dequeue());
}
if (batch == null || batch.Count == 0)
return;
var filter = SelectedIpFilter();
bool displayChanged = false;
foreach (var message in batch)
{
EnsureIpInFilter(message.IpAddress);
if (string.IsNullOrEmpty(filter) || filter == "全部" || filter == message.IpAddress)
{
InsertMessageAtTop(message);
displayChanged = true;
}
}
if (displayChanged)
RequestStatisticsRefresh();
}
private void InsertMessageAtTop(CommunicationMessage msg)
{
_displayMessages.Insert(0, msg);
while (_displayMessages.Count > MaxDisplayRows)
_displayMessages.RemoveAt(_displayMessages.Count - 1);
if (_selectedRowIndex >= 0)
_selectedRowIndex++;
}
private void ReloadFromService()
{
try
{
var filter = SelectedIpFilter();
var messages = string.IsNullOrEmpty(filter) || filter == "全部"
? MessageService.GetAllMessages()
: MessageService.GetMessagesByIp(filter);
_displayMessages = messages.Take(MaxDisplayRows).ToList();
_selectedRowIndex = -1;
_parsedText = "";
_pendingStatsRefresh = false;
UpdateStatistics(_displayMessages.Count);
}
catch (Exception ex)
{
_statsText = $"加载报文失败: {ex.Message}";
}
}
private void RefreshIpFilter()
{
try
{
var selectedIp = SelectedIpFilter();
var options = new List { "全部" };
var ipAddresses = MessageService.GetUniqueIpAddresses();
if (ipAddresses != null)
{
foreach (var ip in ipAddresses)
{
if (!string.IsNullOrEmpty(ip))
options.Add(ip);
}
}
_ipOptions = options.ToArray();
if (!string.IsNullOrEmpty(selectedIp))
{
var idx = Array.IndexOf(_ipOptions, selectedIp);
_selectedIpIndex = idx >= 0 ? idx : 0;
}
else
{
_selectedIpIndex = 0;
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"刷新IP筛选失败: {ex.Message}");
}
}
private void EnsureIpInFilter(string ipAddress)
{
if (string.IsNullOrWhiteSpace(ipAddress))
return;
if (_ipOptions.Contains(ipAddress))
return;
var list = _ipOptions.ToList();
list.Add(ipAddress);
_ipOptions = list.ToArray();
}
private string SelectedIpFilter()
{
if (_ipOptions == null || _ipOptions.Length == 0)
return "全部";
if (_selectedIpIndex < 0 || _selectedIpIndex >= _ipOptions.Length)
return "全部";
return _ipOptions[_selectedIpIndex];
}
private void RequestStatisticsRefresh()
{
_pendingStatsRefresh = true;
}
/// 统计信息低频刷新(500ms)。
private void MaybeRefreshStatistics()
{
if (!_pendingStatsRefresh)
return;
if (DateTime.Now - _lastStatsRefresh < TimeSpan.FromMilliseconds(StatsRefreshMs))
return;
_pendingStatsRefresh = false;
_lastStatsRefresh = DateTime.Now;
UpdateStatistics(_displayMessages.Count);
}
private void UpdateStatistics(int displayCount)
{
try
{
var allMessages = MessageService.GetAllMessages();
if (allMessages == null)
{
_statsText = "统计信息加载失败";
return;
}
var sendCount = allMessages.Count(m => m.Direction == MessageDirection.Send);
var receiveCount = allMessages.Count(m => m.Direction == MessageDirection.Receive);
_statsText = $"显示: {displayCount} | 总数: {allMessages.Count} | 发送: {sendCount} | 接收: {receiveCount}";
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"更新统计信息失败: {ex.Message}");
_statsText = "统计信息加载失败";
}
}
private string TruncateRawData(string rawData, int maxLen = 48)
{
if (string.IsNullOrEmpty(rawData))
return "";
return rawData.Length <= maxLen ? rawData : rawData.Substring(0, maxLen) + "…";
}
private string BuildParsedText(CommunicationMessage message)
{
if (message == null)
return "";
try
{
var parsed = new StringBuilder();
parsed.AppendLine("=== 报文解析 ===");
parsed.AppendLine($"时间: {message.Timestamp:yyyy-MM-dd HH:mm:ss.fff}");
parsed.AppendLine($"方向: {(message.Direction == MessageDirection.Send ? "发送" : "接收")}");
parsed.AppendLine($"地址: {message.IpAddress}:{message.Port}");
parsed.AppendLine($"站点: {message.StationId ?? "未关联"}");
parsed.AppendLine($"长度: {message.Length} 字节");
parsed.AppendLine();
parsed.AppendLine("=== 原始数据 (HEX) ===");
parsed.AppendLine(message.RawData);
parsed.AppendLine();
parsed.AppendLine("=== 数据解析 ===");
if (message.Direction == MessageDirection.Send)
{
var sendData = MessageService.ParseSendRawData(message.RawData, message.Type);
parsed.AppendLine("示例解析:");
parsed.AppendLine($"充电指令:{sendData.ChargeCommand}");
parsed.AppendLine($"发送电压:{sendData.SetVoltage}");
parsed.AppendLine($"发送电流:{sendData.SetCurrent}");
parsed.AppendLine($"车辆ID:{sendData.CurrentVehicleId}");
parsed.AppendLine($"车辆电量:{sendData.BatteryLevel}");
parsed.AppendLine($"车辆电压:{sendData.CarVoltage}");
parsed.AppendLine($"车辆电流:{sendData.CarCurrent}");
}
else
{
var recData = MessageService.ParseReceiveRawData(message.RawData, message.Type);
string mechanismStatus = (int)recData.MechanismStatus == 1 ? "伸出"
: (int)recData.MechanismStatus == 2 ? "缩回"
: (int)recData.MechanismStatus == 3 ? "运动中"
: recData.MechanismStatus.ToString();
parsed.AppendLine("示例解析:");
parsed.AppendLine($"机构状态:{mechanismStatus}");
parsed.AppendLine($"实时电压:{recData.RealTimeVoltage}");
parsed.AppendLine($"实时电流:{recData.RealTimeCurrent}");
parsed.AppendLine($"充电量: {recData.BatteryAH}");
parsed.AppendLine($"是否报警:{recData.HasAlarm}");
parsed.AppendLine($"充电状态:{recData.Status}");
}
return parsed.ToString();
}
catch (Exception ex)
{
return $"解析失败: {ex.Message}";
}
}
}
}