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

@@ -55,15 +55,15 @@
this.layoutControl1.Name = "layoutControl1";
this.layoutControl1.OptionsCustomizationForm.DesignTimeCustomizationFormPositionAndSize = new System.Drawing.Rectangle(1265, 291, 250, 350);
this.layoutControl1.Root = this.layoutControlGroup1;
this.layoutControl1.Size = new System.Drawing.Size(814, 444);
this.layoutControl1.Size = new System.Drawing.Size(814, 457);
this.layoutControl1.TabIndex = 0;
this.layoutControl1.Text = "layoutControl1";
//
// btnClose
//
this.btnClose.Location = new System.Drawing.Point(714, 403);
this.btnClose.Location = new System.Drawing.Point(714, 415);
this.btnClose.Name = "btnClose";
this.btnClose.Size = new System.Drawing.Size(96, 37);
this.btnClose.Size = new System.Drawing.Size(96, 38);
this.btnClose.StyleController = this.layoutControl1;
this.btnClose.TabIndex = 5;
this.btnClose.Text = "Close";
@@ -80,17 +80,17 @@
this.emptySpaceItem1});
this.layoutControlGroup1.Name = "Root";
this.layoutControlGroup1.Padding = new DevExpress.XtraLayout.Utils.Padding(2, 2, 2, 2);
this.layoutControlGroup1.Size = new System.Drawing.Size(814, 444);
this.layoutControlGroup1.Size = new System.Drawing.Size(814, 457);
this.layoutControlGroup1.TextVisible = false;
//
// layoutControlItem2
//
this.layoutControlItem2.Control = this.btnClose;
this.layoutControlItem2.CustomizationFormText = "layoutControlItem2";
this.layoutControlItem2.Location = new System.Drawing.Point(710, 399);
this.layoutControlItem2.Location = new System.Drawing.Point(710, 411);
this.layoutControlItem2.MinSize = new System.Drawing.Size(93, 26);
this.layoutControlItem2.Name = "layoutControlItem2";
this.layoutControlItem2.Size = new System.Drawing.Size(100, 41);
this.layoutControlItem2.Size = new System.Drawing.Size(100, 42);
this.layoutControlItem2.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom;
this.layoutControlItem2.TextSize = new System.Drawing.Size(0, 0);
this.layoutControlItem2.TextVisible = false;
@@ -99,9 +99,9 @@
//
this.emptySpaceItem1.AllowHotTrack = false;
this.emptySpaceItem1.CustomizationFormText = "emptySpaceItem1";
this.emptySpaceItem1.Location = new System.Drawing.Point(0, 399);
this.emptySpaceItem1.Location = new System.Drawing.Point(0, 411);
this.emptySpaceItem1.Name = "emptySpaceItem1";
this.emptySpaceItem1.Size = new System.Drawing.Size(710, 41);
this.emptySpaceItem1.Size = new System.Drawing.Size(710, 42);
this.emptySpaceItem1.TextSize = new System.Drawing.Size(0, 0);
//
// tmrDisplay
@@ -113,7 +113,7 @@
//
this.ucCalibrationConfig.Location = new System.Drawing.Point(4, 4);
this.ucCalibrationConfig.Name = "ucCalibrationConfig";
this.ucCalibrationConfig.Size = new System.Drawing.Size(806, 395);
this.ucCalibrationConfig.Size = new System.Drawing.Size(806, 407);
this.ucCalibrationConfig.TabIndex = 4;
//
// layoutControlItem1
@@ -123,7 +123,7 @@
this.layoutControlItem1.Location = new System.Drawing.Point(0, 0);
this.layoutControlItem1.MinSize = new System.Drawing.Size(104, 24);
this.layoutControlItem1.Name = "layoutControlItem1";
this.layoutControlItem1.Size = new System.Drawing.Size(810, 399);
this.layoutControlItem1.Size = new System.Drawing.Size(810, 411);
this.layoutControlItem1.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom;
this.layoutControlItem1.TextSize = new System.Drawing.Size(0, 0);
this.layoutControlItem1.TextVisible = false;
@@ -132,7 +132,7 @@
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 14F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(814, 444);
this.ClientSize = new System.Drawing.Size(814, 457);
this.Controls.Add(this.layoutControl1);
this.IconOptions.Icon = ((System.Drawing.Icon)(resources.GetObject("fmxCalibration.IconOptions.Icon")));
this.Name = "fmxCalibration";

