2026-06-14 11:19:15 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Drawing;
|
|
|
|
|
|
using System.Linq;
|
2026-06-26 15:00:53 +08:00
|
|
|
|
using System.Text;
|
2026-08-26 11:18:40 +08:00
|
|
|
|
using FairyView;
|
2026-06-26 15:00:53 +08:00
|
|
|
|
using StandardScene.Utils;
|
2026-06-14 11:19:15 +08:00
|
|
|
|
|
|
|
|
|
|
namespace StandardScene.Charge
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
2026-06-26 15:00:53 +08:00
|
|
|
|
/// 通讯监控面板(CycleGUI 版,替代原 WinForms 窗体)。
|
|
|
|
|
|
/// <list type="bullet">
|
|
|
|
|
|
/// <item>单实例:再次打开则把已有面板置前。</item>
|
|
|
|
|
|
/// <item>订阅 <see cref="CommunicationMessageService.MessageAdded"/>,批量刷新 UI(500ms 节流),最多显示 100 行。</item>
|
|
|
|
|
|
/// <item>支持 IP 筛选、暂停/继续、清空(二次确认)、选中报文解析详情。</item>
|
|
|
|
|
|
/// </list>
|
|
|
|
|
|
/// 保留可实例化 + <see cref="Show"/> 以兼容既有调用。
|
2026-06-14 11:19:15 +08:00
|
|
|
|
/// </summary>
|
2026-06-26 15:00:53 +08:00
|
|
|
|
public class CommunicationMonitorForm
|
2026-06-14 11:19:15 +08:00
|
|
|
|
{
|
|
|
|
|
|
private const int MaxDisplayRows = 100;
|
|
|
|
|
|
private const int UiBatchSize = 20;
|
|
|
|
|
|
private const int StatsRefreshMs = 500;
|
2026-06-26 15:00:53 +08:00
|
|
|
|
private const string TableId = "comm-monitor-msgs";
|
2026-06-14 11:19:15 +08:00
|
|
|
|
|
2026-06-26 15:31:02 +08:00
|
|
|
|
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);
|
2026-06-14 11:19:15 +08:00
|
|
|
|
|
2026-06-26 15:31:02 +08:00
|
|
|
|
private readonly CommunicationMessageService MessageService = CommunicationMessageService.Instance;
|
2026-06-14 11:19:15 +08:00
|
|
|
|
|
2026-06-26 15:31:02 +08:00
|
|
|
|
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 = "";
|
2026-06-14 11:19:15 +08:00
|
|
|
|
|
2026-06-26 15:31:02 +08:00
|
|
|
|
private List<CommunicationMessage> _displayMessages = new List<CommunicationMessage>();
|
|
|
|
|
|
private readonly Queue<CommunicationMessage> PendingMessages = new Queue<CommunicationMessage>();
|
|
|
|
|
|
private readonly object PendingLock = new object();
|
2026-06-14 11:19:15 +08:00
|
|
|
|
|
2026-06-26 15:31:02 +08:00
|
|
|
|
private DateTime _lastStatsRefresh = DateTime.MinValue;
|
|
|
|
|
|
private bool _pendingStatsRefresh;
|
2026-06-26 15:00:53 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>打开(或置前)通讯监控面板。兼容原 <c>new CommunicationMonitorForm().Show()</c> 调用方式。</summary>
|
|
|
|
|
|
public void Show() => Open();
|
2026-06-14 11:19:15 +08:00
|
|
|
|
|
2026-06-26 15:00:53 +08:00
|
|
|
|
/// <summary>打开(或置前)通讯监控面板。</summary>
|
2026-06-26 15:31:02 +08:00
|
|
|
|
public static void Open() => (_instance ??= new CommunicationMonitorForm()).OpenCore();
|
|
|
|
|
|
|
|
|
|
|
|
private static CommunicationMonitorForm _instance;
|
|
|
|
|
|
|
|
|
|
|
|
private void OpenCore()
|
2026-06-14 11:19:15 +08:00
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
if (_panel != null)
|
2026-06-14 11:19:15 +08:00
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
_panel.BringToFront();
|
2026-06-14 11:19:15 +08:00
|
|
|
|
return;
|
2026-06-26 15:00:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
_panel = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_paused = false;
|
|
|
|
|
|
_selectedRowIndex = -1;
|
|
|
|
|
|
_parsedText = "";
|
|
|
|
|
|
RefreshIpFilter();
|
|
|
|
|
|
ReloadFromService();
|
2026-06-14 11:19:15 +08:00
|
|
|
|
|
2026-06-26 15:00:53 +08:00
|
|
|
|
var panel = GUI.DeclarePanel()
|
|
|
|
|
|
.ShowTitle("通讯监控")
|
2026-08-26 11:18:40 +08:00
|
|
|
|
.ForceFloat()
|
2026-06-26 15:00:53 +08:00
|
|
|
|
.InitSize(1400, 800)
|
|
|
|
|
|
.InitPos(false, 0, 0, 0.5f, 0.5f, 0.5f, 0.5f);
|
|
|
|
|
|
_panel = panel;
|
|
|
|
|
|
panel.IfTerminalQuit(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
Unsubscribe();
|
|
|
|
|
|
_panel = null;
|
|
|
|
|
|
});
|
2026-06-14 11:19:15 +08:00
|
|
|
|
|
2026-06-26 15:00:53 +08:00
|
|
|
|
Subscribe();
|
2026-06-14 11:19:15 +08:00
|
|
|
|
|
2026-06-26 15:00:53 +08:00
|
|
|
|
panel.Define(pb =>
|
|
|
|
|
|
{
|
|
|
|
|
|
if (pb.Closing())
|
2026-06-14 11:19:15 +08:00
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
Unsubscribe();
|
|
|
|
|
|
panel.Exit();
|
|
|
|
|
|
_panel = null;
|
|
|
|
|
|
return;
|
2026-06-14 11:19:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-26 15:00:53 +08:00
|
|
|
|
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"))
|
2026-06-14 11:19:15 +08:00
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
RefreshIpFilter();
|
|
|
|
|
|
ReloadFromService();
|
|
|
|
|
|
RequestStatisticsRefresh();
|
2026-06-14 11:19:15 +08:00
|
|
|
|
}
|
2026-06-26 15:00:53 +08:00
|
|
|
|
pb.SameLine(8);
|
|
|
|
|
|
if (pb.Button("清空", distinct: "comm-clear"))
|
2026-06-14 11:19:15 +08:00
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
CycleUiHelper.ConfirmThen("确定要清空所有报文记录吗?", () =>
|
|
|
|
|
|
{
|
|
|
|
|
|
MessageService.Clear();
|
|
|
|
|
|
lock (PendingLock)
|
|
|
|
|
|
PendingMessages.Clear();
|
|
|
|
|
|
RefreshIpFilter();
|
|
|
|
|
|
_displayMessages.Clear();
|
|
|
|
|
|
_selectedRowIndex = -1;
|
|
|
|
|
|
_parsedText = "";
|
|
|
|
|
|
RequestStatisticsRefresh();
|
|
|
|
|
|
});
|
2026-06-14 11:19:15 +08:00
|
|
|
|
}
|
2026-06-26 15:00:53 +08:00
|
|
|
|
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);
|
|
|
|
|
|
}
|
2026-08-26 11:18:40 +08:00
|
|
|
|
}, height: CycleUiHelper.TableHeightPx(20), enableSearch: true);
|
2026-06-26 15:00:53 +08:00
|
|
|
|
|
|
|
|
|
|
pb.SeparatorText("报文解析");
|
|
|
|
|
|
pb.SelectableText(null, _parsedText ?? "", copyButton: true);
|
|
|
|
|
|
|
|
|
|
|
|
pb.Panel.Repaint(repaintTimeMs: 500);
|
|
|
|
|
|
});
|
2026-06-14 11:19:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-26 15:31:02 +08:00
|
|
|
|
private void Subscribe()
|
2026-06-14 11:19:15 +08:00
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
if (_subscribed)
|
|
|
|
|
|
return;
|
|
|
|
|
|
MessageService.MessageAdded += OnMessageAdded;
|
|
|
|
|
|
_subscribed = true;
|
|
|
|
|
|
}
|
2026-06-14 11:19:15 +08:00
|
|
|
|
|
2026-06-26 15:31:02 +08:00
|
|
|
|
private void Unsubscribe()
|
2026-06-26 15:00:53 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (!_subscribed)
|
|
|
|
|
|
return;
|
|
|
|
|
|
MessageService.MessageAdded -= OnMessageAdded;
|
|
|
|
|
|
_subscribed = false;
|
|
|
|
|
|
lock (PendingLock)
|
|
|
|
|
|
PendingMessages.Clear();
|
|
|
|
|
|
}
|
2026-06-14 11:19:15 +08:00
|
|
|
|
|
2026-06-26 15:31:02 +08:00
|
|
|
|
private void OnMessageAdded(object sender, CommunicationMessage message)
|
2026-06-26 15:00:53 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (!_subscribed || message == null)
|
|
|
|
|
|
return;
|
2026-06-14 11:19:15 +08:00
|
|
|
|
|
2026-06-26 15:00:53 +08:00
|
|
|
|
lock (PendingLock)
|
|
|
|
|
|
PendingMessages.Enqueue(message);
|
2026-06-14 11:19:15 +08:00
|
|
|
|
|
2026-06-26 15:00:53 +08:00
|
|
|
|
_panel?.Repaint();
|
2026-06-14 11:19:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-26 15:00:53 +08:00
|
|
|
|
/// <summary>定时批量刷新 UI,避免每条报文都抢占渲染线程。</summary>
|
2026-06-26 15:31:02 +08:00
|
|
|
|
private void FlushPendingBatch()
|
2026-06-14 11:19:15 +08:00
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
if (_paused)
|
2026-06-14 11:19:15 +08:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
List<CommunicationMessage> batch = null;
|
2026-06-26 15:00:53 +08:00
|
|
|
|
lock (PendingLock)
|
2026-06-14 11:19:15 +08:00
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
if (PendingMessages.Count == 0)
|
2026-06-14 11:19:15 +08:00
|
|
|
|
return;
|
|
|
|
|
|
|
2026-06-26 15:00:53 +08:00
|
|
|
|
int count = Math.Min(UiBatchSize, PendingMessages.Count);
|
2026-06-14 11:19:15 +08:00
|
|
|
|
batch = new List<CommunicationMessage>(count);
|
|
|
|
|
|
for (int i = 0; i < count; i++)
|
2026-06-26 15:00:53 +08:00
|
|
|
|
batch.Add(PendingMessages.Dequeue());
|
2026-06-14 11:19:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (batch == null || batch.Count == 0)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2026-06-26 15:00:53 +08:00
|
|
|
|
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();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-26 15:31:02 +08:00
|
|
|
|
private void InsertMessageAtTop(CommunicationMessage msg)
|
2026-06-26 15:00:53 +08:00
|
|
|
|
{
|
|
|
|
|
|
_displayMessages.Insert(0, msg);
|
|
|
|
|
|
while (_displayMessages.Count > MaxDisplayRows)
|
|
|
|
|
|
_displayMessages.RemoveAt(_displayMessages.Count - 1);
|
|
|
|
|
|
|
|
|
|
|
|
if (_selectedRowIndex >= 0)
|
|
|
|
|
|
_selectedRowIndex++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-26 15:31:02 +08:00
|
|
|
|
private void ReloadFromService()
|
2026-06-26 15:00:53 +08:00
|
|
|
|
{
|
|
|
|
|
|
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}";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-26 15:31:02 +08:00
|
|
|
|
private void RefreshIpFilter()
|
2026-06-26 15:00:53 +08:00
|
|
|
|
{
|
2026-06-14 11:19:15 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
var selectedIp = SelectedIpFilter();
|
|
|
|
|
|
var options = new List<string> { "全部" };
|
2026-06-14 11:19:15 +08:00
|
|
|
|
|
2026-06-26 15:00:53 +08:00
|
|
|
|
var ipAddresses = MessageService.GetUniqueIpAddresses();
|
|
|
|
|
|
if (ipAddresses != null)
|
2026-06-14 11:19:15 +08:00
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
foreach (var ip in ipAddresses)
|
2026-06-14 11:19:15 +08:00
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
if (!string.IsNullOrEmpty(ip))
|
|
|
|
|
|
options.Add(ip);
|
2026-06-14 11:19:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-26 15:00:53 +08:00
|
|
|
|
_ipOptions = options.ToArray();
|
|
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(selectedIp))
|
|
|
|
|
|
{
|
|
|
|
|
|
var idx = Array.IndexOf(_ipOptions, selectedIp);
|
|
|
|
|
|
_selectedIpIndex = idx >= 0 ? idx : 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
2026-06-14 11:19:15 +08:00
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
_selectedIpIndex = 0;
|
2026-06-14 11:19:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-26 15:00:53 +08:00
|
|
|
|
catch (Exception ex)
|
2026-06-14 11:19:15 +08:00
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
System.Diagnostics.Debug.WriteLine($"刷新IP筛选失败: {ex.Message}");
|
2026-06-14 11:19:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-26 15:31:02 +08:00
|
|
|
|
private void EnsureIpInFilter(string ipAddress)
|
2026-06-14 11:19:15 +08:00
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
if (string.IsNullOrWhiteSpace(ipAddress))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
if (_ipOptions.Contains(ipAddress))
|
2026-06-14 11:19:15 +08:00
|
|
|
|
return;
|
|
|
|
|
|
|
2026-06-26 15:00:53 +08:00
|
|
|
|
var list = _ipOptions.ToList();
|
|
|
|
|
|
list.Add(ipAddress);
|
|
|
|
|
|
_ipOptions = list.ToArray();
|
2026-06-14 11:19:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-26 15:31:02 +08:00
|
|
|
|
private string SelectedIpFilter()
|
2026-06-14 11:19:15 +08:00
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
if (_ipOptions == null || _ipOptions.Length == 0)
|
|
|
|
|
|
return "全部";
|
|
|
|
|
|
if (_selectedIpIndex < 0 || _selectedIpIndex >= _ipOptions.Length)
|
|
|
|
|
|
return "全部";
|
|
|
|
|
|
return _ipOptions[_selectedIpIndex];
|
2026-06-14 11:19:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-26 15:31:02 +08:00
|
|
|
|
private void RequestStatisticsRefresh()
|
2026-06-14 11:19:15 +08:00
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
_pendingStatsRefresh = true;
|
2026-06-14 11:19:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-26 15:00:53 +08:00
|
|
|
|
/// <summary>统计信息低频刷新(500ms)。</summary>
|
2026-06-26 15:31:02 +08:00
|
|
|
|
private void MaybeRefreshStatistics()
|
2026-06-14 11:19:15 +08:00
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
if (!_pendingStatsRefresh)
|
|
|
|
|
|
return;
|
|
|
|
|
|
if (DateTime.Now - _lastStatsRefresh < TimeSpan.FromMilliseconds(StatsRefreshMs))
|
2026-06-14 11:19:15 +08:00
|
|
|
|
return;
|
|
|
|
|
|
|
2026-06-26 15:00:53 +08:00
|
|
|
|
_pendingStatsRefresh = false;
|
|
|
|
|
|
_lastStatsRefresh = DateTime.Now;
|
|
|
|
|
|
UpdateStatistics(_displayMessages.Count);
|
2026-06-14 11:19:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-26 15:31:02 +08:00
|
|
|
|
private void UpdateStatistics(int displayCount)
|
2026-06-14 11:19:15 +08:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
var allMessages = MessageService.GetAllMessages();
|
2026-06-14 11:19:15 +08:00
|
|
|
|
if (allMessages == null)
|
2026-06-26 15:00:53 +08:00
|
|
|
|
{
|
|
|
|
|
|
_statsText = "统计信息加载失败";
|
2026-06-14 11:19:15 +08:00
|
|
|
|
return;
|
2026-06-26 15:00:53 +08:00
|
|
|
|
}
|
2026-06-14 11:19:15 +08:00
|
|
|
|
|
|
|
|
|
|
var sendCount = allMessages.Count(m => m.Direction == MessageDirection.Send);
|
|
|
|
|
|
var receiveCount = allMessages.Count(m => m.Direction == MessageDirection.Receive);
|
2026-06-26 15:00:53 +08:00
|
|
|
|
_statsText = $"显示: {displayCount} | 总数: {allMessages.Count} | 发送: {sendCount} | 接收: {receiveCount}";
|
2026-06-14 11:19:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
System.Diagnostics.Debug.WriteLine($"更新统计信息失败: {ex.Message}");
|
2026-06-26 15:00:53 +08:00
|
|
|
|
_statsText = "统计信息加载失败";
|
2026-06-14 11:19:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-26 15:31:02 +08:00
|
|
|
|
private string TruncateRawData(string rawData, int maxLen = 48)
|
2026-06-14 11:19:15 +08:00
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
if (string.IsNullOrEmpty(rawData))
|
|
|
|
|
|
return "";
|
|
|
|
|
|
return rawData.Length <= maxLen ? rawData : rawData.Substring(0, maxLen) + "…";
|
2026-06-14 11:19:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-26 15:31:02 +08:00
|
|
|
|
private string BuildParsedText(CommunicationMessage message)
|
2026-06-14 11:19:15 +08:00
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
if (message == null)
|
|
|
|
|
|
return "";
|
2026-06-14 11:19:15 +08:00
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
var parsed = new StringBuilder();
|
2026-06-14 11:19:15 +08:00
|
|
|
|
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("=== 数据解析 ===");
|
|
|
|
|
|
|
2026-06-26 15:00:53 +08:00
|
|
|
|
if (message.Direction == MessageDirection.Send)
|
2026-06-14 11:19:15 +08:00
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
var sendData = MessageService.ParseSendRawData(message.RawData, message.Type);
|
2026-06-14 11:19:15 +08:00
|
|
|
|
parsed.AppendLine("示例解析:");
|
2026-06-26 15:00:53 +08:00
|
|
|
|
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}");
|
2026-06-14 11:19:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
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();
|
2026-06-14 11:19:15 +08:00
|
|
|
|
parsed.AppendLine("示例解析:");
|
|
|
|
|
|
parsed.AppendLine($"机构状态:{mechanismStatus}");
|
2026-06-26 15:00:53 +08:00
|
|
|
|
parsed.AppendLine($"实时电压:{recData.RealTimeVoltage}");
|
|
|
|
|
|
parsed.AppendLine($"实时电流:{recData.RealTimeCurrent}");
|
|
|
|
|
|
parsed.AppendLine($"充电量: {recData.BatteryAH}");
|
|
|
|
|
|
parsed.AppendLine($"是否报警:{recData.HasAlarm}");
|
|
|
|
|
|
parsed.AppendLine($"充电状态:{recData.Status}");
|
2026-06-14 11:19:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-26 15:00:53 +08:00
|
|
|
|
return parsed.ToString();
|
2026-06-14 11:19:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2026-06-26 15:00:53 +08:00
|
|
|
|
return $"解析失败: {ex.Message}";
|
2026-06-14 11:19:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|