diff --git a/Cs_HMI/SubProject/BMS/.gitignore b/Cs_HMI/SubProject/BMS/.gitignore
new file mode 100644
index 0000000..ff91f4d
--- /dev/null
+++ b/Cs_HMI/SubProject/BMS/.gitignore
@@ -0,0 +1,12 @@
+*.suo
+*.user
+*.pdb
+bin
+obj
+desktop.ini
+.vs
+.git
+packages
+*.patch
+/Project/SourceSetup.exe
+*.zip
diff --git a/Cs_HMI/SubProject/BMS/BMS.cs b/Cs_HMI/SubProject/BMS/BMS.cs
new file mode 100644
index 0000000..c20eee5
--- /dev/null
+++ b/Cs_HMI/SubProject/BMS/BMS.cs
@@ -0,0 +1,411 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.ComponentModel;
+using System.Threading;
+using System.Threading.Tasks;
+using System.CodeDom;
+
+namespace arDev
+{
+ public class BMS : arRS232
+ {
+
+ ///
+ /// 데이터조회간격(초)
+ ///
+ public float ScanInterval { get; set; }
+
+ public BMS()
+ {
+ ScanInterval = 1000;
+ MinRecvLength = 34;
+ }
+
+ ///
+ /// 시리얼포트의 핀 상태값이 변경될 때 발생합니다.
+ ///
+ public event EventHandler BMSDataReceive;
+ public event EventHandler BMSCellDataReceive;
+
+
+ protected override bool CustomParser(byte[] buf, out byte[] remainBuffer)
+ {
+ List remain = new List();
+
+ //데이터는 총 34byt 이며 , DD~77로 구성됨
+ Boolean bComplete = false;
+ for (int i = 0; i < buf.Length; i++)
+ {
+ var incomByte = buf[i];
+ if (bComplete == true)
+ {
+ remain.Add(incomByte);
+ }
+ else
+ {
+ if (findSTX == false)
+ {
+ if (incomByte != 0xDD)
+ {
+ //버리는데이터
+ }
+ else
+ {
+ findSTX = true;
+ tempBuffer.Clear();
+ tempBuffer.Add(incomByte);
+ }
+ }
+ else
+ {
+ tempBuffer.Add(incomByte);
+
+ var queylen = QueryIndex == 0 ? 34 : 23;
+ if (tempBuffer.Count == queylen)
+ {
+ if (incomByte != 0x77)
+ {
+ //종단기호가 맞지 않다. 이자료는 폐기한다.
+ tempBuffer.Clear();
+ }
+ else
+ {
+ //데이터가 맞게 수신됨
+ LastReceiveBuffer = tempBuffer.ToArray();
+ bComplete = true;
+ }
+ findSTX = false;
+ }
+ else
+ {
+ //아직 모자르므로 대기한다
+ }
+ }
+ }
+ }
+
+ remainBuffer = remain.ToArray();
+ return bComplete;
+ }
+
+ bool Recv0 = false;
+ bool Recv1 = false;
+ public event EventHandler ChargeDetect;
+ public override bool ProcessRecvData(byte[] data)
+ {
+ //LastReceiveBuffer
+ if (data == null)
+ {
+ RaiseMessage(MessageType.Error, "수신 데이터가 없습니다");
+ return false;
+ }
+
+ var datalne = QueryIndex == 0 ? 34 : 23;
+ if (data.Length != datalne)
+ {
+ RaiseMessage(MessageType.Error, $"데이터의 길이가 {MinRecvLength}가 아닙니다 길이={data.Length}");
+ return false;
+ }
+
+ if (QueryIndex == 0)
+ {
+ return ParseBMSInfo();
+ }
+ else
+ {
+ return ParseBMSCellVoltage();
+ }
+
+
+ }
+ bool ParseBMSCellVoltage()
+ {
+ //var i = 0;
+ var BatteryCell_Checksum = 0xffff;// - (LastReceiveBuffer[i + 3] + LastReceiveBuffer[i + 4] + LastReceiveBuffer[i + 5] + LastReceiveBuffer[i + 6] + LastReceiveBuffer[i + 7] + LastReceiveBuffer[i + 8] + LastReceiveBuffer[i + 9] + LastReceiveBuffer[i + 10] + LastReceiveBuffer[i + 11] + LastReceiveBuffer[i + 12] + LastReceiveBuffer[i + 13] + LastReceiveBuffer[i + 14] + LastReceiveBuffer[i + 15] + LastReceiveBuffer[i + 16] + LastReceiveBuffer[i + 17] + LastReceiveBuffer[i + 18] + LastReceiveBuffer[i + 19])) + 1;
+ for (int i = 3; i < 20; i++)
+ {
+ BatteryCell_Checksum -= LastReceiveBuffer[i];// + LastReceiveBuffer[i + 4] + LastReceiveBuffer[i + 5] + LastReceiveBuffer[i + 6] + LastReceiveBuffer[i + 7] + LastReceiveBuffer[i + 8] + LastReceiveBuffer[i + 9] + LastReceiveBuffer[i + 10] + LastReceiveBuffer[i + 11] + LastReceiveBuffer[i + 12] + LastReceiveBuffer[i + 13] + LastReceiveBuffer[i + 14] + LastReceiveBuffer[i + 15] + LastReceiveBuffer[i + 16] + LastReceiveBuffer[i + 17] + LastReceiveBuffer[i + 18] + LastReceiveBuffer[i + 19])) + 1;
+ }
+
+ BatteryCell_Checksum += 1;
+ var recvchecksum = BitConverter.ToUInt16(LastReceiveBuffer.Skip(20).Take(2).Reverse().ToArray(), 0);
+ if (recvchecksum == BatteryCell_Checksum)
+ {
+ var v1 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(4).Take(2).Reverse().ToArray(), 0) / 1000.0;
+ var v2 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(6).Take(2).Reverse().ToArray(), 0) / 1000.0;
+ var v3 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(8).Take(2).Reverse().ToArray(), 0) / 1000.0;
+ var v4 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(10).Take(2).Reverse().ToArray(), 0) / 1000.0;
+ var v5 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(12).Take(2).Reverse().ToArray(), 0) / 1000.0;
+ var v6 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(14).Take(2).Reverse().ToArray(), 0) / 1000.0;
+ var v7 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(16).Take(2).Reverse().ToArray(), 0) / 1000.0;
+ var v8 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(18).Take(2).Reverse().ToArray(), 0) / 1000.0;
+
+ var idx = 0;
+ CellVoltage[idx++] = v1;
+ CellVoltage[idx++] = v2;
+ CellVoltage[idx++] = v3;
+ CellVoltage[idx++] = v4;
+ CellVoltage[idx++] = v5;
+ CellVoltage[idx++] = v6;
+ CellVoltage[idx++] = v7;
+ CellVoltage[idx++] = v8;
+
+ Recv1 = true;
+
+ try
+ {
+ BMSCellDataReceive?.Invoke(this, new BMSCelvoltageEventArgs(v1, v2, v3, v4, v5, v6, v7, v8));
+ return true;
+ }
+ catch (Exception ex)
+ {
+ RaiseMessage(MessageType.Error, ex.Message);
+ return false;
+ }
+ }
+ else return false;
+
+
+ }
+ bool ParseBMSInfo()
+ {
+ //전압확인
+ UInt16 batH = (UInt16)LastReceiveBuffer[4];
+ UInt16 batL = (UInt16)LastReceiveBuffer[5];
+ batH = (UInt16)(batH << 8);
+ batH = (UInt16)(batH | batL);
+ Current_Volt = (float)(batH / 100.0);
+
+ //잔량확인
+ batH = (UInt16)LastReceiveBuffer[8];
+ batL = (UInt16)LastReceiveBuffer[9];
+ batH = (UInt16)(batH << 8);
+ batH = (UInt16)(batH | batL);
+ var newamp = (int)batH;
+ var Changed = newamp != Current_Amp;
+ Current_Amp = newamp;
+
+ //총량확인
+ batH = (UInt16)LastReceiveBuffer[10];
+ batL = (UInt16)LastReceiveBuffer[11];
+ batH = (UInt16)(batH << 8);
+ batH = (UInt16)(batH | batL);
+ Current_MaxAmp = (int)batH;
+
+ //남은 % 계산
+ float levraw = (float)(Current_Amp / (Current_MaxAmp * 1.0));
+ Current_Level = (float)(levraw * 100.0);// (float)(map(remain, 0, total, 0, 100));
+ Current_LevelA = LastReceiveBuffer[23]; //<- 23번자료는 byte이므로 소수점이잇는 데이터로 직접 계산한다
+ Current_DataTime = DateTime.Now;
+
+ CheckManualCharge();
+
+ Recv0 = true;
+
+ try
+ {
+ BMSDataReceive?.Invoke(this, new BMSInformationEventArgs(Current_Volt, Current_Amp, Current_MaxAmp, Current_Level, Changed));
+ return true;
+ }
+ catch (Exception ex)
+ {
+ RaiseMessage(MessageType.Error, ex.Message);
+ return false;
+ }
+ }
+
+ private bool _autocharge = false;
+ public Boolean AutoCharge
+ {
+ get { return _autocharge; }
+ set { _autocharge = false; }
+ }
+
+ //public void ClearManualChargeCheckValue()
+ //{
+ // chk_timee = new DateTime(1982, 11, 23);
+ // chk_times = new DateTime(1982, 11, 23);
+ // chk_valuee = 0f;
+ // chk_values = 0f;
+ //}
+
+ void CheckManualCharge()
+ {
+ if (AutoCharge)
+ {
+ if (chk_timee.Year != 1982)
+ {
+ chk_timee = new DateTime(1982, 11, 23);
+ chk_valuee = 999f;
+ }
+ if (chk_times.Year != 1982)
+ {
+ chk_times = new DateTime(1982, 11, 23);
+ chk_values = 999f;
+ }
+ }
+ if (chk_times.Year == 1982)
+ {
+ chk_times = DateTime.Now;
+ chk_values = Current_Level;
+ }
+ else
+ {
+ if (chk_timee.Year == 1982)
+ {
+ if ((Current_Level - chk_values) >= 0.1)
+ {
+ //충전중이다
+ chk_timee = DateTime.Now;
+ chk_valuee = Current_Level;
+ try
+ {
+ ChargeDetect?.Invoke(this, new ChargetDetectArgs(chk_times, chk_values, chk_timee, chk_valuee));
+ }
+ catch (Exception ex) { RaiseMessage(MessageType.Error, ex.Message); }
+
+ }
+ else if ((Current_Level - chk_values) <= -0.1)
+ {
+ //방전중이다
+ if (chk_times.Year != 1982) chk_times = new DateTime(1982, 11, 23);
+ if (chk_timee.Year != 1982) chk_timee = new DateTime(1982, 11, 23);
+ }
+ else
+ {
+ //아직 변화가 없으니 종료일을 기록하지 않는다
+ }
+ }
+ else
+ {
+ //이미 종료일이 셋팅된 상태이다
+ if ((Current_Level - chk_valuee) >= 0.1)
+ {
+ //종료시간을 시작값에 넣는다
+ chk_times = chk_timee;
+ chk_values = chk_valuee;
+
+ chk_timee = DateTime.Now;
+ chk_valuee = Current_Level;
+ try
+ {
+ ChargeDetect?.Invoke(this, new ChargetDetectArgs(chk_times, chk_values, chk_timee, chk_valuee));
+ }
+ catch (Exception ex) { RaiseMessage(MessageType.Error, ex.Message); }
+ }
+ else if ((Current_Level - chk_valuee) <= -0.1)
+ {
+ //방전중이다
+ if (chk_times.Year != 1982) chk_times = new DateTime(1982, 11, 23);
+ if (chk_timee.Year != 1982) chk_timee = new DateTime(1982, 11, 23);
+ }
+ else
+ {
+ //아직 변화가 없으니 종료일을 기록하지 않는다
+ }
+ }
+
+ }
+ }
+
+
+ public DateTime chk_times { get; set; } = new DateTime(1982, 11, 23);
+ public DateTime chk_timee { get; set; } = new DateTime(1982, 11, 23);
+ public float chk_values { get; set; } = 0f;
+ public float chk_valuee { get; set; } = 0f;
+
+ ///
+ /// 전압
+ ///
+ public float Current_Volt { get; set; }
+ ///
+ /// 남은 잔량(%)
+ ///
+ public float Current_Level { get; set; }
+ public double[] CellVoltage = new double[] { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ public float Current_LevelA { get; set; }
+
+ ///
+ /// 남은 전류량
+ ///
+ public int Current_Amp { get; set; }
+
+ ///
+ /// 총 전류량
+ ///
+ public int Current_MaxAmp { get; set; }
+
+ public DateTime Current_DataTime = DateTime.Parse("1982-11-23");
+
+ public int QueryIndex { get; set; } = 0;
+
+ ///
+ /// 상태읽기와 전압읽기명령을 반복합니다
+ ///
+ ///
+ public Boolean SendQuery()
+ {
+ if (QueryIndex == 0)
+ {
+ if (Recv0 == true)
+ {
+ QueryIndex = 1;
+ Recv1 = false;
+ return true;
+ }
+ else
+ {
+ return SendQuery_ReadStatue();
+ }
+ }
+ else
+ {
+ if (Recv1 == true)
+ {
+ QueryIndex = 0;
+ Recv0 = false;
+ return true;
+ }
+ else
+ {
+ return SendQuery_ReadCellvoltage();
+ }
+ }
+ }
+
+ public Boolean SendQuery_ReadStatue()
+ {
+ Recv0 = false;
+ var cmd = new List();
+ cmd.Add(0xDD);
+ cmd.Add(0xA5);
+ cmd.Add(0x03);
+ cmd.Add(0x00);
+ cmd.Add(0xFF);
+ cmd.Add(0xFD);
+ cmd.Add(0x77);
+ cmd.Add(0x0D);
+ return WriteData(cmd.ToArray());
+ }
+
+
+ public Boolean SendQuery_ReadCellvoltage()
+ {
+ Recv1 = false;
+ var cmd = new List();
+ cmd.Add(0xDD);
+ cmd.Add(0xA5);
+ cmd.Add(0x04);
+ cmd.Add(0x00);
+ cmd.Add(0xFF);
+ cmd.Add(0xFC);
+ cmd.Add(0x77);
+ cmd.Add(0x0D);
+ return WriteData(cmd.ToArray());
+ }
+
+ }
+}
diff --git a/Cs_HMI/SubProject/BMS/BMS.csproj b/Cs_HMI/SubProject/BMS/BMS.csproj
new file mode 100644
index 0000000..e8ca0f5
--- /dev/null
+++ b/Cs_HMI/SubProject/BMS/BMS.csproj
@@ -0,0 +1,57 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {7A94C30C-6772-4F71-BF9C-0DF071A1BC70}
+ Library
+ Properties
+ arDevice
+ BMS
+ v4.8
+ 512
+ true
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {14e8c9a5-013e-49ba-b435-efefc77dd623}
+ CommData
+
+
+
+
\ No newline at end of file
diff --git a/Cs_HMI/SubProject/BMS/BMSInformationEventArgs.cs b/Cs_HMI/SubProject/BMS/BMSInformationEventArgs.cs
new file mode 100644
index 0000000..73dffe9
--- /dev/null
+++ b/Cs_HMI/SubProject/BMS/BMSInformationEventArgs.cs
@@ -0,0 +1,44 @@
+using System;
+
+namespace arDev
+{
+ public class ChargetDetectArgs : EventArgs
+ {
+ public DateTime times { get; set; }
+ public DateTime timee { get; set; }
+ public float values { get; set; }
+ public float valuee { get; set; }
+ public ChargetDetectArgs(DateTime times, float values, DateTime timee, float valuee)
+ {
+ this.times = times;
+ this.times = timee;
+ this.values = values;
+ this.valuee = valuee;
+ }
+ }
+ public class BMSInformationEventArgs : EventArgs
+ {
+ public float Volt { get; set; }
+ public int CurAmp { get; set; }
+ public int MaxAmp { get; set; }
+ public float Level { get; set; }
+ public bool Changed { get; set; }
+ public BMSInformationEventArgs(float _volt, int _curamp, int _maxamp, float _level, bool _changed)
+ {
+ this.Volt = _volt;
+ this.CurAmp = _curamp;
+ this.MaxAmp = _maxamp;
+ this.Level = _level;
+ this.Changed = _changed;
+ }
+ }
+
+ public class BMSCelvoltageEventArgs : EventArgs
+ {
+ public double[] voltage;
+ public BMSCelvoltageEventArgs(double v1, double v2, double v3, double v4, double v5, double v6, double v7, double v8)
+ {
+ voltage = new double[] { v1, v2, v3, v4, v5, v6, v7, v8 };
+ }
+ }
+}
diff --git a/Cs_HMI/SubProject/BMS/Properties/AssemblyInfo.cs b/Cs_HMI/SubProject/BMS/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..dafbac1
--- /dev/null
+++ b/Cs_HMI/SubProject/BMS/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해
+// 제어됩니다. 어셈블리와 관련된 정보를 수정하려면
+// 이러한 특성 값을 변경하세요.
+[assembly: AssemblyTitle("BMS Commnunication Module")]
+[assembly: AssemblyDescription("BMS Commnunication Module")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("ATK4")]
+[assembly: AssemblyProduct("BMS Commnunication Module")]
+[assembly: AssemblyCopyright("Copyright ©ATK4 2020")]
+[assembly: AssemblyTrademark("ATK4")]
+[assembly: AssemblyCulture("")]
+
+// ComVisible을 false로 설정하면 이 어셈블리의 형식이 COM 구성 요소에
+// 표시되지 않습니다. COM에서 이 어셈블리의 형식에 액세스하려면
+// 해당 형식에 대해 ComVisible 특성을 true로 설정하세요.
+[assembly: ComVisible(false)]
+
+// 이 프로젝트가 COM에 노출되는 경우 다음 GUID는 typelib의 ID를 나타냅니다.
+[assembly: Guid("7a94c30c-6772-4f71-bf9c-0df071a1bc70")]
+
+// 어셈블리의 버전 정보는 다음 네 가지 값으로 구성됩니다.
+//
+// 주 버전
+// 부 버전
+// 빌드 번호
+// 수정 버전
+//
+// 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호를
+// 기본값으로 할 수 있습니다.
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("20.03.09.0000")]
+[assembly: AssemblyFileVersion("20.03.09.0000")]