Files
StandardSence/StandardScene.Core/Charge/CommunicationMonitorForm.cs
T
2026-06-14 11:19:15 +08:00

562 lines
20 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace StandardScene.Charge
{
/// <summary>
/// 通讯监控窗体
/// </summary>
public partial class CommunicationMonitorForm : Form
{
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;
// 订阅窗体关闭事件
this.FormClosing += CommunicationMonitorForm_FormClosing;
}
private void CommunicationMonitorForm_Load(object sender, EventArgs e)
{
try
{
InitializeForm();
LoadMessages();
// 标记窗体已加载完成
isFormLoaded = true;
uiFlushTimer.Start();
statsRefreshTimer.Start();
// 在窗体加载完成后再订阅报文添加事件(避免在初始化期间触发)
messageService.MessageAdded += OnMessageAdded;
}
catch (Exception ex)
{
MessageBox.Show($"窗体加载失败: {ex.Message}\r\n{ex.StackTrace}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
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()
{
try
{
if (cmbIpFilter == null || messageService == null)
return;
var selectedIp = cmbIpFilter.SelectedItem?.ToString();
cmbIpFilter.Items.Clear();
cmbIpFilter.Items.Add("全部");
var ipAddresses = messageService.GetUniqueIpAddresses();
if (ipAddresses != null)
{
foreach (var ip in ipAddresses)
{
if (!string.IsNullOrEmpty(ip))
{
cmbIpFilter.Items.Add(ip);
}
}
}
// 恢复选中项
if (!string.IsNullOrEmpty(selectedIp) && cmbIpFilter.Items.Contains(selectedIp))
{
cmbIpFilter.SelectedItem = selectedIp;
}
else if (cmbIpFilter.Items.Count > 0)
{
cmbIpFilter.SelectedIndex = 0;
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"刷新IP筛选失败: {ex.Message}");
}
}
/// <summary>
/// 加载报文列表
/// </summary>
private void LoadMessages()
{
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)
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)
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();
}
}
/// <summary>
/// 统计信息低频刷新(500ms
/// </summary>
private void StatsRefreshTimer_Tick(object sender, EventArgs e)
{
if (!isFormLoaded || isFormMessageStop || !pendingStatsRefresh)
return;
pendingStatsRefresh = false;
UpdateStatistics(lastDisplayCountForStats);
}
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)
{
try
{
if (lblStatistics == null || messageService == null)
return;
var allMessages = messageService.GetAllMessages();
if (allMessages == null)
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}";
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"更新统计信息失败: {ex.Message}");
if (lblStatistics != null)
{
lblStatistics.Text = "统计信息加载失败";
}
}
}
/// <summary>
/// 新报文添加事件处理(线程安全)
/// </summary>
private void OnMessageAdded(object sender, CommunicationMessage message)
{
// 如果窗体还未加载完成,忽略此事件
if (!isFormLoaded || isFormMessageStop)
return;
try
{
if (message == null)
{
return;
}
lock (pendingMessagesLock)
{
pendingMessages.Enqueue(message);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"处理新报文失败: {ex.Message}");
}
}
/// <summary>
/// 解析报文数据
/// </summary>
private void ParseMessage(CommunicationMessage message)
{
if (message == null || txtParsedData == null)
return;
try
{
var parsed = new System.Text.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(FormatHexString(message.RawData));
parsed.AppendLine();
parsed.AppendLine("=== 数据解析 ===");
// TODO: 根据实际协议进行解析
parsed.AppendLine();
if (message.Direction== MessageDirection.Send)
{
var sendDate = 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}");
}
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();
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()}");
}
txtParsedData.Text = 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 ? "继续" : "暂停";
}
}
}
}