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:
@@ -2,108 +2,298 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using System.Text;
|
||||
using CycleGUI;
|
||||
using StandardScene.Utils;
|
||||
|
||||
namespace StandardScene.Charge
|
||||
{
|
||||
/// <summary>
|
||||
/// 通讯监控窗体
|
||||
/// 通讯监控面板(CycleGUI 版,替代原 WinForms 窗体)。
|
||||
/// <list type="bullet">
|
||||
/// <item>单实例:再次打开则把已有面板置前。</item>
|
||||
/// <item>订阅 <see cref="CommunicationMessageService.MessageAdded"/>,批量刷新 UI(500ms 节流),最多显示 100 行。</item>
|
||||
/// <item>支持 IP 筛选、暂停/继续、清空(二次确认)、选中报文解析详情。</item>
|
||||
/// </list>
|
||||
/// 保留可实例化 + <see cref="Show"/> 以兼容既有调用。
|
||||
/// </summary>
|
||||
public partial class CommunicationMonitorForm : Form
|
||||
public class CommunicationMonitorForm
|
||||
{
|
||||
private readonly CommunicationMessageService messageService;
|
||||
private bool isFormLoaded = false;
|
||||
private bool isFormMessageStop = false;
|
||||
private const int MaxDisplayRows = 100;
|
||||
private const int UiBatchSize = 20;
|
||||
private const int StatsRefreshMs = 500;
|
||||
private readonly Queue<CommunicationMessage> pendingMessages = new Queue<CommunicationMessage>();
|
||||
private readonly object pendingMessagesLock = new object();
|
||||
private readonly Timer uiFlushTimer;
|
||||
private readonly Timer statsRefreshTimer;
|
||||
private bool pendingStatsRefresh = false;
|
||||
private int lastDisplayCountForStats = 0;
|
||||
public CommunicationMonitorForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
messageService = CommunicationMessageService.Instance;
|
||||
uiFlushTimer = new Timer { Interval = 500 };
|
||||
uiFlushTimer.Tick += UiFlushTimer_Tick;
|
||||
statsRefreshTimer = new Timer { Interval = StatsRefreshMs };
|
||||
statsRefreshTimer.Tick += StatsRefreshTimer_Tick;
|
||||
private const string TableId = "comm-monitor-msgs";
|
||||
|
||||
// 订阅窗体关闭事件
|
||||
this.FormClosing += CommunicationMonitorForm_FormClosing;
|
||||
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 static 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 static List<CommunicationMessage> _displayMessages = new List<CommunicationMessage>();
|
||||
private static readonly Queue<CommunicationMessage> PendingMessages = new Queue<CommunicationMessage>();
|
||||
private static readonly object PendingLock = new object();
|
||||
|
||||
private static DateTime _lastStatsRefresh = DateTime.MinValue;
|
||||
private static bool _pendingStatsRefresh;
|
||||
|
||||
/// <summary>打开(或置前)通讯监控面板。兼容原 <c>new CommunicationMonitorForm().Show()</c> 调用方式。</summary>
|
||||
public void Show() => Open();
|
||||
|
||||
/// <summary>打开(或置前)通讯监控面板。</summary>
|
||||
public static void Open()
|
||||
{
|
||||
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 CommunicationMonitorForm_Load(object sender, EventArgs e)
|
||||
private static void Subscribe()
|
||||
{
|
||||
if (_subscribed)
|
||||
return;
|
||||
MessageService.MessageAdded += OnMessageAdded;
|
||||
_subscribed = true;
|
||||
}
|
||||
|
||||
private static void Unsubscribe()
|
||||
{
|
||||
if (!_subscribed)
|
||||
return;
|
||||
MessageService.MessageAdded -= OnMessageAdded;
|
||||
_subscribed = false;
|
||||
lock (PendingLock)
|
||||
PendingMessages.Clear();
|
||||
}
|
||||
|
||||
private static void OnMessageAdded(object sender, CommunicationMessage message)
|
||||
{
|
||||
if (!_subscribed || message == null)
|
||||
return;
|
||||
|
||||
lock (PendingLock)
|
||||
PendingMessages.Enqueue(message);
|
||||
|
||||
_panel?.Repaint();
|
||||
}
|
||||
|
||||
/// <summary>定时批量刷新 UI,避免每条报文都抢占渲染线程。</summary>
|
||||
private static void FlushPendingBatch()
|
||||
{
|
||||
if (_paused)
|
||||
return;
|
||||
|
||||
List<CommunicationMessage> batch = null;
|
||||
lock (PendingLock)
|
||||
{
|
||||
if (PendingMessages.Count == 0)
|
||||
return;
|
||||
|
||||
int count = Math.Min(UiBatchSize, PendingMessages.Count);
|
||||
batch = new List<CommunicationMessage>(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 static void InsertMessageAtTop(CommunicationMessage msg)
|
||||
{
|
||||
_displayMessages.Insert(0, msg);
|
||||
while (_displayMessages.Count > MaxDisplayRows)
|
||||
_displayMessages.RemoveAt(_displayMessages.Count - 1);
|
||||
|
||||
if (_selectedRowIndex >= 0)
|
||||
_selectedRowIndex++;
|
||||
}
|
||||
|
||||
private static void ReloadFromService()
|
||||
{
|
||||
try
|
||||
{
|
||||
InitializeForm();
|
||||
LoadMessages();
|
||||
var filter = SelectedIpFilter();
|
||||
var messages = string.IsNullOrEmpty(filter) || filter == "全部"
|
||||
? MessageService.GetAllMessages()
|
||||
: MessageService.GetMessagesByIp(filter);
|
||||
|
||||
// 标记窗体已加载完成
|
||||
isFormLoaded = true;
|
||||
uiFlushTimer.Start();
|
||||
statsRefreshTimer.Start();
|
||||
|
||||
// 在窗体加载完成后再订阅报文添加事件(避免在初始化期间触发)
|
||||
messageService.MessageAdded += OnMessageAdded;
|
||||
_displayMessages = messages.Take(MaxDisplayRows).ToList();
|
||||
_selectedRowIndex = -1;
|
||||
_parsedText = "";
|
||||
_pendingStatsRefresh = false;
|
||||
UpdateStatistics(_displayMessages.Count);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"窗体加载失败: {ex.Message}\r\n{ex.StackTrace}", "错误",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
_statsText = $"加载报文失败: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeForm()
|
||||
{
|
||||
this.Text = "通讯监控";
|
||||
this.Size = new Size(1400, 800);
|
||||
this.StartPosition = FormStartPosition.CenterScreen;
|
||||
this.MinimumSize = new Size(1200, 600);
|
||||
|
||||
// 初始化IP筛选下拉框
|
||||
RefreshIpFilter();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新IP筛选下拉框
|
||||
/// </summary>
|
||||
private void RefreshIpFilter()
|
||||
private static void RefreshIpFilter()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (cmbIpFilter == null || messageService == null)
|
||||
return;
|
||||
var selectedIp = SelectedIpFilter();
|
||||
var options = new List<string> { "全部" };
|
||||
|
||||
var selectedIp = cmbIpFilter.SelectedItem?.ToString();
|
||||
|
||||
cmbIpFilter.Items.Clear();
|
||||
cmbIpFilter.Items.Add("全部");
|
||||
|
||||
var ipAddresses = messageService.GetUniqueIpAddresses();
|
||||
var ipAddresses = MessageService.GetUniqueIpAddresses();
|
||||
if (ipAddresses != null)
|
||||
{
|
||||
foreach (var ip in ipAddresses)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(ip))
|
||||
{
|
||||
cmbIpFilter.Items.Add(ip);
|
||||
}
|
||||
options.Add(ip);
|
||||
}
|
||||
}
|
||||
|
||||
// 恢复选中项
|
||||
if (!string.IsNullOrEmpty(selectedIp) && cmbIpFilter.Items.Contains(selectedIp))
|
||||
_ipOptions = options.ToArray();
|
||||
|
||||
if (!string.IsNullOrEmpty(selectedIp))
|
||||
{
|
||||
cmbIpFilter.SelectedItem = selectedIp;
|
||||
var idx = Array.IndexOf(_ipOptions, selectedIp);
|
||||
_selectedIpIndex = idx >= 0 ? idx : 0;
|
||||
}
|
||||
else if (cmbIpFilter.Items.Count > 0)
|
||||
else
|
||||
{
|
||||
cmbIpFilter.SelectedIndex = 0;
|
||||
_selectedIpIndex = 0;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -112,248 +302,83 @@ namespace StandardScene.Charge
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载报文列表
|
||||
/// </summary>
|
||||
private void LoadMessages()
|
||||
private static void EnsureIpInFilter(string ipAddress)
|
||||
{
|
||||
var layoutSuspended = false;
|
||||
try
|
||||
{
|
||||
if (dgvMessages == null|| isFormMessageStop)
|
||||
return;
|
||||
|
||||
var selectedIp = cmbIpFilter?.SelectedItem?.ToString();
|
||||
var messages = string.IsNullOrEmpty(selectedIp) || selectedIp == "全部"
|
||||
? messageService.GetAllMessages()
|
||||
: messageService.GetMessagesByIp(selectedIp);
|
||||
|
||||
dgvMessages.SuspendLayout();
|
||||
layoutSuspended = true;
|
||||
dgvMessages.Rows.Clear();
|
||||
|
||||
foreach (var msg in messages)
|
||||
{
|
||||
AddMessageRow(msg, false);
|
||||
}
|
||||
|
||||
UpdateStatistics(messages.Count);
|
||||
pendingStatsRefresh = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"加载报文失败: {ex.Message}", "错误",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (layoutSuspended && dgvMessages != null)
|
||||
{
|
||||
dgvMessages.ResumeLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 定时批量刷新UI,避免每条报文都抢占UI线程
|
||||
/// </summary>
|
||||
private void UiFlushTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
if (!isFormLoaded || isFormMessageStop)
|
||||
if (string.IsNullOrWhiteSpace(ipAddress))
|
||||
return;
|
||||
|
||||
List<CommunicationMessage> batch = null;
|
||||
lock (pendingMessagesLock)
|
||||
{
|
||||
if (pendingMessages.Count == 0)
|
||||
return;
|
||||
|
||||
int count = Math.Min(UiBatchSize, pendingMessages.Count);
|
||||
batch = new List<CommunicationMessage>(count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
batch.Add(pendingMessages.Dequeue());
|
||||
}
|
||||
}
|
||||
|
||||
if (batch == null || batch.Count == 0)
|
||||
if (_ipOptions.Contains(ipAddress))
|
||||
return;
|
||||
|
||||
dgvMessages.SuspendLayout();
|
||||
try
|
||||
{
|
||||
var selectedIp = cmbIpFilter?.SelectedItem?.ToString();
|
||||
bool displayChanged = false;
|
||||
|
||||
foreach (var message in batch)
|
||||
{
|
||||
EnsureIpInFilter(message.IpAddress);
|
||||
if (string.IsNullOrEmpty(selectedIp) || selectedIp == "全部" || selectedIp == message.IpAddress)
|
||||
{
|
||||
AddMessageRow(message, true);
|
||||
displayChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (displayChanged)
|
||||
{
|
||||
RequestStatisticsRefresh(dgvMessages.Rows.Count);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
dgvMessages.ResumeLayout();
|
||||
}
|
||||
var list = _ipOptions.ToList();
|
||||
list.Add(ipAddress);
|
||||
_ipOptions = list.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 统计信息低频刷新(500ms)
|
||||
/// </summary>
|
||||
private void StatsRefreshTimer_Tick(object sender, EventArgs e)
|
||||
private static string SelectedIpFilter()
|
||||
{
|
||||
if (!isFormLoaded || isFormMessageStop || !pendingStatsRefresh)
|
||||
if (_ipOptions == null || _ipOptions.Length == 0)
|
||||
return "全部";
|
||||
if (_selectedIpIndex < 0 || _selectedIpIndex >= _ipOptions.Length)
|
||||
return "全部";
|
||||
return _ipOptions[_selectedIpIndex];
|
||||
}
|
||||
|
||||
private static void RequestStatisticsRefresh()
|
||||
{
|
||||
_pendingStatsRefresh = true;
|
||||
}
|
||||
|
||||
/// <summary>统计信息低频刷新(500ms)。</summary>
|
||||
private static void MaybeRefreshStatistics()
|
||||
{
|
||||
if (!_pendingStatsRefresh)
|
||||
return;
|
||||
if (DateTime.Now - _lastStatsRefresh < TimeSpan.FromMilliseconds(StatsRefreshMs))
|
||||
return;
|
||||
|
||||
pendingStatsRefresh = false;
|
||||
UpdateStatistics(lastDisplayCountForStats);
|
||||
_pendingStatsRefresh = false;
|
||||
_lastStatsRefresh = DateTime.Now;
|
||||
UpdateStatistics(_displayMessages.Count);
|
||||
}
|
||||
|
||||
private void RequestStatisticsRefresh(int displayCount)
|
||||
{
|
||||
lastDisplayCountForStats = displayCount;
|
||||
pendingStatsRefresh = true;
|
||||
}
|
||||
|
||||
private void EnsureIpInFilter(string ipAddress)
|
||||
{
|
||||
if (cmbIpFilter == null || string.IsNullOrWhiteSpace(ipAddress))
|
||||
return;
|
||||
|
||||
if (!cmbIpFilter.Items.Contains(ipAddress))
|
||||
{
|
||||
cmbIpFilter.Items.Add(ipAddress);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向表格新增一条报文行(支持头部插入)
|
||||
/// </summary>
|
||||
private void AddMessageRow(CommunicationMessage msg, bool insertAtTop = true)
|
||||
{
|
||||
if (msg == null || dgvMessages == null)
|
||||
return;
|
||||
|
||||
DataGridViewRow row;
|
||||
if (insertAtTop)
|
||||
{
|
||||
dgvMessages.Rows.Insert(0,
|
||||
msg.Timestamp.ToString("HH:mm:ss.fff"),
|
||||
msg.Direction == MessageDirection.Send ? "发送" : "接收",
|
||||
msg.IpAddress,
|
||||
msg.Port,
|
||||
msg.Length,
|
||||
msg.RawData,
|
||||
msg.StationId ?? "-",
|
||||
msg.Type);
|
||||
row = dgvMessages.Rows[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
var index = dgvMessages.Rows.Add(
|
||||
msg.Timestamp.ToString("HH:mm:ss.fff"),
|
||||
msg.Direction == MessageDirection.Send ? "发送" : "接收",
|
||||
msg.IpAddress,
|
||||
msg.Port,
|
||||
msg.Length,
|
||||
msg.RawData,
|
||||
msg.StationId ?? "-",
|
||||
msg.Type);
|
||||
row = dgvMessages.Rows[index];
|
||||
}
|
||||
|
||||
if (msg.Direction == MessageDirection.Send)
|
||||
{
|
||||
row.DefaultCellStyle.BackColor = Color.FromArgb(232, 245, 233);
|
||||
row.DefaultCellStyle.ForeColor = Color.FromArgb(46, 125, 50);
|
||||
}
|
||||
else
|
||||
{
|
||||
row.DefaultCellStyle.BackColor = Color.FromArgb(227, 242, 253);
|
||||
row.DefaultCellStyle.ForeColor = Color.FromArgb(13, 71, 161);
|
||||
}
|
||||
|
||||
while (dgvMessages.Rows.Count > MaxDisplayRows)
|
||||
{
|
||||
dgvMessages.Rows.RemoveAt(dgvMessages.Rows.Count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新统计信息
|
||||
/// </summary>
|
||||
private void UpdateStatistics(int displayCount)
|
||||
private static void UpdateStatistics(int displayCount)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (lblStatistics == null || messageService == null)
|
||||
return;
|
||||
|
||||
var allMessages = messageService.GetAllMessages();
|
||||
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);
|
||||
|
||||
lblStatistics.Text = $"显示: {displayCount} | 总数: {allMessages.Count} | 发送: {sendCount} | 接收: {receiveCount}";
|
||||
_statsText = $"显示: {displayCount} | 总数: {allMessages.Count} | 发送: {sendCount} | 接收: {receiveCount}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"更新统计信息失败: {ex.Message}");
|
||||
if (lblStatistics != null)
|
||||
{
|
||||
lblStatistics.Text = "统计信息加载失败";
|
||||
}
|
||||
_statsText = "统计信息加载失败";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新报文添加事件处理(线程安全)
|
||||
/// </summary>
|
||||
private void OnMessageAdded(object sender, CommunicationMessage message)
|
||||
private static string TruncateRawData(string rawData, int maxLen = 48)
|
||||
{
|
||||
// 如果窗体还未加载完成,忽略此事件
|
||||
if (!isFormLoaded || isFormMessageStop)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (message == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
lock (pendingMessagesLock)
|
||||
{
|
||||
pendingMessages.Enqueue(message);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"处理新报文失败: {ex.Message}");
|
||||
}
|
||||
if (string.IsNullOrEmpty(rawData))
|
||||
return "";
|
||||
return rawData.Length <= maxLen ? rawData : rawData.Substring(0, maxLen) + "…";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析报文数据
|
||||
/// </summary>
|
||||
private void ParseMessage(CommunicationMessage message)
|
||||
private static string BuildParsedText(CommunicationMessage message)
|
||||
{
|
||||
if (message == null || txtParsedData == null)
|
||||
return;
|
||||
if (message == null)
|
||||
return "";
|
||||
|
||||
try
|
||||
{
|
||||
var parsed = new System.Text.StringBuilder();
|
||||
var parsed = new StringBuilder();
|
||||
parsed.AppendLine("=== 报文解析 ===");
|
||||
parsed.AppendLine($"时间: {message.Timestamp:yyyy-MM-dd HH:mm:ss.fff}");
|
||||
parsed.AppendLine($"方向: {(message.Direction == MessageDirection.Send ? "发送" : "接收")}");
|
||||
@@ -363,199 +388,43 @@ namespace StandardScene.Charge
|
||||
parsed.AppendLine();
|
||||
parsed.AppendLine("=== 原始数据 (HEX) ===");
|
||||
parsed.AppendLine(message.RawData);
|
||||
// parsed.AppendLine(FormatHexString(message.RawData));
|
||||
parsed.AppendLine();
|
||||
parsed.AppendLine("=== 数据解析 ===");
|
||||
|
||||
// TODO: 根据实际协议进行解析
|
||||
parsed.AppendLine();
|
||||
if (message.Direction== MessageDirection.Send)
|
||||
if (message.Direction == MessageDirection.Send)
|
||||
{
|
||||
var sendDate = messageService.ParseSendRawData(message.RawData, message.Type);
|
||||
var sendData = MessageService.ParseSendRawData(message.RawData, message.Type);
|
||||
parsed.AppendLine("示例解析:");
|
||||
parsed.AppendLine($"充电指令:{sendDate.ChargeCommand}");
|
||||
parsed.AppendLine($"发送电压:{sendDate.SetVoltage}");
|
||||
parsed.AppendLine($"发送电流:{sendDate.SetCurrent}");
|
||||
parsed.AppendLine($"车辆ID:{sendDate.CurrentVehicleId}");
|
||||
parsed.AppendLine($"车辆电量:{sendDate.BatteryLevel}");
|
||||
parsed.AppendLine($"车辆电压:{sendDate.CarVoltage}");
|
||||
parsed.AppendLine($"车辆电流:{sendDate.CarCurrent}");
|
||||
|
||||
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 recDate = messageService.ParseReceiveRawData(message.RawData, message.Type);
|
||||
string mechanismStatus = (int)recDate.MechanismStatus == 1 ? "伸出" : (int)recDate.MechanismStatus == 2 ? "缩回" : (int)recDate.MechanismStatus == 3 ? "运动中" : recDate.MechanismStatus.ToString();
|
||||
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($"实时电压:{recDate.RealTimeVoltage}");
|
||||
parsed.AppendLine($"实时电流:{recDate.RealTimeCurrent}");
|
||||
parsed.AppendLine($"充电量: {recDate.BatteryAH}");
|
||||
parsed.AppendLine($"是否报警:{recDate.HasAlarm}");
|
||||
parsed.AppendLine($"充电状态:{recDate.Status.ToString()}");
|
||||
|
||||
parsed.AppendLine($"实时电压:{recData.RealTimeVoltage}");
|
||||
parsed.AppendLine($"实时电流:{recData.RealTimeCurrent}");
|
||||
parsed.AppendLine($"充电量: {recData.BatteryAH}");
|
||||
parsed.AppendLine($"是否报警:{recData.HasAlarm}");
|
||||
parsed.AppendLine($"充电状态:{recData.Status}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
txtParsedData.Text = parsed.ToString();
|
||||
return parsed.ToString();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
txtParsedData.Text = $"解析失败: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 格式化十六进制字符串
|
||||
/// </summary>
|
||||
private string FormatHexString(string hexData)
|
||||
{
|
||||
if (string.IsNullOrEmpty(hexData))
|
||||
return string.Empty;
|
||||
|
||||
var formatted = new System.Text.StringBuilder();
|
||||
for (int i = 0; i < hexData.Length; i += 2)
|
||||
{
|
||||
if (i > 0 && i % 32 == 0)
|
||||
formatted.AppendLine();
|
||||
else if (i > 0)
|
||||
formatted.Append(" ");
|
||||
|
||||
if (i + 1 < hexData.Length)
|
||||
formatted.Append(hexData.Substring(i, 2));
|
||||
else
|
||||
formatted.Append(hexData[i]);
|
||||
}
|
||||
return formatted.ToString();
|
||||
}
|
||||
|
||||
// ==================== 事件处理 ====================
|
||||
|
||||
private void cmbIpFilter_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
LoadMessages();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"筛选改变失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void dgvMessages_SelectionChanged(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (dgvMessages.SelectedRows.Count > 0)
|
||||
{
|
||||
var row = dgvMessages.SelectedRows[0];
|
||||
var rawData = row.Cells[5].Value?.ToString();
|
||||
var ipAddress = row.Cells[2].Value?.ToString();
|
||||
var port = int.Parse(row.Cells[3].Value?.ToString() ?? "0");
|
||||
var timeStr = row.Cells[0].Value?.ToString();
|
||||
var directionStr = row.Cells[1].Value?.ToString();
|
||||
var stationId = row.Cells[6].Value?.ToString();
|
||||
var type = row.Cells[7].Value?.ToString();
|
||||
|
||||
// 构造消息对象用于解析
|
||||
var message = new CommunicationMessage
|
||||
{
|
||||
RawData = rawData,
|
||||
IpAddress = ipAddress,
|
||||
Port = port,
|
||||
Direction = directionStr == "发送" ? MessageDirection.Send : MessageDirection.Receive,
|
||||
StationId = stationId == "-" ? null : stationId,
|
||||
Length = rawData.Split(' ')?.Length ?? 0,
|
||||
Type=type,
|
||||
|
||||
};
|
||||
|
||||
if (DateTime.TryParse(timeStr, out DateTime timestamp))
|
||||
{
|
||||
message.Timestamp = timestamp;
|
||||
}
|
||||
|
||||
ParseMessage(message);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"选择报文失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void btnRefresh_Click(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
RefreshIpFilter();
|
||||
LoadMessages();
|
||||
RequestStatisticsRefresh(dgvMessages?.Rows.Count ?? 0);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"刷新失败: {ex.Message}", "错误",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void btnClear_Click(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = MessageBox.Show(
|
||||
"确定要清空所有报文记录吗?",
|
||||
"确认清空",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Question);
|
||||
|
||||
if (result == DialogResult.Yes)
|
||||
{
|
||||
messageService.Clear();
|
||||
RefreshIpFilter();
|
||||
LoadMessages();
|
||||
if (txtParsedData != null)
|
||||
{
|
||||
txtParsedData.Clear();
|
||||
}
|
||||
dgvMessages.Rows.Clear();
|
||||
RequestStatisticsRefresh(0);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"清空报文失败: {ex.Message}", "错误",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void btnClose_Click(object sender, EventArgs e)
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
|
||||
private void CommunicationMonitorForm_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
// 取消订阅事件
|
||||
messageService.MessageAdded -= OnMessageAdded;
|
||||
uiFlushTimer.Stop();
|
||||
uiFlushTimer.Dispose();
|
||||
statsRefreshTimer.Stop();
|
||||
statsRefreshTimer.Dispose();
|
||||
}
|
||||
|
||||
private void button1_Click(object sender, EventArgs e)
|
||||
{
|
||||
isFormMessageStop = !isFormMessageStop;
|
||||
if (sender is Button pauseButton)
|
||||
{
|
||||
pauseButton.Text = isFormMessageStop ? "继续" : "暂停";
|
||||
return $"解析失败: {ex.Message}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user