View File

@@ -1,4 +1,4 @@
namespace LFP_Manager.Forms
namespace LFP_Manager.Forms
{
partial class fmxHistory
{
@@ -44,6 +44,7 @@
this.gvBmsHistory = new DevExpress.XtraGrid.Views.Grid.GridView();
this.gcHistDateTime = new DevExpress.XtraGrid.Columns.GridColumn();
this.gcStatus = new DevExpress.XtraGrid.Columns.GridColumn();
this.gcChgWait = new DevExpress.XtraGrid.Columns.GridColumn();
this.gcWarning = new DevExpress.XtraGrid.Columns.GridColumn();
this.gcProtect = new DevExpress.XtraGrid.Columns.GridColumn();
this.gcError = new DevExpress.XtraGrid.Columns.GridColumn();
@@ -239,6 +240,7 @@
this.gcNo,
this.gcHistDateTime,
this.gcStatus,
this.gcChgWait,
this.gcWarning,
this.gcProtect,
this.gcError,
@@ -298,6 +300,18 @@
this.gcStatus.Visible = true;
this.gcStatus.VisibleIndex = 2;
//
// gcChgWait
//
this.gcChgWait.AppearanceCell.Options.UseTextOptions = true;
this.gcChgWait.AppearanceCell.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Center;
this.gcChgWait.AppearanceHeader.Options.UseTextOptions = true;
this.gcChgWait.AppearanceHeader.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Center;
this.gcChgWait.Caption = "Chg Wait Status";
this.gcChgWait.FieldName = "chg_wait";
this.gcChgWait.Name = "gcChgWait";
this.gcChgWait.Visible = true;
this.gcChgWait.VisibleIndex = 3;
//
// gcWarning
//
this.gcWarning.AppearanceCell.Options.UseTextOptions = true;
@@ -308,7 +322,7 @@
this.gcWarning.FieldName = "warning";
this.gcWarning.Name = "gcWarning";
this.gcWarning.Visible = true;
this.gcWarning.VisibleIndex = 3;
this.gcWarning.VisibleIndex = 4;
//
// gcProtect
//
@@ -320,7 +334,7 @@
this.gcProtect.FieldName = "protect";
this.gcProtect.Name = "gcProtect";
this.gcProtect.Visible = true;
this.gcProtect.VisibleIndex = 4;
this.gcProtect.VisibleIndex = 5;
//
// gcError
//
@@ -332,7 +346,7 @@
this.gcError.FieldName = "error";
this.gcError.Name = "gcError";
this.gcError.Visible = true;
this.gcError.VisibleIndex = 5;
this.gcError.VisibleIndex = 6;
//
// gcVoltage
//
@@ -346,7 +360,7 @@
this.gcVoltage.FieldName = "voltage";
this.gcVoltage.Name = "gcVoltage";
this.gcVoltage.Visible = true;
this.gcVoltage.VisibleIndex = 6;
this.gcVoltage.VisibleIndex = 7;
//
// gcCurrent
//
@@ -360,7 +374,7 @@
this.gcCurrent.FieldName = "current";
this.gcCurrent.Name = "gcCurrent";
this.gcCurrent.Visible = true;
this.gcCurrent.VisibleIndex = 7;
this.gcCurrent.VisibleIndex = 8;
//
// gcSOC
//
@@ -373,7 +387,7 @@
this.gcSOC.FieldName = "SOC";
this.gcSOC.Name = "gcSOC";
this.gcSOC.Visible = true;
this.gcSOC.VisibleIndex = 8;
this.gcSOC.VisibleIndex = 9;
//
// gcSOH
//
@@ -386,7 +400,7 @@
this.gcSOH.FieldName = "SOH";
this.gcSOH.Name = "gcSOH";
this.gcSOH.Visible = true;
this.gcSOH.VisibleIndex = 9;
this.gcSOH.VisibleIndex = 10;
//
// gcTemp1
//
@@ -898,6 +912,7 @@
private DevExpress.XtraLayout.LayoutControlGroup layoutControlGroup4;
private DevExpress.XtraGrid.Columns.GridColumn gcHistDateTime;
private DevExpress.XtraGrid.Columns.GridColumn gcStatus;
private DevExpress.XtraGrid.Columns.GridColumn gcChgWait;
private DevExpress.XtraGrid.Columns.GridColumn gcWarning;
private DevExpress.XtraGrid.Columns.GridColumn gcProtect;
private DevExpress.XtraGrid.Columns.GridColumn gcError;

View File

@@ -103,6 +103,7 @@ namespace LFP_Manager.Forms
hDT.Columns.Add("no", typeof(int));
hDT.Columns.Add("datetime", typeof(string));
hDT.Columns.Add("status", typeof(int));
hDT.Columns.Add("chg_wait", typeof(int));
hDT.Columns.Add("warning", typeof(short));
hDT.Columns.Add("protect", typeof(short));
hDT.Columns.Add("error", typeof(short));
@@ -116,7 +117,7 @@ namespace LFP_Manager.Forms
{
hDT.Columns.Add(string.Format("temp_{0:00}", i + 1), typeof(float));
}
for (int i = 0; i < 16; i++)
for (int i = 0; i < csConstData.SystemInfo.MAX_MODULE_CELL_SIZE; i++)
{
hDT.Columns.Add(string.Format("cell_v_{0:00}", i + 1), typeof(float));
}
@@ -146,8 +147,8 @@ namespace LFP_Manager.Forms
{
if (bms_history_req == false || data == null) return;
// BMS History response packet: Addr(1) + Func(1) + ByteCount(1 = 58) + Data(58) + CRC(2) = 63 bytes
if (data.Length >= 63 && data[1] == 0x03 && data[2] == 58)
// BMS History response packet: Addr(1) + Func(1) + ByteCount(1 = 56) + Data(56) + CRC(2) = 61 bytes
if (data.Length >= 61 && data[1] == 0x03 && data[2] == 56)
{
try
{
@@ -184,7 +185,9 @@ namespace LFP_Manager.Forms
hRow["datetime"] = stDateTime;
pos = 2;
hRow["status"] = shorts[pos++];
short rawStatus = shorts[pos++];
hRow["status"] = (int)(rawStatus & 0x7FFF);
hRow["chg_wait"] = ((rawStatus & 0x8000) != 0) ? 1 : 0;
hRow["warning"] = shorts[pos++];
hRow["protect"] = shorts[pos++];
hRow["error"] = shorts[pos++];
@@ -192,14 +195,14 @@ namespace LFP_Manager.Forms
hRow["current"] = string.Format("{0:0.00}", Convert.ToDouble(shorts[pos++]) / 100);
hRow["SOC"] = string.Format("{0:0.0}", Convert.ToDouble(shorts[pos++]) / 1);
// Temperature
// Temperature (3 registers = 6 temps)
for (int i = 0; i < 3; i++)
{
hRow[string.Format("temp_{0:00}", (i * 2) + 1)] = Convert.ToDouble((byte)(shorts[pos] >> 8));
hRow[string.Format("temp_{0:00}", (i * 2) + 2)] = Convert.ToDouble((byte)(shorts[pos++] >> 0));
}
// Cell Voltage
for (int i = 0; i < 16; i++)
// Cell Voltage (15 cells)
for (int i = 0; i < csConstData.SystemInfo.MAX_MODULE_CELL_SIZE; i++)
{
hRow[string.Format("cell_v_{0:00}", i + 1)] = Convert.ToDouble(shorts[pos++]) / 1000;
}
@@ -278,14 +281,14 @@ namespace LFP_Manager.Forms
sdata[2] = (byte)(addr >> 8); // Address H
sdata[3] = (byte)(addr >> 0); // Address L
sdata[4] = 0x00; // Number of Register H
sdata[5] = 0x1D; // Number of Register L
sdata[5] = 0x1C; // Number of Register L (28 registers = 56 bytes)
crc = csSerialCommFunction.GetCRC(sdata, 6);
sdata[6] = crc[1]; // CRCH
sdata[7] = crc[0]; // CRCL
OnSendUartData?.Invoke(addr, sdata, false);
OnSendUartData?.Invoke(addr, sdata, true);
}
private void gvBmsHistory_CustomColumnDisplayText(object sender, DevExpress.XtraGrid.Views.Base.CustomColumnDisplayTextEventArgs e)
@@ -294,7 +297,7 @@ namespace LFP_Manager.Forms
if (e.Column.FieldName == "status")
{
int status = Convert.ToInt32(e.Value);
int status = Convert.ToInt32(e.Value) & 0x7FFF;
switch ((ushort)status)
{
case 0x0000: e.DisplayText = "STANDBY (0x0000)"; break;
@@ -304,11 +307,6 @@ namespace LFP_Manager.Forms
case 0x0004: e.DisplayText = "TRIP (0x0004)"; break;
case 0x0008: e.DisplayText = "CHG-LIMIT (0x0008)"; break;
case 0x0032: e.DisplayText = "POWER-ON (0x0032)"; break;
case 0x8000: e.DisplayText = "STANDBY-HEAT (0x8000)"; break;
case 0x8001: e.DisplayText = "CHARGING-HEAT (0x8001)"; break;
case 0x8002: e.DisplayText = "DISCHARGING-HEAT (0x8002)"; break;
case 0x8004: e.DisplayText = "TRIP-HEAT (0x8004)"; break;
case 0x8008: e.DisplayText = "CHG-LIMIT-HEAT (0x8008)"; break;
default: e.DisplayText = string.Format("UNKNOWN (0x{0:X4})", status); break;
}
}

View File

@@ -58,6 +58,8 @@ namespace LFP_Manager.Forms
this.historyViewToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.updateToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.firmwareUpdateToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.packetAnalyzerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.changePasswordToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
((System.ComponentModel.ISupportInitialize)(this.layoutControl1)).BeginInit();
@@ -311,6 +313,7 @@ namespace LFP_Manager.Forms
this.fileToolStripMenuItem,
this.historyToolStripMenuItem,
this.updateToolStripMenuItem,
this.toolsToolStripMenuItem,
this.settingsToolStripMenuItem});
this.mnMain.Location = new System.Drawing.Point(0, 0);
this.mnMain.Name = "mnMain";
@@ -363,6 +366,21 @@ namespace LFP_Manager.Forms
this.firmwareUpdateToolStripMenuItem.Text = "Firmware Update";
this.firmwareUpdateToolStripMenuItem.Click += new System.EventHandler(this.firmwareUpdateToolStripMenuItem_Click);
//
// toolsToolStripMenuItem
//
this.toolsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.packetAnalyzerToolStripMenuItem});
this.toolsToolStripMenuItem.Name = "toolsToolStripMenuItem";
this.toolsToolStripMenuItem.Size = new System.Drawing.Size(46, 20);
this.toolsToolStripMenuItem.Text = "Tools";
//
// packetAnalyzerToolStripMenuItem
//
this.packetAnalyzerToolStripMenuItem.Name = "packetAnalyzerToolStripMenuItem";
this.packetAnalyzerToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.packetAnalyzerToolStripMenuItem.Text = "Packet Analyzer";
this.packetAnalyzerToolStripMenuItem.Click += new System.EventHandler(this.packetAnalyzerToolStripMenuItem_Click);
//
// settingsToolStripMenuItem
//
this.settingsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
@@ -374,7 +392,7 @@ namespace LFP_Manager.Forms
// changePasswordToolStripMenuItem
//
this.changePasswordToolStripMenuItem.Name = "changePasswordToolStripMenuItem";
this.changePasswordToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.changePasswordToolStripMenuItem.Size = new System.Drawing.Size(168, 22);
this.changePasswordToolStripMenuItem.Text = "Change Password";
this.changePasswordToolStripMenuItem.Click += new System.EventHandler(this.changePasswordToolStripMenuItem_Click);
//
@@ -386,6 +404,7 @@ namespace LFP_Manager.Forms
this.Controls.Add(this.layoutControl1);
this.Controls.Add(this.mnMain);
this.IconOptions.Icon = ((System.Drawing.Icon)(resources.GetObject("fmxMain.IconOptions.Icon")));
this.MainMenuStrip = this.mnMain;
this.Name = "fmxMain";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "LFP SYSTEM";
@@ -443,6 +462,8 @@ namespace LFP_Manager.Forms
private System.Windows.Forms.ToolStripMenuItem historyViewToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem updateToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem firmwareUpdateToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem toolsToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem packetAnalyzerToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem settingsToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem changePasswordToolStripMenuItem;
}

