Release v1.1.1.1: Anti-Theft buttons update, BMS history chg_wait column, and wait for charging status display

This commit is contained in:
2026-09-22 15:17:18 +09:00
parent df038ae86b
commit 33fee50a10
26 changed files with 1570 additions and 2643 deletions

View File

@@ -0,0 +1,369 @@
using System;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using DevExpress.XtraEditors;
using DevExpress.XtraGrid.Views.Grid;
using LFP_Manager.Function;
namespace LFP_Manager.Forms
{
public partial class fmxPacketLog : DevExpress.XtraEditors.XtraForm
{
private DataTable packetTable = null;
private int packetIndex = 0;
private int txCount = 0;
private int rxCount = 0;
private bool isCapturing = true;
private const int MAX_LOG_COUNT = 5000;
public fmxPacketLog()
{
InitializeComponent();
InitDataTable();
}
private void InitDataTable()
{
packetTable = new DataTable();
packetTable.Columns.Add("no", typeof(int));
packetTable.Columns.Add("time", typeof(string));
packetTable.Columns.Add("dir", typeof(string));
packetTable.Columns.Add("dev_id", typeof(string));
packetTable.Columns.Add("func", typeof(string));
packetTable.Columns.Add("len", typeof(int));
packetTable.Columns.Add("hex", typeof(string));
packetTable.Columns.Add("desc", typeof(string));
packetTable.Columns.Add("crc", typeof(string));
packetTable.Columns.Add("raw_bytes", typeof(object));
gcPacketLog.DataSource = packetTable;
ApplyFilter();
}
public void AddPacketLog(DateTime timestamp, bool isTx, byte[] data, string description)
{
if (!isCapturing || data == null || data.Length == 0) return;
if (this.InvokeRequired)
{
this.BeginInvoke(new Action(() => AddPacketLogRow(timestamp, isTx, data, description)));
}
else
{
AddPacketLogRow(timestamp, isTx, data, description);
}
}
private void AddPacketLogRow(DateTime timestamp, bool isTx, byte[] data, string description)
{
try
{
if (packetTable == null) return;
// Max rows management
if (packetTable.Rows.Count >= MAX_LOG_COUNT)
{
for (int i = 0; i < 500; i++)
{
if (packetTable.Rows.Count > 0)
packetTable.Rows.RemoveAt(0);
}
}
packetIndex++;
if (isTx) txCount++;
else rxCount++;
string dir = isTx ? "TX" : "RX";
string devId = data.Length > 0 ? string.Format("0x{0:X2}", data[0]) : "-";
string func = data.Length > 1 ? string.Format("0x{0:X2}", data[1]) : "-";
int len = data.Length;
StringBuilder sbHex = new StringBuilder(data.Length * 3);
for (int i = 0; i < data.Length; i++)
{
sbHex.AppendFormat("{0:X2} ", data[i]);
}
string hex = sbHex.ToString().TrimEnd();
// CRC Check
string crcResult = "OK";
if (data.Length >= 4)
{
byte[] calcCrc = csSerialCommFunction.GetCRC(data, data.Length - 2);
byte rxCrcH = data[data.Length - 2];
byte rxCrcL = data[data.Length - 1];
if (calcCrc[1] != rxCrcH || calcCrc[0] != rxCrcL)
{
crcResult = "ERR";
}
}
DataRow row = packetTable.NewRow();
row["no"] = packetIndex;
row["time"] = timestamp.ToString("HH:mm:ss.fff");
row["dir"] = dir;
row["dev_id"] = devId;
row["func"] = func;
row["len"] = len;
row["hex"] = hex;
row["desc"] = description;
row["crc"] = crcResult;
row["raw_bytes"] = data;
packetTable.Rows.Add(row);
lblStatus.Text = string.Format("Total: {0} | TX: {1} | RX: {2}", packetIndex, txCount, rxCount);
if (chkAutoScroll.Checked && gvPacketLog.RowCount > 0)
{
gvPacketLog.MoveLast();
}
}
catch
{
}
}
private void gvPacketLog_FocusedRowChanged(object sender, DevExpress.XtraGrid.Views.Base.FocusedRowChangedEventArgs e)
{
DataRow row = gvPacketLog.GetFocusedDataRow();
if (row == null)
{
txtDetail.Text = string.Empty;
return;
}
byte[] rawBytes = row["raw_bytes"] as byte[];
string dir = row["dir"].ToString();
string desc = row["desc"].ToString();
string time = row["time"].ToString();
txtDetail.Text = AnalyzePacket(rawBytes, dir, desc, time);
}
private string AnalyzePacket(byte[] data, string dir, string desc, string time)
{
if (data == null || data.Length == 0) return "No Data";
StringBuilder sb = new StringBuilder();
sb.AppendLine("================================================================================");
sb.AppendLine(string.Format("[{0}] {1} (Length: {2} bytes) - {3}", dir, desc, data.Length, time));
sb.AppendLine("================================================================================");
// 1. Raw HEX Dump formatted
sb.AppendLine("[RAW HEX DUMP]");
for (int i = 0; i < data.Length; i += 16)
{
sb.AppendFormat("{0:X4}: ", i);
int count = Math.Min(16, data.Length - i);
for (int j = 0; j < count; j++)
{
sb.AppendFormat("{0:X2} ", data[i + j]);
}
sb.AppendLine();
}
sb.AppendLine();
// 2. Modbus Protocol Interpretation
sb.AppendLine("[MODBUS FRAME INTERPRETATION]");
if (data.Length >= 2)
{
byte devId = data[0];
byte funcCode = data[1];
sb.AppendLine(string.Format(" - Device (Slave) Address : 0x{0:X2} ({0})", devId));
sb.AppendLine(string.Format(" - Function Code : 0x{0:X2} ({1})", funcCode, GetFunctionCodeName(funcCode)));
if (dir == "TX")
{
if (data.Length >= 6)
{
ushort regAddr = (ushort)((data[2] << 8) | data[3]);
ushort regCount = (ushort)((data[4] << 8) | data[5]);
sb.AppendLine(string.Format(" - Target Register Address: 0x{0:X4} ({0})", regAddr));
sb.AppendLine(string.Format(" - Register Count / Value : 0x{0:X4} ({0})", regCount));
}
}
else // RX
{
if (funcCode == 0x03 && data.Length >= 3)
{
byte byteCount = data[2];
sb.AppendLine(string.Format(" - Data Byte Count : {0} bytes ({1} registers)", byteCount, byteCount / 2));
int regIndex = 0;
for (int p = 3; p + 1 < data.Length - 2; p += 2)
{
ushort val = (ushort)((data[p] << 8) | data[p + 1]);
sb.AppendLine(string.Format(" [Reg +{0:D2}] Offset 0x{1:X2} : 0x{2:X4} ({3})", regIndex++, p - 3, val, (short)val));
}
}
else if (funcCode > 0x80)
{
byte errCode = data.Length > 2 ? data[2] : (byte)0;
sb.AppendLine(string.Format(" - Modbus Exception Code : 0x{0:X2} ({1})", errCode, GetExceptionName(errCode)));
}
}
// CRC Check
if (data.Length >= 4)
{
byte[] calcCrc = csSerialCommFunction.GetCRC(data, data.Length - 2);
byte rxCrcH = data[data.Length - 2];
byte rxCrcL = data[data.Length - 1];
bool crcOk = (calcCrc[1] == rxCrcH && calcCrc[0] == rxCrcL);
sb.AppendLine(string.Format(" - CRC16 Checksum : [H:0x{0:X2}, L:0x{1:X2}] (Calc: 0x{2:X2}{3:X2}) -> {4}",
rxCrcH, rxCrcL, calcCrc[1], calcCrc[0], crcOk ? "VALID (OK)" : "INVALID (MISMATCH)"));
}
}
return sb.ToString();
}
private string GetFunctionCodeName(byte func)
{
switch (func)
{
case 0x01: return "Read Coil Status (0x01)";
case 0x02: return "Read Input Status (0x02)";
case 0x03: return "Read Holding Registers (0x03)";
case 0x04: return "Read Input Registers (0x04)";
case 0x05: return "Force Single Coil (0x05)";
case 0x06: return "Preset Single Register (0x06)";
case 0x10: return "Preset Multiple Registers (0x10)";
default:
if (func > 0x80) return string.Format("Exception Response (0x{0:X2})", func);
return string.Format("Custom / Unknown (0x{0:X2})", func);
}
}
private string GetExceptionName(byte err)
{
switch (err)
{
case 0x01: return "Illegal Function";
case 0x02: return "Illegal Data Address";
case 0x03: return "Illegal Data Value";
case 0x04: return "Slave Device Failure";
case 0x05: return "Acknowledge";
case 0x06: return "Slave Device Busy";
default: return "Unknown Exception";
}
}
private void gvPacketLog_RowStyle(object sender, RowStyleEventArgs e)
{
if (e.RowHandle < 0) return;
DataRow row = gvPacketLog.GetDataRow(e.RowHandle);
if (row == null) return;
string dir = row["dir"].ToString();
string crc = row["crc"].ToString();
if (crc == "ERR")
{
e.Appearance.ForeColor = Color.Red;
e.Appearance.Font = new Font(e.Appearance.Font, FontStyle.Bold);
}
else if (dir == "TX")
{
e.Appearance.ForeColor = Color.DarkBlue;
}
else if (dir == "RX")
{
e.Appearance.ForeColor = Color.DarkGreen;
}
}
private void btnCapture_Click(object sender, EventArgs e)
{
isCapturing = !isCapturing;
btnCapture.Text = isCapturing ? "Pause" : "Resume";
btnCapture.Appearance.ForeColor = isCapturing ? Color.Black : Color.DarkRed;
}
private void btnClear_Click(object sender, EventArgs e)
{
packetTable?.Rows.Clear();
packetIndex = 0;
txCount = 0;
rxCount = 0;
lblStatus.Text = "Total: 0 | TX: 0 | RX: 0";
txtDetail.Text = string.Empty;
}
private void cmbFilter_SelectedIndexChanged(object sender, EventArgs e)
{
ApplyFilter();
}
private void ApplyFilter()
{
if (packetTable == null) return;
string selected = cmbFilter.Text;
DataView dv = packetTable.DefaultView;
if (selected == "TX Only")
{
dv.RowFilter = "dir = 'TX'";
}
else if (selected == "RX Only")
{
dv.RowFilter = "dir = 'RX'";
}
else if (selected == "Error Only")
{
dv.RowFilter = "crc = 'ERR'";
}
else
{
dv.RowFilter = string.Empty;
}
}
private void btnExport_Click(object sender, EventArgs e)
{
if (packetTable == null || packetTable.Rows.Count == 0)
{
MessageBox.Show("No packet logs to export.", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
SaveFileDialog sDialog = new SaveFileDialog();
sDialog.Title = "Export Packet Logs";
sDialog.DefaultExt = "xls";
sDialog.Filter = "Excel files (*.xls)|*.xls|All files (*.*)|*.*";
if (sDialog.ShowDialog() == DialogResult.OK)
{
try
{
DataTable exportTable = packetTable.DefaultView.ToTable(false, "no", "time", "dir", "dev_id", "func", "len", "hex", "desc", "crc");
csExcelExport.ExportToExcelExt(exportTable, sDialog.FileName);
MessageBox.Show("Complete Exporting Packet Log File.", "Result", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
private void fmxPacketLog_FormClosing(object sender, FormClosingEventArgs e)
{
// Just hide on close so it can be re-opened quickly
if (e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
this.Hide();
}
}
}
}