From 33fee50a10c49439d662c2cec8bf1f479f75bb8b Mon Sep 17 00:00:00 2001 From: jkwoo Date: Tue, 22 Sep 2026 15:17:18 +0900 Subject: [PATCH] Release v1.1.1.1: Anti-Theft buttons update, BMS history chg_wait column, and wait for charging status display --- .../Controls/ucCalibration.Designer.cs | 382 ++++++----- LFP_Manager/Controls/ucCalibration.cs | 99 +-- LFP_Manager/Controls/ucEventLog.Designer.cs | 70 +- LFP_Manager/Controls/ucEventLog.cs | 9 +- LFP_Manager/Controls/ucHistroy.cs | 10 +- LFP_Manager/Controls/ucShelfStatus.cs | 27 +- LFP_Manager/DataStructure/csConstData.cs | 17 +- LFP_Manager/DataStructure/csDataStructure.cs | 39 ++ LFP_Manager/Forms/fmxCalibration.Designer.cs | 22 +- LFP_Manager/Forms/fmxHistory.Designer.cs | 31 +- LFP_Manager/Forms/fmxHistory.cs | 28 +- LFP_Manager/Forms/fmxMain.Designer.cs | 23 +- LFP_Manager/Forms/fmxMain.cs | 18 + LFP_Manager/Forms/fmxPacketLog.Designer.cs | 354 ++++++++++ LFP_Manager/Forms/fmxPacketLog.cs | 369 ++++++++++ LFP_Manager/Forms/fmxPacketLog.resx | 61 ++ LFP_Manager/Function/csSerialCommFunction.cs | 3 + LFP_Manager/LFP_Manager.zip | Bin 104659 -> 0 bytes LFP_Manager/LFP_Manager_GL.csproj | 530 --------------- LFP_Manager/LFP_Manager_GL.csproj.bak | 526 -------------- LFP_Manager/LFP_Manager_JP.csproj.bak | 411 ----------- LFP_Manager/LFP_Manager_M20.csproj.bak | 508 -------------- LFP_Manager/LFP_Manager_TW_DBG.csproj | 9 + LFP_Manager/Properties/AssemblyInfo.cs | 4 +- LFP_Manager/Threads/csUartThread.cs | 643 +++++++++--------- LFP_Manager/Utils/csLog.cs | 20 +- 26 files changed, 1570 insertions(+), 2643 deletions(-) create mode 100644 LFP_Manager/Forms/fmxPacketLog.Designer.cs create mode 100644 LFP_Manager/Forms/fmxPacketLog.cs create mode 100644 LFP_Manager/Forms/fmxPacketLog.resx delete mode 100644 LFP_Manager/LFP_Manager.zip delete mode 100644 LFP_Manager/LFP_Manager_GL.csproj delete mode 100644 LFP_Manager/LFP_Manager_GL.csproj.bak delete mode 100644 LFP_Manager/LFP_Manager_JP.csproj.bak delete mode 100644 LFP_Manager/LFP_Manager_M20.csproj.bak diff --git a/LFP_Manager/Controls/ucCalibration.Designer.cs b/LFP_Manager/Controls/ucCalibration.Designer.cs index 95e9f56..ec21a66 100644 --- a/LFP_Manager/Controls/ucCalibration.Designer.cs +++ b/LFP_Manager/Controls/ucCalibration.Designer.cs @@ -1,4 +1,4 @@ -namespace LFP_Manager.Controls +namespace LFP_Manager.Controls { partial class ucCalibration { @@ -29,10 +29,19 @@ private void InitializeComponent() { this.layoutControl1 = new DevExpress.XtraLayout.LayoutControl(); + this.gbAntiTheft = new DevExpress.XtraEditors.GroupControl(); + this.layoutControl4 = new DevExpress.XtraLayout.LayoutControl(); this.TeSensitive = new DevExpress.XtraEditors.TextEdit(); - this.BtnAntiTheftEnable = new DevExpress.XtraEditors.SimpleButton(); - this.BtnClearAntiTheftProtect = new DevExpress.XtraEditors.SimpleButton(); - this.BtnAntiTheftDisable = new DevExpress.XtraEditors.SimpleButton(); + this.TeAntiTheftGyroCurr = new DevExpress.XtraEditors.TextEdit(); + this.BtnClearAlarm = new DevExpress.XtraEditors.SimpleButton(); + this.layoutControlItemClearAlarm = new DevExpress.XtraLayout.LayoutControlItem(); + this.BtnClearActivation = new DevExpress.XtraEditors.SimpleButton(); + this.layoutControlGroup4 = new DevExpress.XtraLayout.LayoutControlGroup(); + this.layoutControlItem16 = new DevExpress.XtraLayout.LayoutControlItem(); + this.layoutControlItem17 = new DevExpress.XtraLayout.LayoutControlItem(); + this.simpleLabelItemAntiTheftStatus = new DevExpress.XtraLayout.SimpleLabelItem(); + this.emptySpaceItem6 = new DevExpress.XtraLayout.EmptySpaceItem(); + this.layoutControlItemClearActivation = new DevExpress.XtraLayout.LayoutControlItem(); this.gbBmsDateTime = new DevExpress.XtraEditors.GroupControl(); this.layoutControl3 = new DevExpress.XtraLayout.LayoutControl(); this.BtnGetCurrTime = new DevExpress.XtraEditors.SimpleButton(); @@ -79,22 +88,27 @@ this.layoutControlItem2 = new DevExpress.XtraLayout.LayoutControlItem(); this.layoutControlItem3 = new DevExpress.XtraLayout.LayoutControlItem(); this.simpleLabelItem2 = new DevExpress.XtraLayout.SimpleLabelItem(); - this.TeAntiTheftGyroCurr = new DevExpress.XtraEditors.TextEdit(); this.layoutControlGroup1 = new DevExpress.XtraLayout.LayoutControlGroup(); this.layoutControlItem19 = new DevExpress.XtraLayout.LayoutControlItem(); this.layoutControlItem4 = new DevExpress.XtraLayout.LayoutControlItem(); this.layoutControlItem8 = new DevExpress.XtraLayout.LayoutControlItem(); this.emptySpaceItem5 = new DevExpress.XtraLayout.EmptySpaceItem(); - this.layoutControlGroup3 = new DevExpress.XtraLayout.LayoutControlGroup(); - this.emptySpaceItem6 = new DevExpress.XtraLayout.EmptySpaceItem(); - this.layoutControlItem17 = new DevExpress.XtraLayout.LayoutControlItem(); - this.layoutControlItem21 = new DevExpress.XtraLayout.LayoutControlItem(); - this.layoutControlItem20 = new DevExpress.XtraLayout.LayoutControlItem(); - this.layoutControlItem15 = new DevExpress.XtraLayout.LayoutControlItem(); - this.layoutControlItem16 = new DevExpress.XtraLayout.LayoutControlItem(); + this.layoutControlItemAntiTheftGroup = new DevExpress.XtraLayout.LayoutControlItem(); ((System.ComponentModel.ISupportInitialize)(this.layoutControl1)).BeginInit(); this.layoutControl1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.gbAntiTheft)).BeginInit(); + this.gbAntiTheft.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.layoutControl4)).BeginInit(); + this.layoutControl4.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.TeSensitive.Properties)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.TeAntiTheftGyroCurr.Properties)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.layoutControlGroup4)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem16)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem17)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.simpleLabelItemAntiTheftStatus)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.emptySpaceItem6)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.layoutControlItemClearAlarm)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.layoutControlItemClearActivation)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.gbBmsDateTime)).BeginInit(); this.gbBmsDateTime.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.layoutControl3)).BeginInit(); @@ -143,31 +157,20 @@ ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem2)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem3)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.simpleLabelItem2)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.TeAntiTheftGyroCurr.Properties)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlGroup1)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem19)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem4)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem8)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.emptySpaceItem5)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.layoutControlGroup3)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.emptySpaceItem6)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem17)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem21)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem20)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem15)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem16)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.layoutControlItemAntiTheftGroup)).BeginInit(); this.SuspendLayout(); // // layoutControl1 // - this.layoutControl1.Controls.Add(this.TeSensitive); - this.layoutControl1.Controls.Add(this.BtnAntiTheftEnable); - this.layoutControl1.Controls.Add(this.BtnClearAntiTheftProtect); - this.layoutControl1.Controls.Add(this.BtnAntiTheftDisable); + this.layoutControl1.Controls.Add(this.gbAntiTheft); this.layoutControl1.Controls.Add(this.gbBmsDateTime); this.layoutControl1.Controls.Add(this.gbChaModeControl); this.layoutControl1.Controls.Add(this.gbCapacityControl); - this.layoutControl1.Controls.Add(this.TeAntiTheftGyroCurr); this.layoutControl1.Dock = System.Windows.Forms.DockStyle.Fill; this.layoutControl1.Location = new System.Drawing.Point(0, 0); this.layoutControl1.Name = "layoutControl1"; @@ -177,47 +180,170 @@ this.layoutControl1.TabIndex = 0; this.layoutControl1.Text = "layoutControl1"; // + // gbAntiTheft + // + this.gbAntiTheft.Controls.Add(this.layoutControl4); + this.gbAntiTheft.Location = new System.Drawing.Point(2, 212); + this.gbAntiTheft.Name = "gbAntiTheft"; + this.gbAntiTheft.Size = new System.Drawing.Size(282, 192); + this.gbAntiTheft.TabIndex = 13; + this.gbAntiTheft.Text = "Anti-Theft"; + // + // layoutControl4 + // + this.layoutControl4.Controls.Add(this.BtnClearAlarm); + this.layoutControl4.Controls.Add(this.BtnClearActivation); + this.layoutControl4.Controls.Add(this.TeSensitive); + this.layoutControl4.Controls.Add(this.TeAntiTheftGyroCurr); + this.layoutControl4.Dock = System.Windows.Forms.DockStyle.Fill; + this.layoutControl4.Location = new System.Drawing.Point(2, 23); + this.layoutControl4.Name = "layoutControl4"; + this.layoutControl4.Root = this.layoutControlGroup4; + this.layoutControl4.Size = new System.Drawing.Size(278, 167); + this.layoutControl4.TabIndex = 0; + this.layoutControl4.Text = "layoutControl4"; + // // TeSensitive // - this.TeSensitive.Location = new System.Drawing.Point(86, 265); + this.TeSensitive.Location = new System.Drawing.Point(88, 3); this.TeSensitive.Name = "TeSensitive"; this.TeSensitive.Properties.Appearance.Font = new System.Drawing.Font("Tahoma", 11.25F); this.TeSensitive.Properties.Appearance.Options.UseFont = true; this.TeSensitive.Properties.Appearance.Options.UseTextOptions = true; this.TeSensitive.Properties.Appearance.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Far; this.TeSensitive.Properties.ReadOnly = true; - this.TeSensitive.Size = new System.Drawing.Size(194, 24); - this.TeSensitive.TabIndex = 18; + this.TeSensitive.Size = new System.Drawing.Size(187, 24); + this.TeSensitive.StyleController = this.layoutControl4; + this.TeSensitive.TabIndex = 4; // - // BtnAntiTheftEnable + // TeAntiTheftGyroCurr // - this.BtnAntiTheftEnable.Location = new System.Drawing.Point(109, 365); - this.BtnAntiTheftEnable.Name = "BtnAntiTheftEnable"; - this.BtnAntiTheftEnable.Size = new System.Drawing.Size(85, 35); - this.BtnAntiTheftEnable.StyleController = this.layoutControl1; - this.BtnAntiTheftEnable.TabIndex = 17; - this.BtnAntiTheftEnable.Text = "Enable"; - this.BtnAntiTheftEnable.Click += new System.EventHandler(this.BtnAntiTheftEnable_Click); + this.TeAntiTheftGyroCurr.Location = new System.Drawing.Point(88, 31); + this.TeAntiTheftGyroCurr.Name = "TeAntiTheftGyroCurr"; + this.TeAntiTheftGyroCurr.Properties.Appearance.Font = new System.Drawing.Font("Tahoma", 11.25F); + this.TeAntiTheftGyroCurr.Properties.Appearance.Options.UseFont = true; + this.TeAntiTheftGyroCurr.Properties.Appearance.Options.UseTextOptions = true; + this.TeAntiTheftGyroCurr.Properties.Appearance.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Far; + this.TeAntiTheftGyroCurr.Properties.ReadOnly = true; + this.TeAntiTheftGyroCurr.Size = new System.Drawing.Size(187, 24); + this.TeAntiTheftGyroCurr.StyleController = this.layoutControl4; + this.TeAntiTheftGyroCurr.TabIndex = 5; // - // BtnClearAntiTheftProtect + // BtnClearAlarm // - this.BtnClearAntiTheftProtect.Location = new System.Drawing.Point(6, 365); - this.BtnClearAntiTheftProtect.Name = "BtnClearAntiTheftProtect"; - this.BtnClearAntiTheftProtect.Size = new System.Drawing.Size(99, 35); - this.BtnClearAntiTheftProtect.StyleController = this.layoutControl1; - this.BtnClearAntiTheftProtect.TabIndex = 16; - this.BtnClearAntiTheftProtect.Text = "Clear Protect"; - this.BtnClearAntiTheftProtect.Click += new System.EventHandler(this.BtnClearAntiTheftProtect_Click); + this.BtnClearAlarm.Location = new System.Drawing.Point(3, 127); + this.BtnClearAlarm.Name = "BtnClearAlarm"; + this.BtnClearAlarm.Size = new System.Drawing.Size(133, 37); + this.BtnClearAlarm.StyleController = this.layoutControl4; + this.BtnClearAlarm.TabIndex = 6; + this.BtnClearAlarm.Text = "Clear Alarm"; + this.BtnClearAlarm.Click += new System.EventHandler(this.BtnClearAlarm_Click); // - // BtnAntiTheftDisable + // BtnClearActivation // - this.BtnAntiTheftDisable.Location = new System.Drawing.Point(198, 365); - this.BtnAntiTheftDisable.Name = "BtnAntiTheftDisable"; - this.BtnAntiTheftDisable.Size = new System.Drawing.Size(82, 35); - this.BtnAntiTheftDisable.StyleController = this.layoutControl1; - this.BtnAntiTheftDisable.TabIndex = 15; - this.BtnAntiTheftDisable.Text = "Disable"; - this.BtnAntiTheftDisable.Click += new System.EventHandler(this.BtnAntiTheftDisable_Click); + this.BtnClearActivation.Location = new System.Drawing.Point(140, 127); + this.BtnClearActivation.Name = "BtnClearActivation"; + this.BtnClearActivation.Size = new System.Drawing.Size(135, 37); + this.BtnClearActivation.StyleController = this.layoutControl4; + this.BtnClearActivation.TabIndex = 7; + this.BtnClearActivation.Text = "Clear Activation"; + this.BtnClearActivation.Click += new System.EventHandler(this.BtnClearActivation_Click); + // + // layoutControlGroup4 + // + this.layoutControlGroup4.EnableIndentsWithoutBorders = DevExpress.Utils.DefaultBoolean.True; + this.layoutControlGroup4.GroupBordersVisible = false; + this.layoutControlGroup4.Items.AddRange(new DevExpress.XtraLayout.BaseLayoutItem[] { + this.layoutControlItem16, + this.layoutControlItem17, + this.simpleLabelItemAntiTheftStatus, + this.emptySpaceItem6, + this.layoutControlItemClearAlarm, + this.layoutControlItemClearActivation}); + this.layoutControlGroup4.Name = "layoutControlGroup4"; + this.layoutControlGroup4.Padding = new DevExpress.XtraLayout.Utils.Padding(1, 1, 1, 1); + this.layoutControlGroup4.Size = new System.Drawing.Size(278, 167); + this.layoutControlGroup4.TextVisible = false; + // + // layoutControlItem16 + // + this.layoutControlItem16.AppearanceItemCaption.Options.UseTextOptions = true; + this.layoutControlItem16.AppearanceItemCaption.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Near; + this.layoutControlItem16.Control = this.TeSensitive; + this.layoutControlItem16.Location = new System.Drawing.Point(0, 0); + this.layoutControlItem16.MinSize = new System.Drawing.Size(115, 28); + this.layoutControlItem16.Name = "layoutControlItem16"; + this.layoutControlItem16.Size = new System.Drawing.Size(276, 28); + this.layoutControlItem16.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom; + this.layoutControlItem16.Text = " Sensitive"; + this.layoutControlItem16.TextAlignMode = DevExpress.XtraLayout.TextAlignModeItem.CustomSize; + this.layoutControlItem16.TextSize = new System.Drawing.Size(80, 20); + this.layoutControlItem16.TextToControlDistance = 5; + // + // layoutControlItem17 + // + this.layoutControlItem17.AppearanceItemCaption.Options.UseTextOptions = true; + this.layoutControlItem17.AppearanceItemCaption.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Near; + this.layoutControlItem17.Control = this.TeAntiTheftGyroCurr; + this.layoutControlItem17.Location = new System.Drawing.Point(0, 28); + this.layoutControlItem17.MinSize = new System.Drawing.Size(115, 28); + this.layoutControlItem17.Name = "layoutControlItem17"; + this.layoutControlItem17.Size = new System.Drawing.Size(276, 28); + this.layoutControlItem17.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom; + this.layoutControlItem17.Text = " Status"; + this.layoutControlItem17.TextAlignMode = DevExpress.XtraLayout.TextAlignModeItem.CustomSize; + this.layoutControlItem17.TextSize = new System.Drawing.Size(80, 20); + this.layoutControlItem17.TextToControlDistance = 5; + // + // simpleLabelItemAntiTheftStatus + // + this.simpleLabelItemAntiTheftStatus.AllowHotTrack = false; + this.simpleLabelItemAntiTheftStatus.AppearanceItemCaption.Font = new System.Drawing.Font("Tahoma", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.simpleLabelItemAntiTheftStatus.AppearanceItemCaption.ForeColor = System.Drawing.Color.Blue; + this.simpleLabelItemAntiTheftStatus.AppearanceItemCaption.Options.UseFont = true; + this.simpleLabelItemAntiTheftStatus.AppearanceItemCaption.Options.UseForeColor = true; + this.simpleLabelItemAntiTheftStatus.AppearanceItemCaption.Options.UseTextOptions = true; + this.simpleLabelItemAntiTheftStatus.AppearanceItemCaption.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Far; + this.simpleLabelItemAntiTheftStatus.Location = new System.Drawing.Point(0, 56); + this.simpleLabelItemAntiTheftStatus.MinSize = new System.Drawing.Size(274, 22); + this.simpleLabelItemAntiTheftStatus.Name = "simpleLabelItemAntiTheftStatus"; + this.simpleLabelItemAntiTheftStatus.Size = new System.Drawing.Size(276, 22); + this.simpleLabelItemAntiTheftStatus.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom; + this.simpleLabelItemAntiTheftStatus.Text = " Waiting for charging activation"; + this.simpleLabelItemAntiTheftStatus.TextAlignMode = DevExpress.XtraLayout.TextAlignModeItem.CustomSize; + this.simpleLabelItemAntiTheftStatus.TextSize = new System.Drawing.Size(270, 18); + // + // emptySpaceItem6 + // + this.emptySpaceItem6.AllowHotTrack = false; + this.emptySpaceItem6.Location = new System.Drawing.Point(0, 78); + this.emptySpaceItem6.MinSize = new System.Drawing.Size(104, 24); + this.emptySpaceItem6.Name = "emptySpaceItem6"; + this.emptySpaceItem6.Size = new System.Drawing.Size(276, 46); + this.emptySpaceItem6.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom; + this.emptySpaceItem6.TextSize = new System.Drawing.Size(0, 0); + // + // layoutControlItemClearAlarm + // + this.layoutControlItemClearAlarm.Control = this.BtnClearAlarm; + this.layoutControlItemClearAlarm.Location = new System.Drawing.Point(0, 124); + this.layoutControlItemClearAlarm.MinSize = new System.Drawing.Size(80, 35); + this.layoutControlItemClearAlarm.Name = "layoutControlItemClearAlarm"; + this.layoutControlItemClearAlarm.Size = new System.Drawing.Size(137, 41); + this.layoutControlItemClearAlarm.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom; + this.layoutControlItemClearAlarm.TextSize = new System.Drawing.Size(0, 0); + this.layoutControlItemClearAlarm.TextVisible = false; + // + // layoutControlItemClearActivation + // + this.layoutControlItemClearActivation.Control = this.BtnClearActivation; + this.layoutControlItemClearActivation.Location = new System.Drawing.Point(137, 124); + this.layoutControlItemClearActivation.MinSize = new System.Drawing.Size(80, 35); + this.layoutControlItemClearActivation.Name = "layoutControlItemClearActivation"; + this.layoutControlItemClearActivation.Size = new System.Drawing.Size(139, 41); + this.layoutControlItemClearActivation.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom; + this.layoutControlItemClearActivation.TextSize = new System.Drawing.Size(0, 0); + this.layoutControlItemClearActivation.TextVisible = false; // // gbBmsDateTime // @@ -770,19 +896,6 @@ this.simpleLabelItem2.Text = " %"; this.simpleLabelItem2.TextSize = new System.Drawing.Size(19, 14); // - // TeAntiTheftGyroCurr - // - this.TeAntiTheftGyroCurr.Location = new System.Drawing.Point(86, 237); - this.TeAntiTheftGyroCurr.Name = "TeAntiTheftGyroCurr"; - this.TeAntiTheftGyroCurr.Properties.Appearance.Font = new System.Drawing.Font("Tahoma", 11.25F); - this.TeAntiTheftGyroCurr.Properties.Appearance.Options.UseFont = true; - this.TeAntiTheftGyroCurr.Properties.Appearance.Options.UseTextOptions = true; - this.TeAntiTheftGyroCurr.Properties.Appearance.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Far; - this.TeAntiTheftGyroCurr.Properties.ReadOnly = true; - this.TeAntiTheftGyroCurr.Size = new System.Drawing.Size(194, 24); - this.TeAntiTheftGyroCurr.StyleController = this.layoutControl1; - this.TeAntiTheftGyroCurr.TabIndex = 13; - // // layoutControlGroup1 // this.layoutControlGroup1.CustomizationFormText = "layoutControlGroup1"; @@ -793,7 +906,7 @@ this.layoutControlItem4, this.layoutControlItem8, this.emptySpaceItem5, - this.layoutControlGroup3}); + this.layoutControlItemAntiTheftGroup}); this.layoutControlGroup1.Name = "Root"; this.layoutControlGroup1.Padding = new DevExpress.XtraLayout.Utils.Padding(0, 0, 0, 0); this.layoutControlGroup1.Size = new System.Drawing.Size(838, 406); @@ -841,90 +954,17 @@ this.emptySpaceItem5.Size = new System.Drawing.Size(552, 196); this.emptySpaceItem5.TextSize = new System.Drawing.Size(0, 0); // - // layoutControlGroup3 + // layoutControlItemAntiTheftGroup // - this.layoutControlGroup3.Items.AddRange(new DevExpress.XtraLayout.BaseLayoutItem[] { - this.emptySpaceItem6, - this.layoutControlItem17, - this.layoutControlItem21, - this.layoutControlItem20, - this.layoutControlItem15, - this.layoutControlItem16}); - this.layoutControlGroup3.Location = new System.Drawing.Point(0, 210); - this.layoutControlGroup3.Name = "layoutControlGroup3"; - this.layoutControlGroup3.Padding = new DevExpress.XtraLayout.Utils.Padding(1, 1, 1, 1); - this.layoutControlGroup3.Size = new System.Drawing.Size(286, 196); - this.layoutControlGroup3.Text = "Anti-Theft"; - // - // emptySpaceItem6 - // - this.emptySpaceItem6.AllowHotTrack = false; - this.emptySpaceItem6.Location = new System.Drawing.Point(0, 56); - this.emptySpaceItem6.Name = "emptySpaceItem6"; - this.emptySpaceItem6.Size = new System.Drawing.Size(278, 72); - this.emptySpaceItem6.TextSize = new System.Drawing.Size(0, 0); - // - // layoutControlItem17 - // - this.layoutControlItem17.Control = this.TeAntiTheftGyroCurr; - this.layoutControlItem17.ControlAlignment = System.Drawing.ContentAlignment.TopLeft; - this.layoutControlItem17.CustomizationFormText = " Gyro-Scope Curr"; - this.layoutControlItem17.Location = new System.Drawing.Point(0, 0); - this.layoutControlItem17.MaxSize = new System.Drawing.Size(0, 28); - this.layoutControlItem17.MinSize = new System.Drawing.Size(134, 28); - this.layoutControlItem17.Name = "layoutControlItem17"; - this.layoutControlItem17.Size = new System.Drawing.Size(278, 28); - this.layoutControlItem17.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom; - this.layoutControlItem17.Text = " Gyro Scope"; - this.layoutControlItem17.TextAlignMode = DevExpress.XtraLayout.TextAlignModeItem.CustomSize; - this.layoutControlItem17.TextSize = new System.Drawing.Size(75, 20); - this.layoutControlItem17.TextToControlDistance = 5; - // - // layoutControlItem21 - // - this.layoutControlItem21.Control = this.BtnAntiTheftDisable; - this.layoutControlItem21.Location = new System.Drawing.Point(192, 128); - this.layoutControlItem21.MinSize = new System.Drawing.Size(30, 26); - this.layoutControlItem21.Name = "layoutControlItem21"; - this.layoutControlItem21.Size = new System.Drawing.Size(86, 39); - this.layoutControlItem21.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom; - this.layoutControlItem21.TextSize = new System.Drawing.Size(0, 0); - this.layoutControlItem21.TextVisible = false; - // - // layoutControlItem20 - // - this.layoutControlItem20.Control = this.BtnClearAntiTheftProtect; - this.layoutControlItem20.Location = new System.Drawing.Point(0, 128); - this.layoutControlItem20.MinSize = new System.Drawing.Size(30, 26); - this.layoutControlItem20.Name = "layoutControlItem20"; - this.layoutControlItem20.Size = new System.Drawing.Size(103, 39); - this.layoutControlItem20.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom; - this.layoutControlItem20.TextSize = new System.Drawing.Size(0, 0); - this.layoutControlItem20.TextVisible = false; - // - // layoutControlItem15 - // - this.layoutControlItem15.Control = this.BtnAntiTheftEnable; - this.layoutControlItem15.Location = new System.Drawing.Point(103, 128); - this.layoutControlItem15.MinSize = new System.Drawing.Size(30, 26); - this.layoutControlItem15.Name = "layoutControlItem15"; - this.layoutControlItem15.Size = new System.Drawing.Size(89, 39); - this.layoutControlItem15.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom; - this.layoutControlItem15.TextSize = new System.Drawing.Size(0, 0); - this.layoutControlItem15.TextVisible = false; - // - // layoutControlItem16 - // - this.layoutControlItem16.Control = this.TeSensitive; - this.layoutControlItem16.Location = new System.Drawing.Point(0, 28); - this.layoutControlItem16.MinSize = new System.Drawing.Size(178, 28); - this.layoutControlItem16.Name = "layoutControlItem16"; - this.layoutControlItem16.Size = new System.Drawing.Size(278, 28); - this.layoutControlItem16.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom; - this.layoutControlItem16.Text = " Sensitive"; - this.layoutControlItem16.TextAlignMode = DevExpress.XtraLayout.TextAlignModeItem.CustomSize; - this.layoutControlItem16.TextSize = new System.Drawing.Size(75, 14); - this.layoutControlItem16.TextToControlDistance = 5; + this.layoutControlItemAntiTheftGroup.Control = this.gbAntiTheft; + this.layoutControlItemAntiTheftGroup.Location = new System.Drawing.Point(0, 210); + this.layoutControlItemAntiTheftGroup.MaxSize = new System.Drawing.Size(286, 196); + this.layoutControlItemAntiTheftGroup.MinSize = new System.Drawing.Size(286, 196); + this.layoutControlItemAntiTheftGroup.Name = "layoutControlItemAntiTheftGroup"; + this.layoutControlItemAntiTheftGroup.Size = new System.Drawing.Size(286, 196); + this.layoutControlItemAntiTheftGroup.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom; + this.layoutControlItemAntiTheftGroup.TextSize = new System.Drawing.Size(0, 0); + this.layoutControlItemAntiTheftGroup.TextVisible = false; // // ucCalibration // @@ -935,7 +975,19 @@ this.Size = new System.Drawing.Size(838, 406); ((System.ComponentModel.ISupportInitialize)(this.layoutControl1)).EndInit(); this.layoutControl1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.gbAntiTheft)).EndInit(); + this.gbAntiTheft.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.layoutControl4)).EndInit(); + this.layoutControl4.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.TeSensitive.Properties)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.TeAntiTheftGyroCurr.Properties)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.layoutControlGroup4)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem16)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem17)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.simpleLabelItemAntiTheftStatus)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.emptySpaceItem6)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.layoutControlItemClearAlarm)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.layoutControlItemClearActivation)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.gbBmsDateTime)).EndInit(); this.gbBmsDateTime.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.layoutControl3)).EndInit(); @@ -984,19 +1036,12 @@ ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem2)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem3)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.simpleLabelItem2)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.TeAntiTheftGyroCurr.Properties)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlGroup1)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem19)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem4)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem8)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.emptySpaceItem5)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.layoutControlGroup3)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.emptySpaceItem6)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem17)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem21)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem20)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem15)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem16)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.layoutControlItemAntiTheftGroup)).EndInit(); this.ResumeLayout(false); } @@ -1055,17 +1100,20 @@ private DevExpress.XtraEditors.TextEdit TeChgModeValueNew; private DevExpress.XtraLayout.LayoutControlItem layoutControlItem14; private DevExpress.XtraLayout.EmptySpaceItem emptySpaceItem5; - private DevExpress.XtraLayout.LayoutControlGroup layoutControlGroup3; - private DevExpress.XtraLayout.EmptySpaceItem emptySpaceItem6; - private DevExpress.XtraEditors.TextEdit TeAntiTheftGyroCurr; - private DevExpress.XtraLayout.LayoutControlItem layoutControlItem17; - private DevExpress.XtraEditors.SimpleButton BtnAntiTheftDisable; - private DevExpress.XtraLayout.LayoutControlItem layoutControlItem21; - private DevExpress.XtraEditors.SimpleButton BtnClearAntiTheftProtect; - private DevExpress.XtraLayout.LayoutControlItem layoutControlItem20; - private DevExpress.XtraEditors.SimpleButton BtnAntiTheftEnable; - private DevExpress.XtraLayout.LayoutControlItem layoutControlItem15; + private DevExpress.XtraEditors.GroupControl gbAntiTheft; + private DevExpress.XtraLayout.LayoutControlItem layoutControlItemAntiTheftGroup; + private DevExpress.XtraLayout.LayoutControl layoutControl4; + private DevExpress.XtraLayout.LayoutControlGroup layoutControlGroup4; private DevExpress.XtraEditors.TextEdit TeSensitive; private DevExpress.XtraLayout.LayoutControlItem layoutControlItem16; + private DevExpress.XtraEditors.TextEdit TeAntiTheftGyroCurr; + private DevExpress.XtraLayout.LayoutControlItem layoutControlItem17; + private DevExpress.XtraLayout.SimpleLabelItem simpleLabelItemAntiTheftStatus; + private DevExpress.XtraLayout.EmptySpaceItem emptySpaceItem6; + private DevExpress.XtraEditors.SimpleButton BtnClearAlarm; + private DevExpress.XtraLayout.LayoutControlItem layoutControlItemClearAlarm; + private DevExpress.XtraEditors.SimpleButton BtnClearActivation; + private DevExpress.XtraLayout.LayoutControlItem layoutControlItemClearActivation; } } + diff --git a/LFP_Manager/Controls/ucCalibration.cs b/LFP_Manager/Controls/ucCalibration.cs index d4f3160..159ba26 100644 --- a/LFP_Manager/Controls/ucCalibration.cs +++ b/LFP_Manager/Controls/ucCalibration.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; @@ -133,15 +133,17 @@ namespace LFP_Manager.Controls TeCurrDateTime.Text = string.Format("{0:yyyy-MM-dd HH:mm:ss}", SystemData.BmsDateTime); // Anti-Theft Data Display + TeSensitive.Text = string.Format("{0}", SystemData.CalibriationData.AntiTheft.GyroScope); + TeAntiTheftGyroCurr.Text = string.Format("{0}", SystemData.CalibriationData.AntiTheft.Status); + if (SystemData.CalibriationData.AntiTheft.GyroScope == 0) { - TeAntiTheftGyroCurr.Text = "DISABLE"; + simpleLabelItemAntiTheftStatus.Text = "DISABLE"; } else { - TeAntiTheftGyroCurr.Text = "ENABLE"; + simpleLabelItemAntiTheftStatus.Text = csConstData.SystemInfo.GetAntiTheftStatusString(SystemData.CalibriationData.AntiTheft.Status); } - TeSensitive.Text = string.Format("{0}", SystemData.CalibriationData.AntiTheft.GyroScope); if (dFlag == 0) { @@ -218,9 +220,9 @@ namespace LFP_Manager.Controls { TeNewDateTime.Text = string.Format("{0:yyyy-MM-dd HH:mm:ss}", DateTime.Now); } - private void BtnClearAntiTheftProtect_Click(object sender, EventArgs e) + private void BtnClearAlarm_Click(object sender, EventArgs e) { - // Anti Theft Data Clear + // Anti Theft Clear Alarm (Send 1 to 0x0089) PasswordResult = 0; foreach (Form frm in Application.OpenForms) @@ -239,7 +241,34 @@ namespace LFP_Manager.Controls if (PasswordResult == 1) { wCalib = rCalib.DeepCopy(); - wCalib.AntiTheft.GyroScope = Config.GyroSensitive; + wCalib.AntiTheft.GyroScope = 1; + + OnCommnadEvent(19, CALC_INDEX, 1, ref rParam, ref wCalib); + } + } + + private void BtnClearActivation_Click(object sender, EventArgs e) + { + // Anti Theft Clear Activation (Set Sensitive to 0) + PasswordResult = 0; + + foreach (Form frm in Application.OpenForms) + { + if (frm.Name == "FmxPassword") + { + frm.Activate(); + return; + } + } + FmxPassword PasswordForm = new FmxPassword(Config, 0); + + PasswordForm.OnResult += PassWordResultProc; + PasswordForm.ShowDialog(); + + if (PasswordResult == 1) + { + wCalib = rCalib.DeepCopy(); + wCalib.AntiTheft.GyroScope = 0; OnCommnadEvent(19, CALC_INDEX, 1, ref rParam, ref wCalib); } @@ -249,62 +278,6 @@ namespace LFP_Manager.Controls { PasswordResult = result; } - - private void BtnAntiTheftEnable_Click(object sender, EventArgs e) - { - // Anti Theft Data Get - PasswordResult = 0; - - foreach (Form frm in Application.OpenForms) - { - if (frm.Name == "FmxPassword") - { - frm.Activate(); - return; - } - } - FmxPassword PasswordForm = new FmxPassword(Config, 1); - - PasswordForm.OnResult += PassWordResultProc; - PasswordForm.ShowDialog(); - - if (PasswordResult == 1) - { - wCalib = rCalib.DeepCopy(); - - wCalib.AntiTheft.GyroScope = Config.GyroSensitive; - - OnCommnadEvent(18, CALC_INDEX, 1, ref rParam, ref wCalib); - } - } - - private void BtnAntiTheftDisable_Click(object sender, EventArgs e) - { - // Anti Theft Data Get - PasswordResult = 0; - - foreach (Form frm in Application.OpenForms) - { - if (frm.Name == "FmxPassword") - { - frm.Activate(); - return; - } - } - FmxPassword PasswordForm = new FmxPassword(Config, 2); - - PasswordForm.OnResult += PassWordResultProc; - PasswordForm.ShowDialog(); - - if (PasswordResult == 1) - { - wCalib = rCalib.DeepCopy(); - - wCalib.AntiTheft.GyroScope = 0; - - OnCommnadEvent(18, CALC_INDEX, 1, ref rParam, ref wCalib); - } - } #endregion #region COMPONENT EVENT diff --git a/LFP_Manager/Controls/ucEventLog.Designer.cs b/LFP_Manager/Controls/ucEventLog.Designer.cs index 6867e73..2a710fb 100644 --- a/LFP_Manager/Controls/ucEventLog.Designer.cs +++ b/LFP_Manager/Controls/ucEventLog.Designer.cs @@ -1,4 +1,4 @@ -namespace LFP_Manager.Controls +namespace LFP_Manager.Controls { partial class ucEventLog { @@ -32,6 +32,7 @@ this.gcLogging = new DevExpress.XtraEditors.GroupControl(); this.layoutControl2 = new DevExpress.XtraLayout.LayoutControl(); this.cbPacketLog = new DevExpress.XtraEditors.CheckEdit(); + this.btnAnalyzer = new DevExpress.XtraEditors.SimpleButton(); this.btnLogClear = new DevExpress.XtraEditors.SimpleButton(); this.tabDataLog = new DevExpress.XtraTab.XtraTabControl(); this.tpDataLog = new DevExpress.XtraTab.XtraTabPage(); @@ -39,8 +40,9 @@ this.meDataLog = new DevExpress.XtraEditors.MemoEdit(); this.layoutControlGroup2 = new DevExpress.XtraLayout.LayoutControlGroup(); this.layoutControlItem6 = new DevExpress.XtraLayout.LayoutControlItem(); - this.layoutControlItem14 = new DevExpress.XtraLayout.LayoutControlItem(); this.lcCheckEdit = new DevExpress.XtraLayout.LayoutControlItem(); + this.layoutControlItemAnalyzer = new DevExpress.XtraLayout.LayoutControlItem(); + this.layoutControlItem14 = new DevExpress.XtraLayout.LayoutControlItem(); this.layoutControlGroup1 = new DevExpress.XtraLayout.LayoutControlGroup(); this.layoutControlItem1 = new DevExpress.XtraLayout.LayoutControlItem(); ((System.ComponentModel.ISupportInitialize)(this.layoutControl1)).BeginInit(); @@ -58,8 +60,9 @@ ((System.ComponentModel.ISupportInitialize)(this.meDataLog.Properties)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlGroup2)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem6)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem14)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.lcCheckEdit)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.layoutControlItemAnalyzer)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem14)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlGroup1)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem1)).BeginInit(); this.SuspendLayout(); @@ -90,6 +93,7 @@ // this.layoutControl2.AllowCustomization = false; this.layoutControl2.Controls.Add(this.cbPacketLog); + this.layoutControl2.Controls.Add(this.btnAnalyzer); this.layoutControl2.Controls.Add(this.btnLogClear); this.layoutControl2.Controls.Add(this.tabDataLog); this.layoutControl2.Dock = System.Windows.Forms.DockStyle.Fill; @@ -110,11 +114,21 @@ this.cbPacketLog.StyleController = this.layoutControl2; this.cbPacketLog.TabIndex = 6; // + // btnAnalyzer + // + this.btnAnalyzer.Location = new System.Drawing.Point(702, 28); + this.btnAnalyzer.Name = "btnAnalyzer"; + this.btnAnalyzer.Size = new System.Drawing.Size(83, 50); + this.btnAnalyzer.StyleController = this.layoutControl2; + this.btnAnalyzer.TabIndex = 7; + this.btnAnalyzer.Text = "Analyzer"; + this.btnAnalyzer.Click += new System.EventHandler(this.btnAnalyzer_Click); + // // btnLogClear // - this.btnLogClear.Location = new System.Drawing.Point(702, 28); + this.btnLogClear.Location = new System.Drawing.Point(702, 82); this.btnLogClear.Name = "btnLogClear"; - this.btnLogClear.Size = new System.Drawing.Size(83, 110); + this.btnLogClear.Size = new System.Drawing.Size(83, 56); this.btnLogClear.StyleController = this.layoutControl2; this.btnLogClear.TabIndex = 5; this.btnLogClear.Text = "Clear"; @@ -162,8 +176,9 @@ this.layoutControlGroup2.GroupBordersVisible = false; this.layoutControlGroup2.Items.AddRange(new DevExpress.XtraLayout.BaseLayoutItem[] { this.layoutControlItem6, - this.layoutControlItem14, - this.lcCheckEdit}); + this.lcCheckEdit, + this.layoutControlItemAnalyzer, + this.layoutControlItem14}); this.layoutControlGroup2.Name = "layoutControlGroup2"; this.layoutControlGroup2.Padding = new DevExpress.XtraLayout.Utils.Padding(2, 2, 2, 2); this.layoutControlGroup2.Size = new System.Drawing.Size(789, 142); @@ -181,18 +196,6 @@ this.layoutControlItem6.TextSize = new System.Drawing.Size(0, 0); this.layoutControlItem6.TextVisible = false; // - // layoutControlItem14 - // - this.layoutControlItem14.Control = this.btnLogClear; - this.layoutControlItem14.CustomizationFormText = "layoutControlItem14"; - this.layoutControlItem14.Location = new System.Drawing.Point(698, 24); - this.layoutControlItem14.MinSize = new System.Drawing.Size(60, 26); - this.layoutControlItem14.Name = "layoutControlItem14"; - this.layoutControlItem14.Size = new System.Drawing.Size(87, 114); - this.layoutControlItem14.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom; - this.layoutControlItem14.TextSize = new System.Drawing.Size(0, 0); - this.layoutControlItem14.TextVisible = false; - // // lcCheckEdit // this.lcCheckEdit.Control = this.cbPacketLog; @@ -205,6 +208,30 @@ this.lcCheckEdit.TextSize = new System.Drawing.Size(0, 0); this.lcCheckEdit.TextVisible = false; // + // layoutControlItemAnalyzer + // + this.layoutControlItemAnalyzer.Control = this.btnAnalyzer; + this.layoutControlItemAnalyzer.CustomizationFormText = "layoutControlItemAnalyzer"; + this.layoutControlItemAnalyzer.Location = new System.Drawing.Point(698, 24); + this.layoutControlItemAnalyzer.MinSize = new System.Drawing.Size(60, 26); + this.layoutControlItemAnalyzer.Name = "layoutControlItemAnalyzer"; + this.layoutControlItemAnalyzer.Size = new System.Drawing.Size(87, 54); + this.layoutControlItemAnalyzer.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom; + this.layoutControlItemAnalyzer.TextSize = new System.Drawing.Size(0, 0); + this.layoutControlItemAnalyzer.TextVisible = false; + // + // layoutControlItem14 + // + this.layoutControlItem14.Control = this.btnLogClear; + this.layoutControlItem14.CustomizationFormText = "layoutControlItem14"; + this.layoutControlItem14.Location = new System.Drawing.Point(698, 78); + this.layoutControlItem14.MinSize = new System.Drawing.Size(60, 26); + this.layoutControlItem14.Name = "layoutControlItem14"; + this.layoutControlItem14.Size = new System.Drawing.Size(87, 60); + this.layoutControlItem14.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom; + this.layoutControlItem14.TextSize = new System.Drawing.Size(0, 0); + this.layoutControlItem14.TextVisible = false; + // // layoutControlGroup1 // this.layoutControlGroup1.CustomizationFormText = "layoutControlGroup1"; @@ -249,8 +276,9 @@ ((System.ComponentModel.ISupportInitialize)(this.meDataLog.Properties)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlGroup2)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem6)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem14)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.lcCheckEdit)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.layoutControlItemAnalyzer)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem14)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlGroup1)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem1)).EndInit(); this.ResumeLayout(false); @@ -263,6 +291,7 @@ private DevExpress.XtraLayout.LayoutControlGroup layoutControlGroup1; private DevExpress.XtraEditors.GroupControl gcLogging; private DevExpress.XtraLayout.LayoutControl layoutControl2; + private DevExpress.XtraEditors.SimpleButton btnAnalyzer; private DevExpress.XtraEditors.SimpleButton btnLogClear; private DevExpress.XtraTab.XtraTabControl tabDataLog; private DevExpress.XtraTab.XtraTabPage tpDataLog; @@ -270,6 +299,7 @@ private DevExpress.XtraEditors.MemoEdit meDataLog; private DevExpress.XtraLayout.LayoutControlGroup layoutControlGroup2; private DevExpress.XtraLayout.LayoutControlItem layoutControlItem6; + private DevExpress.XtraLayout.LayoutControlItem layoutControlItemAnalyzer; private DevExpress.XtraLayout.LayoutControlItem layoutControlItem14; private DevExpress.XtraLayout.LayoutControlItem layoutControlItem1; private DevExpress.XtraEditors.CheckEdit cbPacketLog; diff --git a/LFP_Manager/Controls/ucEventLog.cs b/LFP_Manager/Controls/ucEventLog.cs index 7de9ca7..c45667b 100644 --- a/LFP_Manager/Controls/ucEventLog.cs +++ b/LFP_Manager/Controls/ucEventLog.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; @@ -23,6 +23,8 @@ namespace LFP_Manager.Controls #region PUBLIC UPDATE + public event EventHandler OnPacketAnalyzerClick = null; + public void EventUpdate(string aEvent) { if (this.InvokeRequired) @@ -44,6 +46,11 @@ namespace LFP_Manager.Controls #region ENVENT + private void btnAnalyzer_Click(object sender, EventArgs e) + { + OnPacketAnalyzerClick?.Invoke(this, EventArgs.Empty); + } + private void btnLogClear_Click(object sender, EventArgs e) { meDataLog.Text = ""; diff --git a/LFP_Manager/Controls/ucHistroy.cs b/LFP_Manager/Controls/ucHistroy.cs index 07aa233..a1846d7 100644 --- a/LFP_Manager/Controls/ucHistroy.cs +++ b/LFP_Manager/Controls/ucHistroy.cs @@ -466,7 +466,8 @@ namespace LFP_Manager.Controls private string GetOpStatusDisplayText(short val) { - switch ((ushort)val) + ushort uval = (ushort)(val & 0x7FFF); + switch (uval) { case 0x0000: return "STANDBY (0x0000)"; case 0x0001: return "CHARGING (0x0001)"; @@ -475,12 +476,7 @@ namespace LFP_Manager.Controls case 0x0004: return "TRIP (0x0004)"; case 0x0008: return "CHG-LIMIT (0x0008)"; case 0x0032: return "POWER-ON (0x0032)"; - case 0x8000: return "STANDBY-HEAT (0x8000)"; - case 0x8001: return "CHARGING-HEAT (0x8001)"; - case 0x8002: return "DISCHARGING-HEAT (0x8002)"; - case 0x8004: return "TRIP-HEAT (0x8004)"; - case 0x8008: return "CHG-LIMIT-HEAT (0x8008)"; - default: return string.Format("UNKNOWN (0x{0:X4})", (ushort)val); + default: return string.Format("UNKNOWN (0x{0:X4})", uval); } } diff --git a/LFP_Manager/Controls/ucShelfStatus.cs b/LFP_Manager/Controls/ucShelfStatus.cs index 47d2125..6506651 100644 --- a/LFP_Manager/Controls/ucShelfStatus.cs +++ b/LFP_Manager/Controls/ucShelfStatus.cs @@ -689,7 +689,7 @@ namespace LFP_Manager.Controls { if (SystemData.ShelfCommFail == false) { - switch ((int)SystemData.StatusData.status) + switch ((int)SystemData.StatusData.status & 0x7FFF) { case 0x0000: // Stand by lbStatus.Text = "STANDBY"; @@ -719,28 +719,8 @@ namespace LFP_Manager.Controls lbStatus.Text = "CHG-LIMIT"; lbStatus.ForeColor = System.Drawing.Color.Blue; break; - case 0x8000: // Stand by - Heat - lbStatus.Text = "STANDBY-HEAT"; - lbStatus.ForeColor = System.Drawing.Color.Blue; - break; - case 0x8001: // Charging - lbStatus.Text = "CHARGING-HEAT"; - lbStatus.ForeColor = System.Drawing.Color.Blue; - break; - case 0x8002: // Discharging - lbStatus.Text = "DISCHARGING-HEAT"; - lbStatus.ForeColor = System.Drawing.Color.Magenta; - break; - case 0x8004: // TRIP - lbStatus.Text = "TRIP-HEAT"; - lbStatus.ForeColor = System.Drawing.Color.Red; - break; - case 0x8008: // Chg-Lmt - lbStatus.Text = "CHG-LIMIT-HEAT"; - lbStatus.ForeColor = System.Drawing.Color.Blue; - break; default: - lbStatus.Text = string.Format("UNKNOWN(0x{0:X4})", (ushort)SystemData.StatusData.status); + lbStatus.Text = string.Format("UNKNOWN(0x{0:X4})", (ushort)(SystemData.StatusData.status & 0x7FFF)); lbStatus.ForeColor = System.Drawing.Color.Black; break; } @@ -842,7 +822,8 @@ namespace LFP_Manager.Controls private void DisplayAlarm() { - gbAlarm.Text = string.Format("Alarm && Fault ( ERROR: 0x{0:X4} )", (ushort)SystemData.StatusData.errorCode); + string chgWaitStr = (((ushort)SystemData.StatusData.status & 0x8000) == 0) ? " - Wait for Charging" : ""; + gbAlarm.Text = string.Format("Alarm && Fault ( ERROR: 0x{0:X4} ){1}", (ushort)SystemData.StatusData.errorCode, chgWaitStr); bool[] aFault = csUtils.Int16ToBitArray(SystemData.StatusData.protection); bool[] aWarining = csUtils.Int16ToBitArray(SystemData.StatusData.warning); diff --git a/LFP_Manager/DataStructure/csConstData.cs b/LFP_Manager/DataStructure/csConstData.cs index 7d5d225..ac616e2 100644 --- a/LFP_Manager/DataStructure/csConstData.cs +++ b/LFP_Manager/DataStructure/csConstData.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -29,6 +29,21 @@ namespace LFP_Manager.DataStructure "Unknown7", // 7 "PRE-CHG + LMT", // 8 }; + + public static readonly string[] ANTI_THEFT_STATUS = + { + "Waiting for charging activation", // 0 + "Activated", // 1 + }; + + public static string GetAntiTheftStatusString(int status) + { + if (status >= 0 && status < ANTI_THEFT_STATUS.Length) + { + return ANTI_THEFT_STATUS[status]; + } + return string.Format("Unknown ({0})", status); + } } public class CommType diff --git a/LFP_Manager/DataStructure/csDataStructure.cs b/LFP_Manager/DataStructure/csDataStructure.cs index 7caa9a4..67a5736 100644 --- a/LFP_Manager/DataStructure/csDataStructure.cs +++ b/LFP_Manager/DataStructure/csDataStructure.cs @@ -551,11 +551,13 @@ namespace LFP_Manager.DataStructure { public int Comm; public int GyroScope; + public int Status; public TAntiTheft() { Comm = 0; GyroScope = 0; + Status = 0; } public TAntiTheft DeepCopy() { @@ -563,6 +565,7 @@ namespace LFP_Manager.DataStructure { Comm = Comm, GyroScope = GyroScope, + Status = Status, }; return newCopy; } @@ -688,6 +691,41 @@ namespace LFP_Manager.DataStructure } } + public enum UartTxType + { + Polling, // 주기적 상태 조회 + WriteParam, // 파라미터 쓰기 (Holding Register) + WriteReg, // 레지스터 쓰기 + WriteCoil, // 코일 쓰기 + ReadReg, // 레지스터 읽기 + FwUpdate, // 펌웨어 업데이트 + Raw // 기타 원시 패킷 + } + + public class TUartTxItem + { + public UartTxType TxType { get; set; } = UartTxType.Raw; + public int SystemId { get; set; } = 0; + public ushort RegAddr { get; set; } = 0; + public ushort RegLen { get; set; } = 0; + public byte[] Data { get; set; } = null; + public bool ExpectReply { get; set; } = true; + public int TimeoutMs { get; set; } = 500; + + public TUartTxItem() { } + + public TUartTxItem(UartTxType type, int sysId, ushort regAddr, ushort regLen, byte[] data, bool expectReply = true, int timeoutMs = 500) + { + TxType = type; + SystemId = sysId; + RegAddr = regAddr; + RegLen = regLen; + Data = data; + ExpectReply = expectReply; + TimeoutMs = timeoutMs; + } + } + public class TUartTxBuff { public int InPos; @@ -731,3 +769,4 @@ namespace LFP_Manager.DataStructure } } + diff --git a/LFP_Manager/Forms/fmxCalibration.Designer.cs b/LFP_Manager/Forms/fmxCalibration.Designer.cs index b9945c1..b2fc746 100644 --- a/LFP_Manager/Forms/fmxCalibration.Designer.cs +++ b/LFP_Manager/Forms/fmxCalibration.Designer.cs @@ -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"; diff --git a/LFP_Manager/Forms/fmxHistory.Designer.cs b/LFP_Manager/Forms/fmxHistory.Designer.cs index db6daaf..1b39f42 100644 --- a/LFP_Manager/Forms/fmxHistory.Designer.cs +++ b/LFP_Manager/Forms/fmxHistory.Designer.cs @@ -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; diff --git a/LFP_Manager/Forms/fmxHistory.cs b/LFP_Manager/Forms/fmxHistory.cs index 6af41f2..462db37 100644 --- a/LFP_Manager/Forms/fmxHistory.cs +++ b/LFP_Manager/Forms/fmxHistory.cs @@ -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; } } diff --git a/LFP_Manager/Forms/fmxMain.Designer.cs b/LFP_Manager/Forms/fmxMain.Designer.cs index 57df2a2..9af9324 100644 --- a/LFP_Manager/Forms/fmxMain.Designer.cs +++ b/LFP_Manager/Forms/fmxMain.Designer.cs @@ -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; } diff --git a/LFP_Manager/Forms/fmxMain.cs b/LFP_Manager/Forms/fmxMain.cs index c2aa38e..4c41865 100644 --- a/LFP_Manager/Forms/fmxMain.cs +++ b/LFP_Manager/Forms/fmxMain.cs @@ -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 diff --git a/LFP_Manager/Forms/fmxPacketLog.Designer.cs b/LFP_Manager/Forms/fmxPacketLog.Designer.cs new file mode 100644 index 0000000..b7f942f --- /dev/null +++ b/LFP_Manager/Forms/fmxPacketLog.Designer.cs @@ -0,0 +1,354 @@ +namespace LFP_Manager.Forms +{ + partial class fmxPacketLog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + 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; + } +} diff --git a/LFP_Manager/Forms/fmxPacketLog.cs b/LFP_Manager/Forms/fmxPacketLog.cs new file mode 100644 index 0000000..da8bc55 --- /dev/null +++ b/LFP_Manager/Forms/fmxPacketLog.cs @@ -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(); + } + } + } +} diff --git a/LFP_Manager/Forms/fmxPacketLog.resx b/LFP_Manager/Forms/fmxPacketLog.resx new file mode 100644 index 0000000..459ff73 --- /dev/null +++ b/LFP_Manager/Forms/fmxPacketLog.resx @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/LFP_Manager/Function/csSerialCommFunction.cs b/LFP_Manager/Function/csSerialCommFunction.cs index 82ea7f7..01cc99a 100644 --- a/LFP_Manager/Function/csSerialCommFunction.cs +++ b/LFP_Manager/Function/csSerialCommFunction.cs @@ -758,6 +758,9 @@ namespace LFP_Manager.Function case 137: // Anti-Theft Gyro-Scope rSystemData.CalibriationData.AntiTheft.GyroScope = reg_value; break; + case 138: // Anti-Theft Activated Status + rSystemData.CalibriationData.AntiTheft.Status = reg_value; + break; } } diff --git a/LFP_Manager/LFP_Manager.zip b/LFP_Manager/LFP_Manager.zip deleted file mode 100644 index f2f4fc43002360ae68e7e2ebe488ffb9122e8c65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 104659 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