View File

@@ -26,6 +26,7 @@ namespace LFP_Manager.Forms
private fmxModuleDetail ModuleDetailForm = null;
private fmxFwUpdate FwUpdateForm = null;
private fmxHistory HistoryForm = null;
private fmxPacketLog PacketLogForm = null;
// Static Variables
CommConfig Config = new CommConfig();
@@ -64,6 +65,7 @@ namespace LFP_Manager.Forms
uTarget.OnStart += StartButtonClick;
uDataLog.OnUpdate += LogStatusUpdate;
uEventLog.OnPacketAnalyzerClick += packetAnalyzerToolStripMenuItem_Click;
this.Text = String.Format("LFP SYSTEM [{0}] -- TW (for DBG)", Application.ProductVersion);
}
@@ -210,6 +212,7 @@ namespace LFP_Manager.Forms
uartThread.OnUpdate += UpdateData;
uartThread.OnDataRecv += UartRecvData;
uartThread.OnPrint += CommDataPrint;
uartThread.OnPacketLog += PacketLogUpdate;
uartThread.SetPolling(chbPolling.Checked, sId, ref SystemData);
if (uartThread.Start(ref Config, sId, chbPolling.Checked))
@@ -813,6 +816,21 @@ namespace LFP_Manager.Forms
}
}
private void packetAnalyzerToolStripMenuItem_Click(object sender, EventArgs e)
{
if (PacketLogForm == null || PacketLogForm.IsDisposed)
{
PacketLogForm = new fmxPacketLog();
}
PacketLogForm.Show();
PacketLogForm.BringToFront();
}
private void PacketLogUpdate(DateTime timestamp, bool isTx, byte[] packet, string description)
{
PacketLogForm?.AddPacketLog(timestamp, isTx, packet, description);
}
#endregion
#region FORM EVENT

