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")]