(kq27V(1CpBin1R%| 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 z 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�VSxm#@Y#qYJU3VgPoEfVu0kYMp>;AW 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 >klG9#$%tJ90 zZGbs`{1;s+0??(5sMV+LU$$-DW&cH&N~fm*bm^p|qyoGknB9NK7qUkXKl*x>hoWT< z2=1EEz3AlyA??la18P$lbgA639z-z~N@HNClp<@NzB7&FBCI?M0#*RpcGCtG)-x8W z`cIE*PG4mT3-H@%r)i~@Kc=tU%RmjnE5{{d2xDspOofRp_vYArj>XLi-?ihJ%jCj8 zqD%e;-xBG-A|9`PsL5`w4T>#oO@->ykWa%Ac%^{e!!RweA9I3y8=xt_#laUnXNze^ z{ZU(&pPf`ds>qfi>~C~O%S<^2)>x7u#1Cd!(VikOG&mcV&n3sdJ`aisoo6#q&?EW5 z)0xlzf?V0LJYI{-4t+7!E;|H^S6HX(MzkXJe4V7;s9&@w{x$qO_*$Qc#nuetg0cB& zXd#II;4=9m3Y+aK<@T!_CDEdBHKp%$B^Q?*HI3OSh?KXRkNHiYH}jmX`BiuKrq<;B zf&Pr*)2zZOZD0iZxhJ!c>pC#quO>FnLMo-1yp~GOGoBOoSNb)P1-ILCMA3{wPj`#b z>vu#ESVSMAtPTN3qBY8HaQzzW?bER^f?-o-fmkLc0~ zfG)-SMVBm)flT*2+P~k${ zsx~gnPMXmIO0KdWtlp}pK0{MN|M|0vJyMrx`pMX%Dik?nlC`sx$&D#E#-6$85JX#+uo*-S!ZJfE9RbM{7~qbM zs`)oKdVwy7ejTxq`kBN}_Z8&Br4yNbTE8k+Rf1ZMNQTaFe^nDIkVc6YMx0$SZHS}t zW5S3QK;;T4zCmRlX-avGda)%z%T;4CnQIJk-o0t5{;( z+M<_7o#1W&U2gtu-s81^D7GxT#SeU{?h`F*k`0zv>9hD8OH`lTbDCNOdFG~BN8oAH z4JCkv4zcJqcLT_k4@v>V&)3ZIpgvWWsj5vY$zcIL0TbcY0}OGQk7)g{n>sY_1(s!O^@)(qk#`HGi_grO1CVGZ`U(6k>!K+1fih`RT8^ zWa2stY(WzLrRxiC53mORl?i^!xl3$`Q86fT{``vE-^7zg+8iFH2OkZrX05U{IldBV-r@Jcjg+rRt|YNY&!Dk0^pMFmXIvgX;3oq<9bV$8 zUGh#2fu;YEa!t1w$@B&j(a*&xzDHUsg`H1v)Cy{Vq!BW?C%I<@YRVw3Dk#KWS7t5d zlUjqa-*ywMt6IUlD%hEBO)U<}`*dplPwJ9KVl;{B=yBG6sY_a`9~M-vdS?fUgS@^< z*!`X)6w)LCZOkEu?W!BMt&@j$f_Y)zUDrq?%vxZk6&T3r)77-f=+Uby@=w>kUL>r8 zDue9;uepboEtrLal5-SY-;goqnF0x&Yz~z7=8X3k=+P~A(iD0)oX|p zB>AT#g_z;*iY{C~2`;;!%H;nNxgv4@Vk0Jy@cj-K)D-eXg*E=&9a#KOHlVrqudFogtWvAnwH7Q z*UEIG<9qW)Em1ml5O;DTpelyz@Q^gh?%W<>gy8n2T2(6Dkq=xhEpn%iC(ed4E2o=x z(5MCc@Dc&*PX+3uahPiDcU^xBy&d}qSMkp>e8TuVGi3Lb*bx*x)grG zI9pclx>YFAd4j~5?xBh};HexGTC*Q&ZV_Owp_i9v$F94o@3yOa_`py$$Hv6x08?kP zaRWkyo1m>ZWE)v$UD$hBzRpdB-5E9R%tqJ0n6^@y3u3AFNmz}ptT{^&mKtf6DVc8d zce8%@OI6$^dR=BGGetm+iqDW2w)ix?mo=l7*t+{6w;D_yb6-?$TEK%;NM{2=NRjtvQ9Xw2c z5WJ!FY1N`*8Nck=CD5@rfVwfIa3(+$9g*hSFer!uA|(HWr03EswN(KZXZV!78(eD|aL1J1FN!wxl@+ct{Dp-Fpj%C^qw0^%S+f;)rf zsO7*}a%#z?xMFj765<3QF>D3oQTzh1+(FgziUxTchrW#adz;1$!tku&1N z$JE-aIzCl5qo&qnF0Jo%?2YNXXeyYV=4lnLwONWSEPVG+2vf9+$k2n@2qgp#5@SPK z=GCAUhA-;M*UoxGN`^7NmPDO67iY{6yra}bB(BAmP)gWxCArxjrkEBFL&CWYGhZls zb|pBbgR8}zOL^Dl1h_XNfZIoo5{`YEOp2?vBk7^()SlE{D3`ItgRGQc5fTsNK+{#5p?|%UhJHy^u2IA zXXK=_sZZ>VX@$g@w!k+|fs*N;H~DlMn^)$a*}CE$ zuy336NF)*Eez22(yxgLc_^k__n<)Rr?^!5(>H(t2F~U(C-N>xEKeteiZu{Zozg9XR z_350^t+jt*U$N?n{Jry7|4Ow=bSiwd0VkibLzW;L5c2F93=B`%3Z%RozEf?a&#++N(qgj2tcCIL!MRP_toTYRl*Ywi{rHR^374r(6_3hDv=UvULSjg-xuDoq|DG*9DIa@6qw4G zaW!h%41O2cIXKYa*#nF2SG{jgju|G+Ab&Kf?gVx+6GG>7dw$Qq#rNl=-v9|F?gj0l zYFd^$&EsLUfrVK$haI9B$-7>}9`Sv3IGnX^;?&8($stwna~xzSSh%J%vn}_F**B)h zufDI=me1J=(q&%Mq9Z*W${A%e2;BgYU9aL>ir?NhY5+<)IPS6hij7jAcZp#>&}!$2 z-1h8?M*x@^FB-G|#X9kv;@hRHSn1&Sw&D-(xqX@)M*IG-OV@1A{Ns8`d{o)n%-BoktUKq?9$4t0p7ptlH}L7 zviFw-IkP!BM9OlC;YNLY=%X=jky8O%#7XJ)d;R)s44v&CcFEoS%TURBe?4SZ5bs}h ziJ5V7S3W86?(~VUdcdZS3qklNuLh&Xknsr`(v*v1)8yQda8d`)mMt02b%0Y_J}=!2 zI?Ith{=x*!7yjsKsQ)uMiwm!k&zaHw9Q#QKWsdeb*je$gMyR3r%C-Bmlyni9T+hh7 z4;N2mG%<9Qhtcb2MKBH@UQQwAoR`!YV%}Xd8OXz$A8+7N8_a1XwYsempqOM*5ZfG5 z08uaBXWSm`nN0H>zw}7v^*@SpWFx^rXm{`&Udvw-)=s>!5=_I3!j9DR$kE*MDoG;} z_rLuNi?hZi8+GBE9BHmzP<64m!qI~?2dx?fI8i-!=(kn<@`RP+6`r3A39Cu8x!KJD zHIZOSWV_S+K<{pOL4L|JPUZK~Pm9I&DZlCS5C(C;1C(x;2Zm%$FX8kr0xH z1B0aD)f;ENkzS)fg-uqI&pT%RVKjcEl^Aqo^s>~ar)FNdrdDIQ%NVBmZce!X1{p39 z>?*@d=}27gp0za9fOCu_uZtrq8#?ysf$WD3SJ4{Yb_;co5K2O*B>i5$pM5#$oA^)r zUBFV??NVt7=FXc~?e7q)ioU$LICfE)sypfud$eEn7vt(bu9zwg3*TBtjpUUB?r9xO zyQXZnTo4oBE>VEEeakft{&kmVKx52*+@*D^5=h=(cd6vR-6eGFL;#4{`r|I0l(2mM zZ+9vEQ$Y?&G(#c7$R^WU+eddvYuMT_@gt<0e7LKmTK_zxaO1N*Sj*ChV*Hg0;!Kl; z20q|0Ig#*L^h)YMz1Nz}Ea-gz+wOb=l4;hxnlEhef9o!VOM2UoNgI7(n^BL;l&ha( zeV08QsAx~G`!q0WKziwn)I#qdey4nc#@dpKRqx27z9QyswI-uRwH+<$;4^HgwUs#!3 zlnb%qb9-wzWiNLFlx@;%;RKLsha4w-NG04IfXx_S+1AgUvu%ZQ*Nm2fG}nZVr5o;1 zn0ihtdqNfEWnk*LNccbQQn?)9E*;Wh(G^MUX?|@DYaO}}t9?m0Y}M$i`7z!0zul!g zm!e+4T`G)vbeCMtnzh#{|GG;ffV)HrxJzY!-6a7n*JxJ2T{8IaJL~x`-jd+km@%gs zlU0O2?h<|kN>DUf)De%mdM|}bFkJyq+g7##+@;xR1j)mS`BU3wX{O*ucL_nG=)c^h zfkebPirdlFZEGZ}ij(~CS`)G%Plb7*7qypen4bw7 z@*Xqd9npsiMCG9u;NDE!H(q+zI-=piiP;8Zv}psq$POoHEWCkzEmiTs-f)x<*t}qJ z$`jm33}JeDbAv#USd)YIQIHHWxtr&zYX2hX-F!W%`)*@&)L`R0U9*V`9Ll&d*tL%ZG|kkB!;sIZ>gl}TQFyGo?;hGrAw zK|-1QzVqmjupHXg8xiHWO*2V&AnKGKL;^dU$tN1EQeM^DT(ak1x)My~N4>N@uudpq z4VOOs@h7uYcu=j&MCG$I1eiBVEoxdGi5-EuC%@pI=p_YJSYJ`tqD;dB1x`G2e&18o znvVQSJoT9qm+}mZPQnss!AdRS)OB_n(Lz~TSQ#3S>?&c_InP7iDnQaDnQJ7G-YFSs ze(d7p#~kyT0=Npn8%Is14U)dJqf*WxT-mJ1OaCZ(bz+rCxAnqgbNN@^-Afyzeo|;* zj0j}y6p8C4dtLWQqY_-X_#3?hpzadM+K3V4%WZ~HPdB#HuFnxklbhoRtNEsg`b0$I zTb5BF$j*XqWsdJ=0p<(E1W;7r&!M@l+6= zEJSa3%EJ)%)TfGX(*=RxRD1hM#Tnb-ZdK3CKZ(e49b=8)d7b*l8Abxw#@tzYcoJZ$ z0ARCi~2&&1M1OcGV5#uGJov{2kB%(WL{0#=_KPKffBHqW^nf! zvk!s{cNKcPn(sjo4wWwvP*Pc?jdWSFiwD&kwzB&ZqY>ehn7BXX#5E()i>q;u)4L9E zd2q-ph^oxs%wrljG^~xvl3_6(zF9$eH4x2w6?QUo;y$4TClq!maqL#1H~?4pK33t& z$LE4Vpv9`}nla&Thg`cR4RVB4QXQ@-f@JYS*Hsw;m4gPzyfx500b%MGm$fK>Mvy|# z9!ym6ZR?@HJ#LnkFVuHiZct|*?H2qcg17l>6^!S%aVj_6^KoU$ep&(2Y6e5zD|D!vyu;#4=& z#+KxW0B)#&v`m*()S?;6GAdVpJt0YP4RTqN7cl9~kr!c3c&`lN;DsC2A}(IUca3jx zv5A!4{{Y@C1(Uf=e~{BUqFeZ?2ogM+S%P4HEoI8Jq=$^)0D9G@{Q{EwN(r#rHiLQP z^fT~zo$jcZK8KTjJtSI%`POARq^lt%BAXkAs;G1T)Lfe+WW|h1otfC*0ozgkg_j%v zcnN&WVHDLIpY4pM|8yh2zpmVrTZ2XolxG`sB)^?j6f-$n*-W3aucu2t!@I@k&2Z?T z0}`u9 zDk@94qPjazR>6*WE%6Euor?$VW|7>V$Gib@fxvuS%uBY~P-u@=4i{0f`Uow&>G}i7yL*`G-Al*$3XZ zP1_4DJ7?%(4BS(_exNA|i($e86Q-TBvKAEFCe$2wX}TZb zCA@#&rNV|IZKhj>zwlBy056^XC%oi9|2YJJmzn{1sqG(lsTf@KAMnz|&2p z^yQ;zoFC;Afm1Bs7JoexH2Egkcgtv4HZ(s)6t!So_@<0P z11~M{v@1dAAU+$V!yG=EXUij?2cML4;};TL9It+&w0K*mH~m<|O}N3uy0fQIqjZ$k z-jf_Gy4E0U!snO#=A*L|@Wl+>&nhy0m4gQ6q`M>?(;{ItDuRV_1(|ruM6W8=%m;?= z$EzSQ(Q|~gufw9}R)_ZWIx&GpA$Jvg>kWCEz?oxd;3)8V)Uo_RR}UBo=ou@^m!0Wb z{&??Nk$<*l;4HLM8$&=r1ItU;C_N{yLvrExN*mZzC_GNlaO~Ls+DLH;)|xWZuYd08 z;}tzHR`=aCy63gFU6;dY|YG^y_VUB-HK0Ni zOnfUH+2Rb^!pce6;Lf(nGkRc^C?x^dC#tX|T+aLLUJVf?$`gJUK`I zA@4`T|Bk#?9Q|VeylFd`CBOY#EI<*)@T5};B=hYm_(eJ_%e(onp?TqKIk95n?qjnH z_pZ%$zCy<+p_J|v;XX{1DpwuwPiF;6qDZx%Th$N=qK*xr-KDi`@6B?P)!m{TwIzjSbcwZ1FO5%#5JjF=tcE95c*oO62G9jyOj}Rix_vNcRNOE{1hnputFP% zpK`LK<5{Gi)=SHWT}@Bz)j}`)=TRa^n9Gdxl3hmlcQL(W$)~(&**2%6TsPZ3KDWmL zBpJtx-*c`cgXnU-vf>+72A6;t&DOV~BX+WDXGFB0cnh|l#LLF}$n4-&!-GCrkn<_g zmKBt9XzRLi_kl;550wb^jN{~thX`+;(z3&l}C9=s^R5-$V(lZRcpmA`3R5lQr2e!a@RpBw~|GT zXFdFj@#WhV$h@zJzzgfQ&YllX%xWg!B)XDMISX;20o{^5X2mQ_d~Z|p+uepcg#+59 zhoU@<$Fnc{ipC8TyoDi27mUGQRT*jt*bYq)fn`tMdg-BZ*a=tLmE%NEYonUBguXTm zxu5)qj^xhHkYlr)S(ewbLWy@vo&qFWK1y(MU(Hn;ufsj6RGrO*_s^mb?ts#ua|xRn z2A$3L9SJLLGQQ1E`Ju(yLaETZ#; zWAI8m-u!seuqzjtM@Ww(vl`Jwdn?GJ4*HqnIs#b5f{sYGY?fl!W+#DxBL+q%BZ0KQ zgOh>7d27)o;}iWeaz8j4Y7}W_W|4a8ZvGYzl#~S-b?{-_&{d*V@-TJ({E^1PJc~Fx zk#@`1jCB8u$TDS-&Cf>JF-Pb9H67EGMxS|^is#-KlD=gKlGCRA9_jPA9{%!{U3UX{4c$v;dP$nOZ$gjlA{G5 z8&I;o4bvS8wQPP)I`&sdd^CqP1^}nCP||YZSW{^bp$OXWuy;D#<^gRzQoO594gbC z@XOMRr6lQkuXzfKoTDG9SsRLc-Yy3e!N8&P6&)<{NDrN&rGCbZ0+Yzu9HrW4{9BLo zPY24Zj!Zs&G=?0*@ZFwwi${u19nVta-4`+l%MknTpeSC$(R3?>Z`_vfoaRUE23vci zlv+j9Sbm3Ohk9=}!>&hL%LvJhglZYV_1f4r8|kZx3NrKW z7GK^k!`%nH+Va)&yAMn612#G2Mlsx`lC@WIY$Khxa}>{q#a%W^%kPN#8jzii56@mM zh79+7!J5r=KSm92D!hHMo`Sg}zP}eviSWxj+Ib}Mir4ZHAnJ#r5!o%G;PC#8cQNDk z0^=fLa^y^@hCA>VlKU<2DKeq20T$ODYQ&_Z23G#Uru%&_c_Y8ENuaBP zM(zjK5C+Yz%~QTBb2p@?BfJ!%@ZtTu%#Tvn=o~N?F+1xgk=xhwpSe147(^B%uoP=| zutmLmxo(UWbbzo_W>q}A5gjvClr;PyX)(dG_1ojE9$+MT!8XX*;*mJudgBD~mNBqy zhssTPGXfCfzcK9g-=$D%4@S8*NOIprM-V;=N?1?}@LE=YvTa?X6;R_vgPXSbY*-&G zP-$rxq+3#D2FJ}oKQKR4%{#yJWaNfcXc}IOD;(VuiQH!Z7yx88y zc)H42yVWiC4B~yaut$CLHXO+uH?74O&5a8k<juC?OAD$64N~GH@bjG23BzA&9yKJ)IAWczk8QE)jA+c}w| z7ja8zM7!MM7b#LK1ckh|2q^Xf$reJH7(O|p7-z5N#ci=gQjX0SrmgNV7{z&{^nLXS{+S@yYwH{S zW(xUfk59L@yzp8+4Kur8#o(krpgn0i(3X`v)g7hd>sVTvzy$!IhnGOlZIbc!W=e(S zH`0IFOX&aDOYQ%gz2wIHXfLro+DnCDi^+(1;()#6-}lE}!h5urlz!}vt-pr#I=%qx zCF)0esjsOp?gGwPsP1@wHVVoYchU(sO?Yb!B~eR-|815{T>7?nHrNdhK^xTQkC0V8|_4Px1&f1;wFC z<+sGxD%GKQcgOr=FCjbH1K5qwUwf(gAA5<~L_;zh4*|TkOZTv_p!)S1ch%w(|9g=B>&G$ecxG~l+*I{Im)yrFV!g-sb3w!|{L2br_usQhl z!wH+$Ur`QzQKOZ5-l|byUU`l2Y+cVy+yAff-a4wTq*>rSNO0HSZo%E%-6goYy9T%5 z?k>UIf;+(p?(QMD>pPkG?wZNm`R-fm{rmP}0jFXAPFL-&+I_04cU6wrR{k=bu;(~@ z(nbBe+wq2KmF|3rJR28*P^NEt<1EHR6P(e16jp-t>IW{uWXrE!N(oZQyJ5b}0>^j$ zAPtWNmy`5=iYDQvon>kqHC0h=;f#ngec06X6*_k{o{7StT>zK+WF~RKJ zWNBXynp%#smo|;fU*(Y2Z3AoFbxIdM){VijdJw%tuZ6Rri5JXINIbF_&-niGcE}GJ z#^r{@jXkEv=iw{*O)ZBxXUe=W@=u9j=PV7gRvyo5HOS0-U>*(x zE8}nxa1bx1b4T}_dvX6Pe{@RhMCa);Q>jx@pvV6a%J z_H7;*iDnVfd;oc7p1FySO(AZ4q8{SF(V@{UqYU{KSo45HO-x1C#ZQB8l6gS_MzpqN zZ0OuQO`Rlheyv{sueZDau*g(hi@9Dovv&pAKRxt4~@LLn9QB2 zo$!|obiyd-*!kma4ULKT;X1Xx0k^Zrd_2@#uoBdHA>!b?n!J36pbkEbD!v_46 z3B2@Xe%3DWW^23jE-Wo$J-;fSJ!WIZ4`D2;12@I|3__{HGCEi>X81#%_fT=@p;w z&HtQF1%gG@VIjZrJBZ{7n$Qce#>8hLXozJ)lzgTlr9@QvbNZFgm%^7|JgVV1W69(W zQz4|G*^AS!2b}ASZ_hVwXiBc3nW~iE^1ZwhUo119vc>lU9%)$kP{la1Fl8YUedw!X zhYS1~dyh#C1L(pG7zJAhID+VpGJ^)j=Q-+K>5fHiW<0A;OAkvCW`jEGr@n(Fx3s3X@w7rWk|6SG6A+RDYn65;8 zDo7u*=tny0RRAtAIH3*y6I6bh_yHxFMhPN9b+zr388M*SZZRSZgFe4MH2ZxXAmS;q z0FLfqOfA4K2TLpmi3IWDuDRG^-T5j6*#uO_{T>01sBHWQ1=i5%4P#ou&@y9@xq{?K zG^P*J+X2*?dDhHn<6W3o%nTu3qlwi7NANCVr7u*4_(I0mQhn01zvYN(~1ajL>g+s~84 z$6tdA?2}7UwB`l;h_4!n2WRaBs8}j72-B$;ts3FX2504Y+G);ONp-|OOW z7C|v@-0iODtojby=iJ*Cio|!_CMgda!$H^FHCj(YxW9)CA|l z)ck?773prrRe39`8ZKn@jzkqt)g&%<>)jVYh5(GOc|)*-)xA=Vxi>;to;@P9qO0yY z>d0xrZ)3K})6%{lT@gmbE%a!|bg7EM^Z=$RBiu3#oF<{l?=)0${hkryNZ<9=#!N{`F-Gz^&qv6BUEk)&&9tumrAG#m2+8HOpWluMtFG# z@+6<3YP8Fb_E@3q3OWXS071n*@P<0$0{?im4@vAsy}hNSZ;g7+*$R@SBJI=Mwj{E5 zWlxV&m%R33D$7b*%ihh7M*41z<2F)DjQFW`&9N;F+gu}Eb^ zMgoFPt%(lp(bq1G?zPk8b)r=Kx}M@agc=+3P0xAop`5vRvq4FaI_-c$HaAsau9W6H z{5^$AKJ{@4>3rK6VY@i;AM=O&(@0Q}UP<}y`#3$B78Vvzs3HXenUzTl+N zbeN+>&?Zz4&~Oh|VU;NT)az1{kyi%+k>3~JjSOjumgCD0zq+D7+ps;8m<2vIXE)6{ zMQ1T;dVPh905R13v26{~ymA+HLM>jAOaU5x;Cu511zU2*3(Pmx3EqIwBkK@uENj|u z!ThFKI~x}Ko0W6SpsDW}a&(~|wsD)FXmHFES{&8eMk+!&rQ1DYM*0ouJa`0RS<1Is z76`ftvepdy_iCjPiW+#cW%SgNMrVdqwo~E|=J!yX4Eo$xrLmH7jqaX0JcrCU91TEm z9W|$SC*A;&|GroU9-79i-x6R?WiWu(zFW#zgP z?DD_2NBlx|cAKhMw5R|W|^|kZ=I>I!ugB0Ki-!gUxo^OYq+eNP#Rc?S*Ts?9OYi1pLN9y3fZq~}w@D8p-`?n^k&v|EM(oY{h9pS6-aGo<9NzQGcleBE(~T- zC-LK#8@K7EZX{?7``(ng?JgAgq;1XfmRJ_da0@dy$UJp16?yy8r5RBb5f$StJ1n6m zf&uQhr*zA<^-4s8j*;*vR6UW>=o(u#bFz5*UsEa62xaZW#TQ`~{4#p)^brHakwo1d z6xNsz#FMmnJA;KFm3`tzeyT*5;>8~?m(mrxN|u>@@l0JNDecTUHnPkqbUm3xC`JGl z43_&z{J~Aaa#HT6!3T;`AMVDOQaVYz#ZoXCG85G1G4kkOREl8KW_SvkEDoxy785Q9 znrsfL>=qLq2bvrXs+<-RJ_nlI`|p0O!2<~&S!Nchyp}7LYl4NG)!<8*niFfo_qbG+ z8WU^3?m3p7d>mQ4cGM|5(HdEVbK)&qtxc>onYt-;Tu>TJ7HSqw@=9iE!fiG89KN^- zOU2v6yg5UH?tt-+8b5_vyh)7O7n%V40Kqm!!61$t-?t?lo2Q+%QgW-qw}x5LA~P+p zNIpFoLpY!glb9^+(?3+gE6$h1s0f4-8!hzA35fJ=WnZ2rDhw4{WC_oIewfAHqxI2< zaRC{xgD$bh=+{io3O79gT*)P||oPajMV`j>8uUsfGVo)+^DKj#i%b z?qgN&H#t^L=r78!PStS2cLCJ!86qZn1VQtJ3lCU^+R2s-b)oT-LZ%+VeQcGC#W76s zj-^+=NytSyAR+JyGILn`F4mjgkfMbjX^weIh29N1B*KohKhoq&^n=VQA;L1Q`XI{F z#|7*0dTNFA;J@n(>#yj{P&26TWl`-SgC%aJ?k=48&QM8Y1B+1P%H7<0s8d9*daWg; zwIhYtQNSg&j;;PEepWP(x{S6tzj=r^=P8yn|Ks(NuBUx{P}I4_>O9?-74qte;4k~t z3>BBzWC<29Km-7)KmM;Nk+O-Sg@LuOosG@kEcmAua7rYG@|p?yf(Q%7E75oyi&lzB zeI!^N4)PU&hKzPo89rU}qs;Yod?Kn|`fKfzJ^#^b@6U-?6g}6{q;V@Rf#6$c*@+2e zZss;=gp{|3g)a8awpOwNNdDZ>`31d!H=d&^OxR=aQ_sDe_I&E&uX38khgF{>b8u$l zLAWuq@DSFub5^LIl07MSxogDzn;in8#I=aSoBi9)^KM3TmEI;?&9@)*>p>$rUyrXA z^pJ0zFq+O#hX@$2PDI^0v`}fa$B6s0A_a#e$D+vtI?U+^k+n@^9^C}4ld&J8>AityqX1R$3y*U1l~iC zdYXDHtH0JS_bFp)zCbbA0_DRDqGMA&AZCHDY`EY_4fZT2k zxb1m@AQmY?T!g3~>YZcLhu3UGjv^I@Z&>i_ih{zJ1FYy`jZfLpQB+v@|lU8JE zMFgA=7Wu>+R8!v$71>iCge}iZjeP*b#|~6;Gl87=enbeIfDO5)8ABhn-Hu2|CEYJd z%(611p8OjtK-PRi0K9{o_?#7ZIZ$grZqvf1V#LY_$ggYtp?ugua5hmbTKQge(*|cs;xJC$o3@HSYFS(}#4_6X@#b z-RkAptJ3haw4B^li_17KLP8`q^-tT;Jr%ripllph?MdZy;YCk2^|q30emmp2TQ`;M zb$LdIUu4pSd24O+D2a{Y*Yg7T7`5su_dHjd%Ic0PHknf*!CKC^Mh%kgi2S)|&Rp-9~)~ZC*(>`kaVoU{o{ANe7+M=E_hNCo=>Z(**cnW5V{F<>dFRQBp4yzkPQ}_FhuA`pNVgL9Qlr+l2pk? z8hA{E!O zj77m+SdN@nRr8i`!M(U{E}mhwb+E#m#3=$9`}fq$+P zOGK0$5EHvlsM47Hupd!Y{Iem%vRuX{g$|?8i2fw0PkJga*oRu#Itli@_!J;&p+4>#@;yZb)|SY&O(zMZwZ_?;2oo@bOjr_6rbP(CA))kFX8 zm3NNsC>`ugde?^KNU(pI{-Jz!Pwlh%(u#|!Wm>Zpm8qyWdt$yfk&AQIcU^|;A!B#t zB(3AqkXW6$qvB#qzp*1(qOSBGvN}||N(&2(Vb6rs86h7k$9}l*%2u45mwjmN-*O?5 z&AM#Nf_LTfr~bZzr8s`u;ZWA7b3+m#o2=ObJ=& zn#-8hl1tl&tA!>G+=|s)lf;de=^2eB_$p<`hOl||F#WvAtTcI(MNuSiC7@+YH9f{I z#wtzoQ^2eLqoKvaehJywFE*PO?<$QkcFS0_l8;hnt4_i;JV(p+3Ij2+>PTM%ds)e*uewORy}VGLcUhwn~ctz9UfhT%$3zLr{3NvmCS zUWN#%;AbL~GM!{%X}AeoPa6t1Tnj{{IpCvh{7p6SRmu3;8cUC?x_$1}24s0Mql(2! z8sS38C6zp_H7GO1dsYccYN+NrN3)-;R^0JT9@m#5;6>X+-f5E~Q*VcippCZB>}e=3 z_19O%o(eh0tk#vh$4PQX6&Q|!fFPwP*c8PZAorXVQ7_vn9#Murl6fb5$8A`WoThtV z@m ztkazbns{=jcFDclDU_&l_4ehD_hh$6BPOMOIw*yLACO&LUz(IfoQtM?NUNJ{MYm(L z8uBq^mRh3uJ8H8KS&;f!-DEGpVm7Gjd{e{4#XY1IsDHAo?7)t$T6tx^)bOu@E=w4a z7H=r!s<@q+NEa_&HWvh3h5uM9n%*^T$M+vDD#(c}xaf^@)EpO3`ei{SU+C+<=9c}WHX;gElMF@r1 zRIU`6sjK(>XeNOHdoeoxQ-gWTZ-BM)P!Hu91FO*hsw7(4HmjlysUnmw12QDOI~kda zvcmJiD;sj_JPNuIyIWO-GMz-8m1K<-Xn0-jg3f_mLJ)T)6@rS=1L9v&?~QV@3-2&@ zufSQma|H;SaSjYn^6zSIn=)s(90ahEbk>E_*B-rCfADyXt8B}q%hpkRcE5j> zbAEkaZM7HPvW2e6?Vp8q^NG6Q^}dCyH48^4^XH6#hL>~u554^#o4OkX1#3TaPMP~F zekxA${y2J&(wMq(4w8aXI?lcuNn;!}8cyslhQmd?Y}+$<3%v%jTqd1@aXU3^Bc@#v z=cpz3=_2p(a&QxKm27%bBOSnvHdHvzy~{T_y*qd&`e@yz|psO1$=&fp+>3B>CA zwIr{ZpE_Tgn^h7QDGAf3cavHdty7#-Km8aaINYH^S{_&G?i*T2XSvqe`$~PQ2D898 z6e>R~x5$f2kMkZx1u^#aLm>+VdOU}L?1)ljYnd8JWg}M)pNY6NRdU?!-lzz{qSPC^ z(&r)%C4rnobX&&0X^S!Iiu}zN>-S*rMzJp5+u2HX=o(rG2^j~w51yX&7mW3g>z5OD9D<8*M27@=X z5r$|lP4v+VI((#iuO}guIXJiaIFxsfEP1xYzZYnKXywS_0AlDRD2X) z0&huprIUY_mOI?6RFuOmls@*LN?3yOY1O;1CAdCXkF;obMZ6LiodkQC%z;$qs!zMk zyf67a%F($yKTL2T5_T4Gj^pIs-J3BsDsR``DD30grg@E0K0wwX+o^SR})`LMgY+U2uzp&CV|vS~v}o_6M*Se5*P zYVW|y?5Fy|0cOXiuQbYd$)4&x70%8{Y=_n?r+RX7OIfGv=?^oC!G+dPK9o3kj2pGq z;b)K0La2krm~fFot4Annb=O?R*iSFG8El8)(LS%wOZ8^Y$`e+d(!Dxsx>tARZgI*- zyf@xu3sTv>cWZansqQ_j7!tYsV_lNrdI6ACgzes8Lq&IdL0Mc@9X57*Pl4SuU>CjyJU{-il*T%E|Pj;O9H!DpP<=uQk%hv=x8YMPeT zF829oLqemRNiADw?0jKX7u<=}Q~y)th_%#{bV}3c-KgtN?iGhXNU|8_OY1&{b*FIX z7W>uB(FOnQ8pJmI5iV7%fB)X6-`_UeWm*}z4N*oj~s#$&wHP$SuACJPT zd3Z4Bhu9oUp)~dMX}7H|2A4vvp_dUO&(gl9puzL%>544yh^z_JEN8Oh^B*=Z1pDmk zhxutN^bC^;t+Y9o`@tSNZX&}|Hduu|wV_JL5=~}Vfv6HgW?6-ZHG+(l1zo9JUr8sp zL5-m_Pe>E^KV?G8l&P~SL^c>QWh^8|@8l&zmJ?OZGmR<7B|Uo*;`3Ojj^0g5g-llz zVzSeU>Q#S`m3iDTBVV%dJY8j3_ZcW`XdT?W$aKwjGZ_o{P_N74q?)uunZBfAQiU;Y zUCNXp%DkYGR=J&ewULjm6a@Qb@b>bz@Y*IYyhfU20*Vd`T(M9905E~!H5GG569Z!> z;DUv+t&RPkuYd~`KXI+(SEO#4lO737m4E7JDI|cmB&9c@z+UT;f+8#2`YV{6_@{EY zRt-UmhUvNTujVxP8cgy$M-Jh0%iT(?l)T&KbXq?cOXEy0mG)vE6=aAo%Y5-5OeVb*HqyfGkH~co4{(nmNQB z2M<1o#Fqq+unPt9iCR|3Sne4z4$&V0BzX!4sbj15$Wc4Nl-}uv?~f4bVkU8)-Z$KL zxSN}cfi2~M`7(WVCcn4Fppz?1$1%F2gWcT{XETojv)_n?F}8n4;N{lIxcYXm5aFw% zjSt_Yi~II;NG_~&50-6|n{^Mub=6N$3xcsfj-yG;EvKHLIXtp1I6B64Q$+u4PE?22ahkRTmj)0rD`5BfjP%Ed?KpZY&Zw-JsO zH3=8<^|1C5XY`Wdo@_jp;o6&ug7V-ewYbOQ#)t>P++KdI4Ql7t5jrJqr%`_k5RRqE zj6>N432B@j{azwKhxYE!Bbt6ED-a^Skr3wfx-34T`#|(9rj~4UOdq+-un46W{gC*Z z(b(aptlwFlp8%v;U!FgmFE9Tx|JWg={k}t5Y*Y#gFK;v+rZ6_>sM;uj1%=ZjC!$l-1 zY1AK1O9xSacMx#@sKR5F(;JPVz$QI48D+uf`zS!if$;U*DOE5E>#-!1goq*(K(i&ilq@n&J$tiV4rHgq*!YRKr3As$#UaEmvQZw zz}|d1?TN8%hUJ&$e80qOz`_t6bt*7Q{+_72Yn%a^tSmpF_&nzd_j$3IGf^ih$1zQ-*LuQseMa?K{`rwnqy+tW${FX(yhIlwO zmM(+r-XLjoVWBBZLin!noy*GXFsJ}EC5eFX( zDF3Dl+NJ-eZq8FB5W+VDE*dGwu9Z~hsS&M)GCCj`xu~W!m99&gYo=aHPu_DR1W>#1{eeg=X*W(Cy=Pj zvs{rbi|OK>vb5v0e%WpG3Dq~hfTgFZPN$-jZi^sY?yE(ItJH%C$ z0UtyV+!%m0`O$iOyqnQZ2M!;1v)@DTY&IS|9xW&bzRDKmaTl5xc~d`Ys0jsK6GuUY zC#zIOay0%b+^Xgoe!Bbk(d&9uy^eQ}$Vgyf*yV1}5B+<~wdg<@_09-5l}_wYuguI> zHHp0uXS2mzhDXN}7!k(4)_|=9Z;K`8)VD^zxIj{4k=u>zM z-rLm2mwH>o{)8BHWrHU+BYJbL;xzuIqfflTC&Ob#{9OjbMd4f67wn6M4q46{9qVTjdm(So zC-vfq5_22nf{sS>hJ|D?ub-BQn5QlExh*Rl%C*6m zoHUh;uQi%GHPhOh&M%k_KdLgR=u+^jpM|EQAy2Zh)5q6swdUITeHPTyj+bBZUq!2s zD7S~@cJkCfyGQMOGw`A-DA=yPv5GKtMdp43TcFVJ6d+2oOUq*wTmJ3_D}Pjl(PLY7 zdm)0lqPE!B7{Jh#7WJ-ZfUOW3Y4N?GPyxKBXvDgs@w z%9Pt45T-9Wz7cMHsZ^e18BEIdQL9_u1@&2iYg2%yc(=_`SS6EG zynl~y0(o6kU4D>0tGap$-*zi04fe>_I$wD<9{pfxdAg9dy`8+|Lw${X+tj|F)HE``Yfh6}tETk7!aHOI0#Zq5`Il*&d1sKE{)ND`#b zc)a76L-CD@7Xen4z6tB(Gk10nd%27df~xotFq5;(BvYTBSEpN|PK`DT%{^M~wREY! zG_C(oN2rBzLW7ynjAXH)`SlQup!Fc-bvXsXlA>lg{Dd2|-7e4o*} zy(&)22-k+Clt_+~O!QAa0+t({pfwp8mJ9t#6DMIEM8eq*=hWj#b&q^+ zs*HZcHRm+C$~j4K`<-9JP+|#%#X87BTZ6wwmlpcnf7xa_`3fyeW>Kth+OfZW2jRu` zb{l*>^Sm&JmP8B7Af;z6JopKK&=!pE$fL|p&iojqf#VPhj~DZaYPA2NX(}iQtyB?! zK^{W@63O@lUz24My_-8593MjIyfiKplzqpc1TC8OeT-0*`Um$@K;3Pm%dQR1i?k#xk7{`vBJkpQ7r zBNHFXxHp%Neq!^cI@mNE8ipIOve}L>hE?v3ltX#7y9HJ%oi^7s z8un~ zI7nP391}~m0U9`i7XIfQ zc)iXr-;-m6i;(liwV5JKfzMKsU{P zj>`yxaj|R(xe&E9!sBU|eIs+$u5xK7U#!=w(eMMK#mi^o2yPC6z+wkKKE^KjPN7)k z=N<_+D!b#NEz$} zzEJvJt4#I>LFZ=%FBzveMsoLgO;d2(rf(6(yYNo*Ii;_eS-4wzd%Yv3Hr)iijs&YObaN*+E91EPt3*IWMfLjb96tP6dsx%BG z#5_c|A#4Qcp*2MsvjGQp3>LiD;ondwyzf#gU*#Umej3b}Tw+xpY}8;yAthZG|0Z@L zlFgwcx&QQ(WR8WSW>=~iQ5Ga}-j)|2#);kQeK4hQPs}yl+mdvqDEO(D9DPZI9Ikdu z{FBZm6IS1hR*S&y1T=M8@6%U52YNu|P9cedJZYjt45`{ZOXy`WWPS|RjO+JMr6&`P z(iT{m6t(TLa$5hXu7%BXXM%ywILyHvm(SV*_%D>tB0q|-)5xH969~F9MtgGkIosTE zHWus`j+N*QR?d>@a~|)v`%VSx67=&h<7(b1^4t$j3>Yy)b-hpDcjAZoa;JeJ8*&hY z9mpX`2ddf0nZx*ZBs4UZPlb6yki_Y2^!$AXY| z#u2DG;*nI(-`@FE*mKBe)#o?#$aE;8an5;fz{|MwRJMTyL%g(kf_X`otWmB@LBZg~ z!)PEdqk?=X#4l9UTgGJxSiMlw4&0n$Hr;b17r`s6rUt_iyKAkGh>5$_3IcQHWG$ae z6XN^{XWgKrvo#m!eBIZxpC|pP%M$DZyCRfg$0ztsl)ycGRckfm&|nBd;Q~^Z>VRuNmDD)HUMYk4~-Yty-&vJp>+ASZl0D1laTx2bh=yFQ*^x7Anb6zIx z)U{!Efjg_OP$TTVy08a`Vl3j&8DI5x7m23x(e%+E=pd0nFbbb~aGNCnwq6=HcRewX9BfX7Lu>)Q9 z_D&D?+mb9IbCD4gu+UENosqHCl*0%fT_k4OFiFyNmSBXEm`d<@F;^_gCp1m)iSBM4 zp-5lWzP(sp$eo1s=I2X#^*owSB9{IA4Lu<8CjQ6|O1+Vc=iXBa%X<6WV@N7?yWXZ< zk<1a4^<_orx1baWobX*N2Ygr|UCHrq8>QcfC2ztV%KN^4*4Zh+y%K~U8y~bYu>%Pa zX7FJ@%E@%ZMn-{iko}m4d?C%j(u5ayNIn-a^&u*;MzA1Wuk?Fz5Zw7tnJd^D^Qb+n z5Hg~fkO(LE9(&{P9pqa4r7wsMe?ehA{EuQswqZr3*hrpcGcSxT-xe)wa&S;L%44St z_;Qu^F!K-J=`wya@U?K#nsS_WOuEx#eG~QJuG9i|jg$jl>cY#^!8~Pp9XA*duS60H1|({tyfvr8j%rleN{uzb8A>CNz%yZZGA%Ug5%bGE zbf}a14L;OzyebLID}C&dY9Z{GE!naX8n5k(WaYMf-VDbY7{VnvL)g4Ts6zkS8kZk! z>bPD5iI&6F_VTYJpKe3?I%bs5PuIR%y6ke`WY#@(oJDsBHss|kK486AZ)NkY1DZ*= z(f2%^8zgp|ZRxqVOdnp-KPmR77{OS%vd{Tr@6G#7{*e=}R zrvOQfBPeiR9a>eQO+!|YK^Pbg9@%LFHVP5jo>U?)eG-8)?Gg&3!#WnwJamKfLE3zR zQ=_>*W8rIkk)(!oP;&g~itwlf1#(1`d4b(^pxw{&4F{e869{?x`b|1`iiwy-0iO)= z=lXz#f>flccS_|f@!P$wHc0c8vd>2JjV&Ajr8Hd@HQOocBDajmuYgR*Y~Ey-b}cF zi>*#pxz+<^N5(L2GGXj(khI<&j=kX~?VzU9)9)n7u&mjBB~2SX>C7ENy_w%95^GiN zz%GW->#2ES&6UB)iotGWsiN{W)8>kin+G%P;^y6mY7C0Brkp zfc%m0{p&YXXAA3J0aO_~GhpP@a}ONQ2+UvnJ435lRAw(zw;K=G9tjM-BKKvg z0Ji)Oe{4499GMxiE z6lJ-mYXJfPr~~B!s^^d6Plz+$ZxB&e6INej}2q|4uYFu{Kq9 zHgI_#^gAfh>o<^!fuos;^M9=QAF59Sf|_29?FN9hLL&Z8)sKJrKS2M7 zjDL3oc)#gvHuV1>{!>abVCOO#vCy}H5(;Po0Jy)Qj-mj7Uk*Uo+0n(w*~QTW=yHKV zIywJp{D+tpK$0g!_Eioj01yiY01*C0dYAUUNxzZ*LHlLTF>!G3q=DlV=YQGrb_Q_Y zkDa596TPX8`+s%6jX(yP4mD>DaJ+*69m4PSY>@TujQ>OD%$`CYhJdEh!vyh#lVsLyUAI; z{`2hrB1#m{L>PuY`LnA50HdrB0HEdlas25k^o{PHX!^(!#4Yk_AlEJ{?7WhWaz(G%a*^f{xvoF@3engyZwvi;R*8JR&xIi m`M0puUy#1wze9ctVktq32FAQ9Vt}Rq_ - - - - Debug - AnyCPU - 9.0.21022 - 2.0 - {BE0785A4-0CCB-44C5-A8C0-1AA2245E71F4} - WinExe - Properties - LFP_Manager - LFP_Manager_GL - v4.5.2 - 512 - - - 3.5 - - - - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - true - x86 - false - - - pdbonly - false - bin\Release\ - DEBUG;TRACE - prompt - 4 - true - x86 - false - - - Resources\AMO_LOGO.ico - - - - - True - - - True - - - True - - - - - True - - - True - - - True - - - True - - - True - - - True - - - - - True - - - - True - - - True - - - True - - - True - - - True - - - - True - - - True - - - - True - - - - True - - - - - - ..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.dll - - - ..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.SqlServer.dll - - - dll\IPAddressControlLib.dll - - - dll\log4net.dll - - - False - dll\SnmpSharpNet.dll - - - - - - 3.5 - - - - ..\packages\Stub.System.Data.SQLite.Core.NetFramework.1.0.113.3\lib\net451\System.Data.SQLite.dll - - - ..\packages\System.Data.SQLite.EF6.1.0.113.0\lib\net451\System.Data.SQLite.EF6.dll - - - ..\packages\System.Data.SQLite.Linq.1.0.113.0\lib\net451\System.Data.SQLite.Linq.dll - - - - - - - - - 3.5 - - - 3.5 - - - - - - - - dll\Tftp.Net.dll - - - - - UserControl - - - ucCalibration.cs - - - UserControl - - - ucCalibrationView.cs - - - UserControl - - - ucCommConfig.cs - - - UserControl - - - ucDataLog.cs - - - UserControl - - - ucEtcStatusView.cs - - - UserControl - - - ucEventLog.cs - - - UserControl - - - ucHistroy.cs - - - UserControl - - - ucMainStatus.cs - - - UserControl - - - ucModuleMainB.cs - - - UserControl - - - ucParamSet.cs - - - UserControl - - - ucShelfStatus.cs - - - UserControl - - - ucStatus.cs - - - UserControl - - - ucTargetControl.cs - - - UserControl - - - ucTftpClientcs.cs - - - UserControl - - - ucModuleMainA.cs - - - - - Form - - - fmxEtcStatus.cs - - - Form - - - fmxExcelFile.cs - - - Form - - - fmxFwImageConverter.cs - - - Form - - - fmxCalibration.cs - - - Form - - - fmxFwUpdate.cs - - - Form - - - fmxFwUpdateTftp.cs - - - Form - - - fmxHistory.cs - - - Form - - - fmxInventoryConfig.cs - - - Form - - - fmxModuleDetail.cs - - - Form - - - fmxParamConfig.cs - - - Form - - - fmxSystemSet.cs - - - Form - - - fmxWait.cs - - - - - - - - - - - - - - - - Form - - - fmxCommConfig.cs - - - Form - - - fmxMain.cs - - - - - - - - - - - - - - ucCalibration.cs - - - ucCalibrationView.cs - - - ucCommConfig.cs - - - ucDataLog.cs - - - ucEtcStatusView.cs - - - ucEventLog.cs - - - ucHistroy.cs - - - ucMainStatus.cs - - - ucModuleMainA.cs - - - ucModuleMainB.cs - - - ucParamSet.cs - - - ucShelfStatus.cs - - - ucStatus.cs - - - ucTargetControl.cs - - - ucTftpClientcs.cs - - - fmxEtcStatus.cs - - - fmxExcelFile.cs - - - fmxFwImageConverter.cs - - - fmxCalibration.cs - - - fmxCommConfig.cs - - - fmxFwUpdate.cs - - - fmxFwUpdateTftp.cs - - - fmxHistory.cs - - - fmxInventoryConfig.cs - - - fmxMain.cs - - - fmxModuleDetail.cs - - - fmxParamConfig.cs - - - fmxSystemSet.cs - - - fmxWait.cs - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - True - - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - - - - - - - - - - if not exist "$(TargetDir)\Library" mkdir "$(TargetDir)\Library" -if not exist "$(TargetDir)\Library\kerneldlls" mkdir "$(TargetDir)\Library\kerneldlls" -move "$(TargetDir)\*.dll" "$(TargetDir)\Library" -move "$(TargetDir)\*.pdb" "$(TargetDir)\Library" -move "$(TargetDir)\*.xml" "$(TargetDir)\Library" -copy "$(ProjectDir)\dll\*.dll" "$(TargetDir)\Library" -copy "$(ProjectDir)\dll\kerneldlls\*.*" "$(TargetDir)\Library\kerneldlls" - -if not exist "$(TargetDir)\sql" mkdir "$(TargetDir)\sql" -copy "$(ProjectDir)\DB Schema\*.sql" "$(TargetDir)\sql" - - - if exist "$(TargetDir)\library" rmdir /s /q "$(TargetDir)\library" -if exist "$(TargetDir)\sql" rmdir /s /q "$(TargetDir)\sql" - - - - 이 프로젝트는 이 컴퓨터에 없는 NuGet 패키지를 참조합니다. 해당 패키지를 다운로드하려면 NuGet 패키지 복원을 사용하십시오. 자세한 내용은 http://go.microsoft.com/fwlink/?LinkID=322105를 참조하십시오. 누락된 파일은 {0}입니다. - - - - - - - - - \ No newline at end of file diff --git a/LFP_Manager/LFP_Manager_GL.csproj.bak b/LFP_Manager/LFP_Manager_GL.csproj.bak deleted file mode 100644 index ea0297e..0000000 --- a/LFP_Manager/LFP_Manager_GL.csproj.bak +++ /dev/null @@ -1,526 +0,0 @@ - - - - - Debug - AnyCPU - 9.0.21022 - 2.0 - {BE0785A4-0CCB-44C5-A8C0-1AA2245E71F4} - WinExe - Properties - LFP_Manager - LFP_Manager_GL - v4.5.2 - 512 - - - 3.5 - - - - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - true - x86 - false - - - pdbonly - false - bin\Release\ - DEBUG;TRACE - prompt - 4 - true - x86 - false - - - Resources\AMO_LOGO.ico - - - - - True - - - True - - - - - True - - - True - - - True - - - True - - - True - - - True - - - - - True - - - - True - - - True - - - True - - - True - - - True - - - - True - - - True - - - - True - - - - True - - - - - ..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.dll - - - ..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.SqlServer.dll - - - dll\IPAddressControlLib.dll - - - dll\log4net.dll - - - False - dll\SnmpSharpNet.dll - - - - - - 3.5 - - - - ..\packages\Stub.System.Data.SQLite.Core.NetFramework.1.0.113.3\lib\net451\System.Data.SQLite.dll - - - ..\packages\System.Data.SQLite.EF6.1.0.113.0\lib\net451\System.Data.SQLite.EF6.dll - - - ..\packages\System.Data.SQLite.Linq.1.0.113.0\lib\net451\System.Data.SQLite.Linq.dll - - - - - - - - - 3.5 - - - 3.5 - - - - - - - - dll\Tftp.Net.dll - - - - - UserControl - - - ucCalibration.cs - - - UserControl - - - ucCalibrationView.cs - - - UserControl - - - ucCommConfig.cs - - - UserControl - - - ucDataLog.cs - - - UserControl - - - ucEtcStatusView.cs - - - UserControl - - - ucEventLog.cs - - - UserControl - - - ucHistroy.cs - - - UserControl - - - ucMainStatus.cs - - - UserControl - - - ucModuleMainB.cs - - - UserControl - - - ucParamSet.cs - - - UserControl - - - ucShelfStatus.cs - - - UserControl - - - ucStatus.cs - - - UserControl - - - ucTargetControl.cs - - - UserControl - - - ucTftpClientcs.cs - - - UserControl - - - ucModuleMainA.cs - - - - - Form - - - fmxEtcStatus.cs - - - Form - - - fmxExcelFile.cs - - - Form - - - fmxFwImageConverter.cs - - - Form - - - fmxCalibration.cs - - - Form - - - fmxFwUpdate.cs - - - Form - - - fmxFwUpdateTftp.cs - - - Form - - - fmxHistory.cs - - - Form - - - fmxInventoryConfig.cs - - - Form - - - fmxModuleDetail.cs - - - Form - - - fmxParamConfig.cs - - - Form - - - fmxSystemSet.cs - - - Form - - - fmxWait.cs - - - - - - - - - - - - - - - - Form - - - fmxCommConfig.cs - - - Form - - - fmxMain.cs - - - - - - - - - - - - - - ucCalibration.cs - - - ucCalibrationView.cs - - - ucCommConfig.cs - - - ucDataLog.cs - - - ucEtcStatusView.cs - - - ucEventLog.cs - - - ucHistroy.cs - - - ucMainStatus.cs - - - ucModuleMainA.cs - - - ucModuleMainB.cs - - - ucParamSet.cs - - - ucShelfStatus.cs - - - ucStatus.cs - - - ucTargetControl.cs - - - ucTftpClientcs.cs - - - fmxEtcStatus.cs - - - fmxExcelFile.cs - - - fmxFwImageConverter.cs - - - fmxCalibration.cs - - - fmxCommConfig.cs - - - fmxFwUpdate.cs - - - fmxFwUpdateTftp.cs - - - fmxHistory.cs - - - fmxInventoryConfig.cs - - - fmxMain.cs - - - fmxModuleDetail.cs - - - fmxParamConfig.cs - - - fmxSystemSet.cs - - - fmxWait.cs - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - True - - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - - - - - - - - - - if not exist "$(TargetDir)\Library" mkdir "$(TargetDir)\Library" -if not exist "$(TargetDir)\Library\kerneldlls" mkdir "$(TargetDir)\Library\kerneldlls" -move "$(TargetDir)\*.dll" "$(TargetDir)\Library" -move "$(TargetDir)\*.pdb" "$(TargetDir)\Library" -move "$(TargetDir)\*.xml" "$(TargetDir)\Library" -copy "$(ProjectDir)\dll\*.dll" "$(TargetDir)\Library" -copy "$(ProjectDir)\dll\kerneldlls\*.*" "$(TargetDir)\Library\kerneldlls" - -if not exist "$(TargetDir)\sql" mkdir "$(TargetDir)\sql" -copy "$(ProjectDir)\DB Schema\*.sql" "$(TargetDir)\sql" - - - if exist "$(TargetDir)\library" rmdir /s /q "$(TargetDir)\library" -if exist "$(TargetDir)\sql" rmdir /s /q "$(TargetDir)\sql" - - - - 이 프로젝트는 이 컴퓨터에 없는 NuGet 패키지를 참조합니다. 해당 패키지를 다운로드하려면 NuGet 패키지 복원을 사용하십시오. 자세한 내용은 http://go.microsoft.com/fwlink/?LinkID=322105를 참조하십시오. 누락된 파일은 {0}입니다. - - - - - - - - - \ No newline at end of file diff --git a/LFP_Manager/LFP_Manager_JP.csproj.bak b/LFP_Manager/LFP_Manager_JP.csproj.bak deleted file mode 100644 index ed92028..0000000 --- a/LFP_Manager/LFP_Manager_JP.csproj.bak +++ /dev/null @@ -1,411 +0,0 @@ - - - - Debug - AnyCPU - 9.0.21022 - 2.0 - {BE0785A4-0CCB-44C5-A8C0-1AA2245E71F4} - WinExe - Properties - LFP_Manager - LFP_Manager_JP - v4.5.2 - 512 - - - 3.5 - - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - true - x86 - false - - - pdbonly - false - bin\Release\ - DEBUG;TRACE - prompt - 4 - true - x86 - false - - - Resources\AMO_LOGO.ico - - - - True - - - True - - - True - - - True - - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - - True - - - - True - - - dll\IPAddressControlLib.dll - - - dll\log4net.dll - - - False - dll\SnmpSharpNet.dll - - - - - 3.5 - - - False - dll\System.Data.SQLite.dll - - - - - - 3.5 - - - 3.5 - - - - - - - - dll\Tftp.Net.dll - - - - - UserControl - - - ucCalibration.cs - - - UserControl - - - ucCommConfig.cs - - - UserControl - - - ucDataLog.cs - - - UserControl - - - ucEventLog.cs - - - UserControl - - - ucHistroy.cs - - - UserControl - - - ucMainStatus.cs - - - UserControl - - - ucParamSet.cs - - - UserControl - - - ucShelfStatus.cs - - - UserControl - - - ucStatus.cs - - - UserControl - - - ucTargetControl.cs - - - UserControl - - - ucTftpClientcs.cs - - - - - Form - - - fmxExcelFile.cs - - - Form - - - fmxFwImageConverter.cs - - - Form - - - fmxCalibration.cs - - - Form - - - fmxFwUpdate.cs - - - Form - - - fmxFwUpdateTftp.cs - - - Form - - - fmxHistorycs.cs - - - Form - - - fmxInventoryConfig.cs - - - Form - - - fmxParamConfig.cs - - - Form - - - fmxSystemSet.cs - - - Form - - - fmxWait.cs - - - - - - - - - - - - - - - - Form - - - fmxCommConfig.cs - - - Form - - - fmxMain.cs - - - - - - - - - - - - - ucCalibration.cs - - - ucCommConfig.cs - - - ucDataLog.cs - - - ucEventLog.cs - - - ucHistroy.cs - - - ucMainStatus.cs - - - ucParamSet.cs - - - ucShelfStatus.cs - - - ucStatus.cs - - - ucTargetControl.cs - - - ucTftpClientcs.cs - - - fmxExcelFile.cs - - - fmxFwImageConverter.cs - - - fmxCalibration.cs - - - fmxCommConfig.cs - - - fmxFwUpdate.cs - - - fmxFwUpdateTftp.cs - - - fmxHistorycs.cs - - - fmxInventoryConfig.cs - - - fmxMain.cs - - - fmxParamConfig.cs - - - fmxSystemSet.cs - - - fmxWait.cs - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - True - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - - - - - - - - - if not exist "$(TargetDir)\Library" mkdir "$(TargetDir)\Library" -move "$(TargetDir)\*.dll" "$(TargetDir)\Library" -move "$(TargetDir)\*.pdb" "$(TargetDir)\Library" -move "$(TargetDir)\*.xml" "$(TargetDir)\Library" - - - if not exist "$(TargetDir)\sql" mkdir "$(TargetDir)\sql" -copy "$(ProjectDir)\DB Schema\*.sql" "$(TargetDir)\sql" - - - \ No newline at end of file diff --git a/LFP_Manager/LFP_Manager_M20.csproj.bak b/LFP_Manager/LFP_Manager_M20.csproj.bak deleted file mode 100644 index 4641307..0000000 --- a/LFP_Manager/LFP_Manager_M20.csproj.bak +++ /dev/null @@ -1,508 +0,0 @@ - - - - - Debug - AnyCPU - 9.0.21022 - 2.0 - {BE0785A4-0CCB-44C5-A8C0-1AA2245E71F4} - WinExe - Properties - LFP_Manager - LFP_Manager_M20 - v4.8 - 512 - - - 3.5 - - - - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - true - x86 - false - - - pdbonly - false - bin\Release\ - DEBUG;TRACE - prompt - 4 - true - x86 - false - - - Resources\AMO_LOGO.ico - - - - - True - - - True - - - - - True - - - True - - - True - - - True - - - True - - - True - - - - - True - - - - True - - - True - - - True - - - True - - - True - - - - True - - - True - - - - True - - - - True - - - - - ..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.dll - - - ..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.SqlServer.dll - - - dll\IPAddressControlLib.dll - - - dll\log4net.dll - - - False - dll\SnmpSharpNet.dll - - - - - - 3.5 - - - - ..\packages\Stub.System.Data.SQLite.Core.NetFramework.1.0.113.3\lib\net451\System.Data.SQLite.dll - - - ..\packages\System.Data.SQLite.EF6.1.0.113.0\lib\net451\System.Data.SQLite.EF6.dll - - - ..\packages\System.Data.SQLite.Linq.1.0.113.0\lib\net451\System.Data.SQLite.Linq.dll - - - - - - - - - 3.5 - - - 3.5 - - - - - - - - dll\Tftp.Net.dll - - - - - UserControl - - - ucCalibration.cs - - - UserControl - - - ucCalibrationView.cs - - - UserControl - - - ucCommConfig.cs - - - UserControl - - - ucDataLog.cs - - - UserControl - - - ucEventLog.cs - - - UserControl - - - ucHistroy.cs - - - UserControl - - - ucMainStatus.cs - - - UserControl - - - ucModuleMainB.cs - - - UserControl - - - ucParamSet.cs - - - UserControl - - - ucShelfStatus.cs - - - UserControl - - - ucStatus.cs - - - UserControl - - - ucTargetControl.cs - - - UserControl - - - ucTftpClientcs.cs - - - UserControl - - - ucModuleMainA.cs - - - - - Form - - - fmxExcelFile.cs - - - Form - - - fmxFwImageConverter.cs - - - Form - - - fmxCalibration.cs - - - Form - - - fmxFwUpdate.cs - - - Form - - - fmxFwUpdateTftp.cs - - - Form - - - fmxHistory.cs - - - Form - - - fmxInventoryConfig.cs - - - Form - - - fmxModuleDetail.cs - - - Form - - - fmxParamConfig.cs - - - Form - - - fmxSystemSet.cs - - - Form - - - fmxWait.cs - - - - - - - - - - - - - - - - Form - - - fmxCommConfig.cs - - - Form - - - fmxMain.cs - - - - - - - - - - - - - - ucCalibration.cs - - - ucCalibrationView.cs - - - ucCommConfig.cs - - - ucDataLog.cs - - - ucEventLog.cs - - - ucHistroy.cs - - - ucMainStatus.cs - - - ucModuleMainA.cs - - - ucModuleMainB.cs - - - ucParamSet.cs - - - ucShelfStatus.cs - - - ucStatus.cs - - - ucTargetControl.cs - - - ucTftpClientcs.cs - - - fmxExcelFile.cs - - - fmxFwImageConverter.cs - - - fmxCalibration.cs - - - fmxCommConfig.cs - - - fmxFwUpdate.cs - - - fmxFwUpdateTftp.cs - - - fmxHistory.cs - - - fmxInventoryConfig.cs - - - fmxMain.cs - - - fmxModuleDetail.cs - - - fmxParamConfig.cs - - - fmxSystemSet.cs - - - fmxWait.cs - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - True - - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - - - - - - - - - - if not exist "$(TargetDir)\Library" mkdir "$(TargetDir)\Library" -if not exist "$(TargetDir)\Library\kerneldlls" mkdir "$(TargetDir)\Library\kerneldlls" -move "$(TargetDir)\*.dll" "$(TargetDir)\Library" -move "$(TargetDir)\*.pdb" "$(TargetDir)\Library" -move "$(TargetDir)\*.xml" "$(TargetDir)\Library" -copy "$(ProjectDir)\dll\*.dll" "$(TargetDir)\Library" -copy "$(ProjectDir)\dll\kerneldlls\*.*" "$(TargetDir)\Library\kerneldlls" - -if not exist "$(TargetDir)\sql" mkdir "$(TargetDir)\sql" -copy "$(ProjectDir)\DB Schema\*.sql" "$(TargetDir)\sql" - - - if exist "$(TargetDir)\library" rmdir /s /q "$(TargetDir)\library" -if exist "$(TargetDir)\sql" rmdir /s /q "$(TargetDir)\sql" - - - - 이 프로젝트는 이 컴퓨터에 없는 NuGet 패키지를 참조합니다. 해당 패키지를 다운로드하려면 NuGet 패키지 복원을 사용하십시오. 자세한 내용은 http://go.microsoft.com/fwlink/?LinkID=322105를 참조하십시오. 누락된 파일은 {0}입니다. - - - - - - - - - \ No newline at end of file diff --git a/LFP_Manager/LFP_Manager_TW_DBG.csproj b/LFP_Manager/LFP_Manager_TW_DBG.csproj index 4d3e056..d36cf50 100644 --- a/LFP_Manager/LFP_Manager_TW_DBG.csproj +++ b/LFP_Manager/LFP_Manager_TW_DBG.csproj @@ -289,6 +289,12 @@ fmxHistory.cs + + Form + + + fmxPacketLog.cs + Form @@ -416,6 +422,9 @@ fmxHistory.cs + + fmxPacketLog.cs + fmxInventoryConfig.cs diff --git a/LFP_Manager/Properties/AssemblyInfo.cs b/LFP_Manager/Properties/AssemblyInfo.cs index d5e6483..37d5ecd 100644 --- a/LFP_Manager/Properties/AssemblyInfo.cs +++ b/LFP_Manager/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.1.1.0")] -[assembly: AssemblyFileVersion("1.1.1.0")] +[assembly: AssemblyVersion("1.1.1.1")] +[assembly: AssemblyFileVersion("1.1.1.1")] diff --git a/LFP_Manager/Threads/csUartThread.cs b/LFP_Manager/Threads/csUartThread.cs index 24cde53..3053009 100644 --- a/LFP_Manager/Threads/csUartThread.cs +++ b/LFP_Manager/Threads/csUartThread.cs @@ -1,7 +1,7 @@ using System; - -using System.Threading; +using System.Collections.Concurrent; using System.IO.Ports; +using System.Threading; using System.Windows.Forms; using LFP_Manager.DataStructure; @@ -12,6 +12,7 @@ namespace LFP_Manager.Threads public delegate void UartDataUpdate(object sender, ref DeviceSystemData aSystemData); public delegate void UartDataRecv(byte[] data); public delegate void UartDataPrint(object sender, string msg); + public delegate void UartPacketLogEvent(DateTime timestamp, bool isTx, byte[] packet, string description); class csUartThread { @@ -28,23 +29,14 @@ namespace LFP_Manager.Threads private bool AutoUartTx = true; private int addr_ch = 0; - private ushort RequestRegAddr = 0x0000; //Byul Init 0x0000 - private ushort RequestRegLen = 50; - private bool rFlag = false; - private int wFlag = 0; - private ushort ReadRegAddr = 0x0000; //Byul Init 0x0000 - private ushort ReadRegLen = 0x0000; //Byul Init 0x0000 - - private ushort WriteRegAddr = 0x0000; //Byul Init 0x0000 - private short WriteCoilRegData = 0; - private byte[] WriteRegData; - - private TUartTxBuff uartTxBuff = null; + // Thread-Safe TX Queue + private readonly ConcurrentQueue txQueue = new ConcurrentQueue(); public event UartDataUpdate OnUpdate = null; public event UartDataRecv OnDataRecv = null; public event UartDataPrint OnPrint = null; + public event UartPacketLogEvent OnPacketLog = null; #endregion @@ -56,8 +48,6 @@ namespace LFP_Manager.Threads Config = aConfig; SystemData = aSystemData; - uartTxBuff = new TUartTxBuff(); - serialComm = new Thread(uartCommThread); } @@ -130,13 +120,10 @@ namespace LFP_Manager.Threads private void sErrorReceived(object sender, System.IO.Ports.SerialErrorReceivedEventArgs e) { - // - //csMakeDataFunction.SetData() } private void sPinChanged(object sender, System.IO.Ports.SerialPinChangedEventArgs e) { - // } private bool Open(string cPort, int cBaudrate) @@ -174,7 +161,7 @@ namespace LFP_Manager.Threads #endregion - #region PUBLIC FUNCTION + #region PUBLIC TX FUNCTIONS (QUEUE BASED) public void SetPolling(bool flag, int sId, ref DeviceSystemData aSystemData) { @@ -189,52 +176,66 @@ namespace LFP_Manager.Threads AutoUartTx = autoTx; } - public void SetWriteCoilReg(ushort WriteAddr, short WriteData) + ///

+ /// 공통 TX 큐 삽입 메서드 + /// + public void EnqueueTx(TUartTxItem item) { - wFlag = 2; - WriteRegAddr = WriteAddr; - WriteCoilRegData = WriteData; - - for (int i = 0; i < 500; i += 10) + if (item != null) { - Thread.Sleep(10); - Application.DoEvents(); + txQueue.Enqueue(item); } } + /// + /// 원시 패킷 전송 (FW 업데이트 등) + /// + public void SendRawPacket(byte[] rawData, bool expectReply = true) + { + if (rawData == null || rawData.Length == 0) return; + TUartTxItem item = new TUartTxItem(UartTxType.Raw, SystemId, 0, (ushort)rawData.Length, rawData, expectReply, Config.RecvWaitTime); + EnqueueTx(item); + } + + /// + /// 코일 레지스터 쓰기 (TX 큐 삽입) + /// + public void SetWriteCoilReg(ushort WriteAddr, short WriteData) + { + byte[] data = csSerialCommFunction.MakeWriteCoilData((ushort)SystemId, WriteAddr, WriteData); + TUartTxItem item = new TUartTxItem(UartTxType.WriteCoil, SystemId, WriteAddr, 1, data, true, Config.RecvWaitTime); + EnqueueTx(item); + } + + /// + /// 레지스터 쓰기 (TX 큐 삽입) + /// public void SetWriteReg(ushort WriteAddr, byte[] WriteData, bool ReplyFlag) { - wFlag = 1; - - WriteRegAddr = WriteAddr; - - TUartTRxData uartTRxData = new TUartTRxData(); - uartTRxData.type = wFlag; - //uartTRxData.data = csSerialCommFunction.MakeWriteRegisterData((ushort)SystemId, WriteRegAddr, WriteData); - uartTRxData.data = WriteData; - uartTRxData.length = uartTRxData.data.Length; - - uartTxBuff?.PutBuff(uartTRxData); + if (WriteData == null) return; + TUartTxItem item = new TUartTxItem(UartTxType.WriteReg, SystemId, WriteAddr, (ushort)WriteData.Length, WriteData, ReplyFlag, Config.RecvWaitTime); + EnqueueTx(item); } + + /// + /// 레지스터 읽기 (TX 큐 삽입) + /// public void SetReadReg(ushort ReadAddr, ushort ReadLen, bool ReplyFlag) { - wFlag = 4; - ReadRegAddr = ReadAddr; - ReadRegLen = ReadLen; + byte[] data = csSerialCommFunction.MakeReadRegisterData((ushort)SystemId, csSerialCommFunction.READ_HOLDING_REG, ReadAddr, ReadLen); + TUartTxItem item = new TUartTxItem(UartTxType.ReadReg, SystemId, ReadAddr, ReadLen, data, ReplyFlag, Config.RecvWaitTime); + EnqueueTx(item); } + /// + /// 파라미터 레지스터 쓰기 (TX 큐 삽입) + /// public void SetParam(ushort WriteAddr, short[] WriteData) { - wFlag = 1; - - WriteRegAddr = WriteAddr; - - TUartTRxData uartTRxData = new TUartTRxData(); - uartTRxData.type = wFlag; - uartTRxData.data = csSerialCommFunction.MakeWriteRegisterData((ushort)SystemId, WriteRegAddr, WriteData); - uartTRxData.length = uartTRxData.data.Length; - - uartTxBuff?.PutBuff(uartTRxData); + if (WriteData == null) return; + byte[] data = csSerialCommFunction.MakeWriteRegisterData((ushort)SystemId, WriteAddr, WriteData); + TUartTxItem item = new TUartTxItem(UartTxType.WriteParam, SystemId, WriteAddr, (ushort)WriteData.Length, data, true, Config.RecvWaitTime); + EnqueueTx(item); } public void SendProcessFromApp(int sId, int mode, int index, int flag, ref DeviceParamData aParam, ref DeviceCalibration aCalib) @@ -315,13 +316,8 @@ namespace LFP_Manager.Threads break; case csConstData.UART_PARAM_LIST.DEFAULT_PARAM: // Default Parameter int i = 0; - - Forms.fmxWait WaitForm = new Forms.fmxWait(); - WaitForm.StartPosition = FormStartPosition.CenterScreen; - try { - //WaitForm.Show(); data = new short[1]; i = csConstData.UART_PARAM_LIST.CELL_UNDER_VOLTAGE; @@ -385,10 +381,6 @@ namespace LFP_Manager.Threads catch (Exception) { - } - finally - { - //WaitForm?.Close(); } break; case 15: // Capcity change @@ -413,16 +405,11 @@ namespace LFP_Manager.Threads case 18: data = new short[1]; i = csConstData.UART_CALIB_ADDR_LIST.ANTI_THEFT; - //data[0] = (short)aCalib.AntiTheft.Comm; SetParam((ushort)(i + 0), data); data[0] = (short)aCalib.AntiTheft.GyroScope; SetParam((ushort)(i + 1), data); break; case 19: data = new short[1]; i = csConstData.UART_CALIB_ADDR_LIST.ANTI_THEFT; - //data[0] = (short)0; SetParam((ushort)(i + 0), data); - data[0] = (short)0; SetParam((ushort)(i + 1), data); - - //data[0] = (short)aCalib.AntiTheft.Comm; SetParam((ushort)(i + 0), data); data[0] = (short)aCalib.AntiTheft.GyroScope; SetParam((ushort)(i + 1), data); break; default: @@ -471,176 +458,84 @@ namespace LFP_Manager.Threads #endregion - #region TX BUFFERING + #region POLLING GENERATOR - private byte[] MakeWriteRegData(ushort DevID, ushort WriteAddr, ushort WriteLen, ref ushort[] WriteData) + /// + /// 다음 주기 Polling 패킷 아이템 생성 + /// + private TUartTxItem CreateNextPollingItem() { - byte[] wData = null; + ushort reqAddr = 0x0000; + ushort reqLen = 50; + ushort sCmd = csSerialCommFunction.READ_HOLDING_REG; - if (WriteLen > 0) + switch (addr_ch) { - - } - - return wData; - } - - private string MakeCheckSum(byte[] sData, int len) - { - string result = ""; - int checksum = 0; - - for (int i = 0; i < len; i++) - { - checksum += sData[i + 1]; - } - - checksum = ~checksum + 1; - - result = String.Format("{0:X2}{1:X2}", (byte)(checksum >> 8), (byte)checksum); - - return result; - } - - private byte[] MakeTxDataVision(ref bool wData) - { - byte[] sData = null; - - if ((uartTxBuff != null) && (uartTxBuff.CheckBuff())) - { - TUartTRxData sBuff = uartTxBuff.GetBuff(); - if (sBuff != null) - { - sData = sBuff.data; - wData = true; - rFlag = true; - } - } - else if (wFlag == 2) - { - sData = csSerialCommFunction.MakeWriteCoilData((ushort)SystemId, WriteRegAddr, WriteCoilRegData); - if (wFlag != 0) wFlag = 0; - rFlag = true; - } - else if (wFlag == 3) - { - sData = WriteRegData; - if (wFlag != 0) wFlag = 0; - rFlag = true; - } - else if (wFlag == 4) - { - sData = csSerialCommFunction.MakeReadRegisterData((ushort)SystemId, csSerialCommFunction.READ_HOLDING_REG, ReadRegAddr, ReadRegLen); - if (wFlag != 0) wFlag = 0; - rFlag = true; - } - else - { - if (UartPolling && AutoUartTx) - { - ushort sCmd; - switch (addr_ch) - { - case 0: - addr_ch = 1; - RequestRegAddr = 0x0000; //Byul Init 0x0000 - RequestRegLen = 0x0031; - sCmd = csSerialCommFunction.READ_HOLDING_REG; - rFlag = true; - break; - case 1: - addr_ch = 4; - RequestRegAddr = 58; - RequestRegLen = 138-58; - sCmd = csSerialCommFunction.READ_HOLDING_REG; - rFlag = true; - break; - case 2: - addr_ch = 3; - RequestRegAddr = 0x3078; - RequestRegLen = 8; - sCmd = csSerialCommFunction.READ_COIL_STATUS; - rFlag = true; - break; - case 3: - addr_ch = 4; - RequestRegAddr = 0x502E; - RequestRegLen = 1; - sCmd = csSerialCommFunction.READ_COIL_STATUS; - rFlag = true; - break; - case 4: - if (Config.ModuleQty > 1) - { - SystemId++; - if (SystemId > Config.ModuleQty) - { - SystemId = 1; - } - } - addr_ch = 0; - sCmd = csSerialCommFunction.NO_CMD; - rFlag = false; - break; - default: - addr_ch = 0; - RequestRegAddr = 0x0000; //Byul Init 0x0000 - RequestRegLen = 100; - sCmd = csSerialCommFunction.READ_HOLDING_REG; - break; - } - if (sCmd == csSerialCommFunction.NO_CMD) - { - sData = null; - } - else - { - sData = csSerialCommFunction.MakeReadRegisterData((ushort)SystemId, sCmd, RequestRegAddr, RequestRegLen); - } - } - } - return sData; - } - - private byte ReqCmd = 0x42; - private byte RequstedCmd; - - private byte[] TxProcess() - { - byte[] result = null; - - switch (ReqCmd) - { - case 0x42: // Get the number of PACK - csSerialCommFunction.MakeTxData(0x01, ReqCmd, 2); - RequstedCmd = ReqCmd; - ReqCmd = 0x46; + case 0: + addr_ch = 1; + reqAddr = 0x0000; + reqLen = 0x0031; // 49 + sCmd = csSerialCommFunction.READ_HOLDING_REG; break; - case 0x46: // Ger the analog information - csSerialCommFunction.MakeTxData(0x01, ReqCmd, 2); - RequstedCmd = ReqCmd; - ReqCmd = 0x46; + case 1: + addr_ch = 4; + reqAddr = 58; + reqLen = 139 - 58; // 81 (Registers 58 ~ 138) + sCmd = csSerialCommFunction.READ_HOLDING_REG; break; - case 0x90: // Ger warning information - csSerialCommFunction.MakeTxData(0x01, ReqCmd, 2); - RequstedCmd = ReqCmd; - ReqCmd = 0x46; + case 2: + addr_ch = 3; + reqAddr = 0x3078; + reqLen = 8; + sCmd = csSerialCommFunction.READ_COIL_STATUS; + break; + case 3: + addr_ch = 4; + reqAddr = 0x502E; + reqLen = 1; + sCmd = csSerialCommFunction.READ_COIL_STATUS; + break; + case 4: + if (Config.ModuleQty > 1) + { + SystemId++; + if (SystemId > Config.ModuleQty) + { + SystemId = 1; + } + } + addr_ch = 0; + sCmd = csSerialCommFunction.NO_CMD; break; default: - ReqCmd = 0x42; + addr_ch = 0; + reqAddr = 0x0000; + reqLen = 100; + sCmd = csSerialCommFunction.READ_HOLDING_REG; break; } - return result; + if (sCmd == csSerialCommFunction.NO_CMD) + { + return null; + } + + byte[] sData = csSerialCommFunction.MakeReadRegisterData((ushort)SystemId, sCmd, reqAddr, reqLen); + if (sData == null || sData.Length == 0) + { + return null; + } + + return new TUartTxItem(UartTxType.Polling, SystemId, reqAddr, reqLen, sData, true, Config.RecvWaitTime); } #endregion - #region COMM THREAD + #region COMM THREAD (QUEUE BASED PIPELINE) private readonly byte[] ReadBuf = new byte[BUFFER_SIZE]; - ushort rPosition = 0; - bool BuffStart = false; + private ushort rPosition = 0; + private bool BuffStart = false; private void RecvPacketInit() { @@ -650,153 +545,180 @@ namespace LFP_Manager.Threads private void uartCommThread() { - int RecvTimeout = Config.RecvWaitTime; int getData = 0; byte cData = 0; int TimeOutCount = 0; - StartSend: while (SerialPortThreadEnd == false) { - if ((sPort == null) || (sPort.IsOpen == false)) continue; - - //if (UartPolling) - if (true) + if ((sPort == null) || (sPort.IsOpen == false)) { - bool wMode = false; - byte[] txData = MakeTxDataVision(ref wMode); - if (sPort == null) return; - if (txData != null) + Thread.Sleep(50); + continue; + } + + // 1. 큐가 비어있고 Polling이 활성화되어 있을 때 다음 Polling 패킷을 TX 큐에 삽입 + if (txQueue.IsEmpty && UartPolling && AutoUartTx) + { + TUartTxItem pollItem = CreateNextPollingItem(); + if (pollItem != null) { - FlushBuff(); - sPort.Write(txData, 0, txData.Length); + txQueue.Enqueue(pollItem); } - - if (rFlag == true) + else { - int rTimeOut = RecvTimeout; - RecvPacketInit(); + Thread.Sleep(10); + continue; + } + } - while (rTimeOut > 0) + // 2. TX 큐에서 패킷 1개 꺼내기 + if (!txQueue.TryDequeue(out TUartTxItem currentTx) || currentTx == null || currentTx.Data == null || currentTx.Data.Length == 0) + { + Thread.Sleep(20); + continue; + } + + // 3. SerialPort로 패킷 전송 + try + { + FlushBuff(); + sPort.Write(currentTx.Data, 0, currentTx.Data.Length); + OnPacketLog?.Invoke(DateTime.Now, true, currentTx.Data, GetTxDescription(currentTx)); + } + catch (Exception) + { + Thread.Sleep(50); + continue; + } + + // 4. 응답 대기 및 수신 처리 + if (currentTx.ExpectReply) + { + int rTimeOut = currentTx.TimeoutMs > 0 ? currentTx.TimeoutMs : Config.RecvWaitTime; + RecvPacketInit(); + bool packetHandled = false; + + while (rTimeOut > 0 && SerialPortThreadEnd == false) + { + if (((getData = GetBuff()) & 0x0100) != 0x0000) { - if (((getData = GetBuff()) & 0x0100) != 0x0000) + rTimeOut = currentTx.TimeoutMs > 0 ? currentTx.TimeoutMs : Config.RecvWaitTime; + cData = (byte)(getData & 0x00FF); + + if (rPosition >= BUFFER_SIZE) RecvPacketInit(); + + if (BuffStart == false) { - rTimeOut = RecvTimeout; - - cData = (byte)(getData & 0x00FF); - - if (rPosition >= BUFFER_SIZE) RecvPacketInit(); - - if (BuffStart == false) - { - if ((cData == (byte)SystemId) || (cData == (byte)0x7F)) - { - rPosition = 0; - ReadBuf[rPosition++] = cData; - BuffStart = true; - } - } - else + if ((cData == (byte)currentTx.SystemId) || (cData == (byte)0x7F) || (cData == (byte)SystemId)) { + rPosition = 0; ReadBuf[rPosition++] = cData; - - switch (csSerialCommFunction.ModbusPacketFromSlaveCheck(ReadBuf, rPosition)) - { - case 0: // Need more data - break; - case 1: // Packet OK, no error - TimeOutCount = 0; - - int mID = ReadBuf[0]; - if (mID > 0) mID--; - - SystemData.CommFail = false; - SystemData.ShelfCommFail = false; - - if (wMode == false) - { - short[] result_code = csSerialCommFunction.SerialRxProcess(ReadBuf, RequestRegAddr, rPosition, ref SystemData); - - OnUpdate?.Invoke(this, ref SystemData); - } - else - { - wMode = false; - WriteRegAddr = 0; - if (OnDataRecv != null) - { - byte[] adata = new byte[rPosition + 1]; - for (int j = 0; j < adata.Length; j++) { adata[j] = ReadBuf[j]; } - OnDataRecv(adata); - } - } - rFlag = false; - Thread.Sleep(5); - goto StartSend; - case 2: // Fw Update Packet OK - TimeOutCount = 0; - rFlag = false; - if (OnDataRecv != null) - { - byte[] adata = new byte[rPosition + 1]; - for (int j = 0; j < adata.Length; j++) - { - adata[j] = ReadBuf[j]; - } - OnDataRecv(adata); - } - goto StartSend; - case -1: // Packet error - RecvPacketInit(); - Thread.Sleep(100); - goto StartSend; - default: - break; - } + BuffStart = true; } } else { - Thread.Sleep(1); - if (rTimeOut > 0) + ReadBuf[rPosition++] = cData; + + int checkResult = csSerialCommFunction.ModbusPacketFromSlaveCheck(ReadBuf, rPosition); + if (checkResult == 0) { - rTimeOut--; + // Need more data + } + else if (checkResult == 1) + { + // Packet OK + TimeOutCount = 0; + SystemData.CommFail = false; + SystemData.ShelfCommFail = false; + + byte[] rxPacket = new byte[rPosition]; + Array.Copy(ReadBuf, rxPacket, rPosition); + OnPacketLog?.Invoke(DateTime.Now, false, rxPacket, GetRxDescription(rxPacket, currentTx)); + + if (currentTx.TxType == UartTxType.Polling) + { + csSerialCommFunction.SerialRxProcess(ReadBuf, currentTx.RegAddr, rPosition, ref SystemData); + OnUpdate?.Invoke(this, ref SystemData); + } + else + { + if (OnDataRecv != null) + { + byte[] adata = new byte[rPosition + 1]; + for (int j = 0; j < adata.Length; j++) { adata[j] = ReadBuf[j]; } + OnDataRecv(adata); + } + } + + packetHandled = true; + Thread.Sleep(5); + break; + } + else if (checkResult == 2) + { + // Fw Update Packet OK + TimeOutCount = 0; + byte[] rxPacket = new byte[rPosition]; + Array.Copy(ReadBuf, rxPacket, rPosition); + OnPacketLog?.Invoke(DateTime.Now, false, rxPacket, "Fw Update Response OK"); + + if (OnDataRecv != null) + { + byte[] adata = new byte[rPosition + 1]; + for (int j = 0; j < adata.Length; j++) { adata[j] = ReadBuf[j]; } + OnDataRecv(adata); + } + + packetHandled = true; + Thread.Sleep(5); + break; + } + else if (checkResult == -1) + { + // Packet Error + byte[] rxPacket = new byte[rPosition]; + Array.Copy(ReadBuf, rxPacket, rPosition); + OnPacketLog?.Invoke(DateTime.Now, false, rxPacket, "Packet CRC/Format Error"); + + RecvPacketInit(); + Thread.Sleep(20); + packetHandled = true; + break; } } } - - if (rPosition > 0) - { - Thread.Sleep(1); - } else { - WriteRegAddr = 0; - TimeOutCount++; - if (TimeOutCount >= 5) + Thread.Sleep(1); + rTimeOut--; + } + } + + // 타임아웃 처리 + if (!packetHandled) + { + TimeOutCount++; + if (TimeOutCount >= 5) + { + TimeOutCount = 5; + if (SystemData.ShelfCommFail == false) { - TimeOutCount = 5; - //SystemData[0].CommFail = true; - if (SystemData.ShelfCommFail == false) - { - SystemData.ShelfCommFail = true; - DataInit(); - } + SystemData.ShelfCommFail = true; + DataInit(); } - Thread.Sleep(100); } OnUpdate?.Invoke(this, ref SystemData); - Thread.Sleep(1); + Thread.Sleep(50); } - else - { - Thread.Sleep(100); - } /* if (rFlag == true) */ + rPosition = 0; } else { - Thread.Sleep(100); + // No reply expected + Thread.Sleep(10); } } } @@ -850,5 +772,62 @@ namespace LFP_Manager.Threads } #endregion + + #region PACKET LOG HELPERS + + private string GetTxDescription(TUartTxItem item) + { + if (item == null) return "Unknown TX"; + switch (item.TxType) + { + case UartTxType.Polling: + return string.Format("Polling (ID:{0}, Addr:0x{1:X4}, Len:{2})", item.SystemId, item.RegAddr, item.RegLen); + case UartTxType.WriteParam: + return string.Format("Write Param (ID:{0}, Addr:0x{1:X4})", item.SystemId, item.RegAddr); + case UartTxType.WriteCoil: + return string.Format("Write Coil (ID:{0}, Addr:0x{1:X4})", item.SystemId, item.RegAddr); + case UartTxType.WriteReg: + return string.Format("Write Reg (ID:{0}, Addr:0x{1:X4}, Len:{2})", item.SystemId, item.RegAddr, item.RegLen); + case UartTxType.ReadReg: + return string.Format("Read Reg (ID:{0}, Addr:0x{1:X4}, Len:{2})", item.SystemId, item.RegAddr, item.RegLen); + case UartTxType.FwUpdate: + return string.Format("FW Update (ID:{0}, Len:{1})", item.SystemId, item.Data != null ? item.Data.Length : 0); + default: + return string.Format("RAW TX (ID:{0}, Len:{1})", item.SystemId, item.Data != null ? item.Data.Length : 0); + } + } + + private string GetRxDescription(byte[] rxPacket, TUartTxItem item) + { + if (rxPacket == null || rxPacket.Length < 3) return "Unknown RX"; + byte devId = rxPacket[0]; + byte funcCode = rxPacket[1]; + + if (funcCode == 0x03) + { + byte byteCount = rxPacket[2]; + if (item != null && item.TxType == UartTxType.Polling) + { + return string.Format("Polling Response (ID:{0}, Bytes:{1})", devId, byteCount); + } + return string.Format("Read Holding Reg Response (ID:{0}, Bytes:{1})", devId, byteCount); + } + else if (funcCode == 0x05) + { + return string.Format("Write Coil Response (ID:{0})", devId); + } + else if (funcCode == 0x06 || funcCode == 0x10) + { + return string.Format("Write Reg Response (ID:{0})", devId); + } + else if (funcCode > 0x80) + { + byte errCode = rxPacket.Length > 2 ? rxPacket[2] : (byte)0; + return string.Format("Modbus Error Response (ID:{0}, Func:0x{1:X2}, ErrCode:0x{2:X2})", devId, funcCode, errCode); + } + return string.Format("RX Data (ID:{0}, Func:0x{1:X2}, Len:{2})", devId, funcCode, rxPacket.Length); + } + + #endregion } } diff --git a/LFP_Manager/Utils/csLog.cs b/LFP_Manager/Utils/csLog.cs index 0792e66..576fc52 100644 --- a/LFP_Manager/Utils/csLog.cs +++ b/LFP_Manager/Utils/csLog.cs @@ -294,8 +294,9 @@ namespace LFP_Manager.Utils private static string GetStatusString(short aStatus, short aProtection = 0) { string result = ""; + ushort uStatus = (ushort)(aStatus & 0x7FFF); - switch ((ushort)aStatus) + switch (uStatus) { case 0x0000: result = "STANDBY"; @@ -321,23 +322,8 @@ namespace LFP_Manager.Utils case 0x0032: result = "POWER-ON"; break; - case 0x8000: - result = "STANDBY-HEAT"; - break; - case 0x8001: - result = "CHARGING-HEAT"; - break; - case 0x8002: - result = "DISCHARGING-HEAT"; - break; - case 0x8004: - result = "TRIP-HEAT"; - break; - case 0x8008: - result = "CHG-LIMIT-HEAT"; - break; default: - result = string.Format("UNKNOWN(0x{0:X4})", (ushort)aStatus); + result = string.Format("UNKNOWN(0x{0:X4})", uStatus); break; } return result;