354
LFP_Manager/Forms/fmxPacketLog.Designer.cs generated Normal file
View File

@@ -0,0 +1,354 @@
namespace LFP_Manager.Forms
{
partial class fmxPacketLog
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.panelTop = new DevExpress.XtraEditors.PanelControl();
this.btnCapture = new DevExpress.XtraEditors.SimpleButton();
this.btnClear = new DevExpress.XtraEditors.SimpleButton();
this.chkAutoScroll = new DevExpress.XtraEditors.CheckEdit();
this.btnExport = new DevExpress.XtraEditors.SimpleButton();
this.cmbFilter = new DevExpress.XtraEditors.ComboBoxEdit();
this.lblFilter = new DevExpress.XtraEditors.LabelControl();
this.lblStatus = new DevExpress.XtraEditors.LabelControl();
this.splitContainerControl = new DevExpress.XtraEditors.SplitContainerControl();
this.gcPacketLog = new DevExpress.XtraGrid.GridControl();
this.gvPacketLog = new DevExpress.XtraGrid.Views.Grid.GridView();
this.colNo = new DevExpress.XtraGrid.Columns.GridColumn();
this.colTime = new DevExpress.XtraGrid.Columns.GridColumn();
this.colDir = new DevExpress.XtraGrid.Columns.GridColumn();
this.colDevId = new DevExpress.XtraGrid.Columns.GridColumn();
this.colFunc = new DevExpress.XtraGrid.Columns.GridColumn();
this.colLen = new DevExpress.XtraGrid.Columns.GridColumn();
this.colHex = new DevExpress.XtraGrid.Columns.GridColumn();
this.colDesc = new DevExpress.XtraGrid.Columns.GridColumn();
this.colCrc = new DevExpress.XtraGrid.Columns.GridColumn();
this.groupDetail = new DevExpress.XtraEditors.GroupControl();
this.txtDetail = new DevExpress.XtraEditors.MemoEdit();
((System.ComponentModel.ISupportInitialize)(this.panelTop)).BeginInit();
this.panelTop.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.chkAutoScroll.Properties)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.cmbFilter.Properties)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.splitContainerControl)).BeginInit();
this.splitContainerControl.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.gcPacketLog)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.gvPacketLog)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.groupDetail)).BeginInit();
this.groupDetail.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.txtDetail.Properties)).BeginInit();
this.SuspendLayout();
//
// panelTop
//
this.panelTop.Controls.Add(this.lblStatus);
this.panelTop.Controls.Add(this.lblFilter);
this.panelTop.Controls.Add(this.cmbFilter);
this.panelTop.Controls.Add(this.btnExport);
this.panelTop.Controls.Add(this.chkAutoScroll);
this.panelTop.Controls.Add(this.btnClear);
this.panelTop.Controls.Add(this.btnCapture);
this.panelTop.Dock = System.Windows.Forms.DockStyle.Top;
this.panelTop.Location = new System.Drawing.Point(0, 0);
this.panelTop.Name = "panelTop";
this.panelTop.Size = new System.Drawing.Size(984, 46);
this.panelTop.TabIndex = 0;
//
// btnCapture
//
this.btnCapture.Location = new System.Drawing.Point(12, 10);
this.btnCapture.Name = "btnCapture";
this.btnCapture.Size = new System.Drawing.Size(85, 26);
this.btnCapture.TabIndex = 0;
this.btnCapture.Text = "Pause";
this.btnCapture.Click += new System.EventHandler(this.btnCapture_Click);
//
// btnClear
//
this.btnClear.Location = new System.Drawing.Point(103, 10);
this.btnClear.Name = "btnClear";
this.btnClear.Size = new System.Drawing.Size(75, 26);
this.btnClear.TabIndex = 1;
this.btnClear.Text = "Clear";
this.btnClear.Click += new System.EventHandler(this.btnClear_Click);
//
// chkAutoScroll
//
this.chkAutoScroll.EditValue = true;
this.chkAutoScroll.Location = new System.Drawing.Point(190, 13);
this.chkAutoScroll.Name = "chkAutoScroll";
this.chkAutoScroll.Properties.Caption = "Auto Scroll";
this.chkAutoScroll.Size = new System.Drawing.Size(85, 20);
this.chkAutoScroll.TabIndex = 2;
//
// btnExport
//
this.btnExport.Location = new System.Drawing.Point(470, 10);
this.btnExport.Name = "btnExport";
this.btnExport.Size = new System.Drawing.Size(95, 26);
this.btnExport.TabIndex = 3;
this.btnExport.Text = "Export Excel";
this.btnExport.Click += new System.EventHandler(this.btnExport_Click);
//
// cmbFilter
//
this.cmbFilter.EditValue = "ALL";
this.cmbFilter.Location = new System.Drawing.Point(340, 13);
this.cmbFilter.Name = "cmbFilter";
this.cmbFilter.Properties.Buttons.AddRange(new DevExpress.XtraEditors.Controls.EditorButton[] {
new DevExpress.XtraEditors.Controls.EditorButton(DevExpress.XtraEditors.Controls.ButtonPredefines.Combo)});
this.cmbFilter.Properties.Items.AddRange(new object[] {
"ALL",
"TX Only",
"RX Only",
"Error Only"});
this.cmbFilter.Properties.TextEditStyle = DevExpress.XtraEditors.Controls.TextEditStyles.DisableTextEditor;
this.cmbFilter.Size = new System.Drawing.Size(110, 20);
this.cmbFilter.TabIndex = 4;
this.cmbFilter.SelectedIndexChanged += new System.EventHandler(this.cmbFilter_SelectedIndexChanged);
//
// lblFilter
//
this.lblFilter.Location = new System.Drawing.Point(295, 16);
this.lblFilter.Name = "lblFilter";
this.lblFilter.Size = new System.Drawing.Size(39, 14);
this.lblFilter.TabIndex = 5;
this.lblFilter.Text = "Filter :";
//
// lblStatus
//
this.lblStatus.Appearance.Font = new System.Drawing.Font("Tahoma", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lblStatus.Appearance.Options.UseFont = true;
this.lblStatus.Location = new System.Drawing.Point(590, 16);
this.lblStatus.Name = "lblStatus";
this.lblStatus.Size = new System.Drawing.Size(200, 14);
this.lblStatus.TabIndex = 6;
this.lblStatus.Text = "Total: 0 | TX: 0 | RX: 0";
//
// splitContainerControl
//
this.splitContainerControl.Dock = System.Windows.Forms.DockStyle.Fill;
this.splitContainerControl.Horizontal = false;
this.splitContainerControl.Location = new System.Drawing.Point(0, 46);
this.splitContainerControl.Name = "splitContainerControl";
this.splitContainerControl.Panel1.Controls.Add(this.gcPacketLog);
this.splitContainerControl.Panel1.Text = "Panel1";
this.splitContainerControl.Panel2.Controls.Add(this.groupDetail);
this.splitContainerControl.Panel2.Text = "Panel2";
this.splitContainerControl.Size = new System.Drawing.Size(984, 565);
this.splitContainerControl.SplitterPosition = 370;
this.splitContainerControl.TabIndex = 1;
//
// gcPacketLog
//
this.gcPacketLog.Dock = System.Windows.Forms.DockStyle.Fill;
this.gcPacketLog.Location = new System.Drawing.Point(0, 0);
this.gcPacketLog.MainView = this.gvPacketLog;
this.gcPacketLog.Name = "gcPacketLog";
this.gcPacketLog.Size = new System.Drawing.Size(984, 370);
this.gcPacketLog.TabIndex = 0;
this.gcPacketLog.ViewCollection.AddRange(new DevExpress.XtraGrid.Views.Base.BaseView[] {
this.gvPacketLog});
//
// gvPacketLog
//
this.gvPacketLog.Columns.AddRange(new DevExpress.XtraGrid.Columns.GridColumn[] {
this.colNo,
this.colTime,
this.colDir,
this.colDevId,
this.colFunc,
this.colLen,
this.colHex,
this.colDesc,
this.colCrc});
this.gvPacketLog.GridControl = this.gcPacketLog;
this.gvPacketLog.Name = "gvPacketLog";
this.gvPacketLog.OptionsBehavior.Editable = false;
this.gvPacketLog.OptionsBehavior.ReadOnly = true;
this.gvPacketLog.OptionsSelection.EnableAppearanceFocusedCell = false;
this.gvPacketLog.OptionsView.ShowGroupPanel = false;
this.gvPacketLog.OptionsView.ShowIndicator = false;
this.gvPacketLog.RowStyle += new DevExpress.XtraGrid.Views.Grid.RowStyleEventHandler(this.gvPacketLog_RowStyle);
this.gvPacketLog.FocusedRowChanged += new DevExpress.XtraGrid.Views.Base.FocusedRowChangedEventHandler(this.gvPacketLog_FocusedRowChanged);
//
// colNo
//
this.colNo.Caption = "No";
this.colNo.FieldName = "no";
this.colNo.Name = "colNo";
this.colNo.Visible = true;
this.colNo.VisibleIndex = 0;
this.colNo.Width = 45;
//
// colTime
//
this.colTime.Caption = "Time";
this.colTime.FieldName = "time";
this.colTime.Name = "colTime";
this.colTime.Visible = true;
this.colTime.VisibleIndex = 1;
this.colTime.Width = 90;
//
// colDir
//
this.colDir.Caption = "Dir";
this.colDir.FieldName = "dir";
this.colDir.Name = "colDir";
this.colDir.Visible = true;
this.colDir.VisibleIndex = 2;
this.colDir.Width = 40;
//
// colDevId
//
this.colDevId.Caption = "ID";
this.colDevId.FieldName = "dev_id";
this.colDevId.Name = "colDevId";
this.colDevId.Visible = true;
this.colDevId.VisibleIndex = 3;
this.colDevId.Width = 40;
//
// colFunc
//
this.colFunc.Caption = "Func";
this.colFunc.FieldName = "func";
this.colFunc.Name = "colFunc";
this.colFunc.Visible = true;
this.colFunc.VisibleIndex = 4;
this.colFunc.Width = 50;
//
// colLen
//
this.colLen.Caption = "Len";
this.colLen.FieldName = "len";
this.colLen.Name = "colLen";
this.colLen.Visible = true;
this.colLen.VisibleIndex = 5;
this.colLen.Width = 40;
//
// colHex
//
this.colHex.Caption = "RAW HEX Dump";
this.colHex.FieldName = "hex";
this.colHex.Name = "colHex";
this.colHex.Visible = true;
this.colHex.VisibleIndex = 6;
this.colHex.Width = 350;
//
// colDesc
//
this.colDesc.Caption = "Description";
this.colDesc.FieldName = "desc";
this.colDesc.Name = "colDesc";
this.colDesc.Visible = true;
this.colDesc.VisibleIndex = 7;
this.colDesc.Width = 260;
//
// colCrc
//
this.colCrc.Caption = "CRC";
this.colCrc.FieldName = "crc";
this.colCrc.Name = "colCrc";
this.colCrc.Visible = true;
this.colCrc.VisibleIndex = 8;
this.colCrc.Width = 55;
//
// groupDetail
//
this.groupDetail.Controls.Add(this.txtDetail);
this.groupDetail.Dock = System.Windows.Forms.DockStyle.Fill;
this.groupDetail.Location = new System.Drawing.Point(0, 0);
this.groupDetail.Name = "groupDetail";
this.groupDetail.Size = new System.Drawing.Size(984, 185);
this.groupDetail.TabIndex = 0;
this.groupDetail.Text = "Packet Inspector & Analysis";
//
// txtDetail
//
this.txtDetail.Dock = System.Windows.Forms.DockStyle.Fill;
this.txtDetail.Location = new System.Drawing.Point(2, 23);
this.txtDetail.Name = "txtDetail";
this.txtDetail.Properties.Appearance.Font = new System.Drawing.Font("Consolas", 9.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.txtDetail.Properties.Appearance.Options.UseFont = true;
this.txtDetail.Properties.ReadOnly = true;
this.txtDetail.Size = new System.Drawing.Size(980, 160);
this.txtDetail.TabIndex = 0;
//
// fmxPacketLog
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 14F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(984, 611);
this.Controls.Add(this.splitContainerControl);
this.Controls.Add(this.panelTop);
this.IconOptions.Image = global::LFP_Manager.Properties.Resources.AMO_LOGO1;
this.Name = "fmxPacketLog";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "RS232 Packet Analyzer";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.fmxPacketLog_FormClosing);
((System.ComponentModel.ISupportInitialize)(this.panelTop)).EndInit();
this.panelTop.ResumeLayout(false);
this.panelTop.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.chkAutoScroll.Properties)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.cmbFilter.Properties)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.splitContainerControl)).EndInit();
this.splitContainerControl.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.gcPacketLog)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.gvPacketLog)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.groupDetail)).EndInit();
this.groupDetail.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.txtDetail.Properties)).EndInit();
this.ResumeLayout(false);
}
#endregion
private DevExpress.XtraEditors.PanelControl panelTop;
private DevExpress.XtraEditors.SimpleButton btnCapture;
private DevExpress.XtraEditors.SimpleButton btnClear;
private DevExpress.XtraEditors.CheckEdit chkAutoScroll;
private DevExpress.XtraEditors.SimpleButton btnExport;
private DevExpress.XtraEditors.ComboBoxEdit cmbFilter;
private DevExpress.XtraEditors.LabelControl lblFilter;
private DevExpress.XtraEditors.LabelControl lblStatus;
private DevExpress.XtraEditors.SplitContainerControl splitContainerControl;
private DevExpress.XtraGrid.GridControl gcPacketLog;
private DevExpress.XtraGrid.Views.Grid.GridView gvPacketLog;
private DevExpress.XtraGrid.Columns.GridColumn colNo;
private DevExpress.XtraGrid.Columns.GridColumn colTime;
private DevExpress.XtraGrid.Columns.GridColumn colDir;
private DevExpress.XtraGrid.Columns.GridColumn colDevId;
private DevExpress.XtraGrid.Columns.GridColumn colFunc;
private DevExpress.XtraGrid.Columns.GridColumn colLen;
private DevExpress.XtraGrid.Columns.GridColumn colHex;
private DevExpress.XtraGrid.Columns.GridColumn colDesc;
private DevExpress.XtraGrid.Columns.GridColumn colCrc;
private DevExpress.XtraEditors.GroupControl groupDetail;
private DevExpress.XtraEditors.MemoEdit txtDetail;
}
}

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();
}
}
}
}

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>