This commit is contained in:
backuppc
2026-01-14 15:29:38 +09:00
parent 5801137d63
commit d5516f9708
17 changed files with 605 additions and 402 deletions

View File

@@ -9,12 +9,152 @@ using System.CodeDom;
namespace arDev
{
public class BMSCellInformation
{
public float[] Voltage = new float[8];
public float Average
{
get
{
if (Voltage.Any() == false) return 0f;
return Voltage.Average();
}
}
public float Delta
{
get
{
if (Voltage.Any() == false) return 0f;
return Math.Abs(Voltage.Max() - Voltage.Min());
}
}
}
public class BMSBasicInformation
{
public float packVoltage { get; set; }
public float current { get; set; }
public float remainingCapacity { get; set; }
public float fullCapacity { get; set; }
public UInt16 cycleCount { get; set; }
public DateTime productionDate { get; set; }
public UInt32 fullBalance { get; set; }
public BMSProtectionStatus protectionStatus { get; set; }
public byte version { get; set; }
public byte rsoc { get; set; }
public BMSMosfetStatus mosfetStatus { get; set; }
public byte ntcCount { get; set; }
public float[] ntcTemp { get; set; }
public ushort raw_protection { get; set; }
public float watt
{
get
{
return (this.current * packVoltage);
}
}
/// <summary>
/// Remain / Full * 100
/// </summary>
public float RawLevel
{
get
{
if (fullCapacity < 1) return 0f;
return (remainingCapacity / fullCapacity) * 100f;
}
}
public BMSBasicInformation()
{
protectionStatus = new BMSProtectionStatus();
mosfetStatus = new BMSMosfetStatus();
productionDate = new DateTime();
ntcTemp = new float[] { 0f,0f};
}
public void Clear()
{
packVoltage = 0;
current = 0;
remainingCapacity = 0;
fullBalance = 0;
fullCapacity = 0;
cycleCount = 0;
productionDate = new DateTime();
protectionStatus = new BMSProtectionStatus();
}
public override string ToString()
{
return $"packVoltage:{packVoltage}\n" +
$"current:{current}\n" +
$"remainingCapacity:{remainingCapacity}\n" +
$"fullCapacity:{fullCapacity}\n" +
$"cycleCount:{cycleCount}\n" +
$"productionDate:{productionDate}\n" +
$"fullBalance:{fullBalance}\n" +
$"protectionStatus:{protectionStatus}\n" +
$"version:{version}\n" +
$"rsoc:{rsoc}\n" +
$"mosfetStatus:{mosfetStatus}\n" +
$"ntcCount:{ntcCount}\n" +
$"ntcTemp:{ntcTemp}\n" +
$"watt:{watt}\n" +
$"RawLevel:{RawLevel}";
}
}
public class BMSProtectionStatus
{
public bool covp { get; set; }// Cell Over Voltage Protection
public bool cuvp { get; set; } // Cell Under Voltage Protection
public bool povp { get; set; } // Pack Over Voltage Protection
public bool puvp { get; set; } // Pack Under Voltage Protection
public bool chgot { get; set; }// Charge Over Temp
public bool chgut { get; set; } // Charge Under Temp
public bool dsgot { get; set; } // Discharge Over Temp
public bool dsgut { get; set; } // Discharge Under Temp
public bool chgoc { get; set; } // Charge Over Current
public bool dsgoc { get; set; } // Discharge Over Current
public bool sc { get; set; } // Short Circuit
public bool afe { get; set; } // AFE Error
public override string ToString()
{
return "BMSProtectionStatus\n" +
$"covp={covp}\n" +// { get; set; }// Cell Over Voltage Protection
$"cuvp={cuvp}\n" +// { get; set; } // Cell Under Voltage Protection
$"povp={povp}\n" +// { get; set; } // Pack Over Voltage Protection
$"puvp={puvp}\n" +// { get; set; } // Pack Under Voltage Protection
$"chgot={chgot}\n" +// { get; set; }// Charge Over Temp
$"chgut={chgut}\n" +// { get; set; } // Charge Under Temp
$"dsgot={dsgot}\n" +// { get; set; } // Discharge Over Temp
$"dsgut={dsgut}\n" +// { get; set; } // Discharge Under Temp
$"chgoc={chgoc}\n" +// { get; set; } // Charge Over Current
$"dsgoc={dsgoc}\n" +// { get; set; } // Discharge Over Current
$"sc={sc}\n" +// { get; set; } // Short Circuit
$"afe={afe}";// +// { get; set; } // AFE Error
}
}
public class BMSMosfetStatus
{
public bool charge { get; set; }
public bool discharge { get; set; }
public override string ToString()
{
return $"charge:{charge}\n" +
$"discharge:{discharge}";
}
}
public class BMS : BMSSerialComm
{
public BMS()
{
}
/// <summary>
@@ -77,14 +217,14 @@ namespace arDev
}
}
// [22 - 12 - 27 14:32:49] open: True
//[22 - 12 - 27 14:32:49] Send: DD A5 03 00 FF FD 77 0D
//[22 - 12 - 27 14:32:50] Send: DD A5 03 00 FF FD 77 0D
//[22 - 12 - 27 14:32:50] Recv: 26.61v,81.4 %
//[22 - 12 - 27 14:32:50] Recv: DD 03 00 1B 0A 65 00 00 21 63 29 04 00 00 2C 92 00 00 00 00 00 00 28 51 03 08 02 0B 69 0B 66 FC 9C 77
//[22 - 12 - 27 14:32:50] Send: DD A5 03 00 FF FD 77 0D
//[22 - 12 - 27 14:32:51] Recv: 26.61v,81.4 %
//[22 - 12 - 27 14:32:51] Recv: DD 03 00 1B 0A 65 00 00 21 63 29 04 00 00 2C 92 00 00 00 00 00 00 28 51 03 08 02 0B 69 0B 66 FC 9C 77
// [22 - 12 - 27 14:32:49] open: True
//[22 - 12 - 27 14:32:49] Send: DD A5 03 00 FF FD 77 0D
//[22 - 12 - 27 14:32:50] Send: DD A5 03 00 FF FD 77 0D
//[22 - 12 - 27 14:32:50] Recv: 26.61v,81.4 %
//[22 - 12 - 27 14:32:50] Recv: DD 03 00 1B 0A 65 00 00 21 63 29 04 00 00 2C 92 00 00 00 00 00 00 28 51 03 08 02 0B 69 0B 66 FC 9C 77
//[22 - 12 - 27 14:32:50] Send: DD A5 03 00 FF FD 77 0D
//[22 - 12 - 27 14:32:51] Recv: 26.61v,81.4 %
//[22 - 12 - 27 14:32:51] Recv: DD 03 00 1B 0A 65 00 00 21 63 29 04 00 00 2C 92 00 00 00 00 00 00 28 51 03 08 02 0B 69 0B 66 FC 9C 77
//var queylen = QueryIndex == 0 ? 34 : 23;
@@ -165,24 +305,24 @@ namespace arDev
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 v1 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(4).Take(2).Reverse().ToArray(), 0) / 1000f;
var v2 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(6).Take(2).Reverse().ToArray(), 0) / 1000f;
var v3 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(8).Take(2).Reverse().ToArray(), 0) / 1000f;
var v4 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(10).Take(2).Reverse().ToArray(), 0) / 1000f;
var v5 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(12).Take(2).Reverse().ToArray(), 0) / 1000f;
var v6 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(14).Take(2).Reverse().ToArray(), 0) / 1000f;
var v7 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(16).Take(2).Reverse().ToArray(), 0) / 1000f;
var v8 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(18).Take(2).Reverse().ToArray(), 0) / 1000f;
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;
BMSCellVoltage.Voltage[idx++] = v1;
BMSCellVoltage.Voltage[idx++] = v2;
BMSCellVoltage.Voltage[idx++] = v3;
BMSCellVoltage.Voltage[idx++] = v4;
BMSCellVoltage.Voltage[idx++] = v5;
BMSCellVoltage.Voltage[idx++] = v6;
BMSCellVoltage.Voltage[idx++] = v7;
BMSCellVoltage.Voltage[idx++] = v8;
Recv1 = true;
@@ -199,61 +339,116 @@ namespace arDev
}
}
else return false;
}
bool ParseBMSInfo()
{
var newinfo = new BMSBasicInformation();
//전압확인
UInt16 batH = (UInt16)LastReceiveBuffer[4];
UInt16 batL = (UInt16)LastReceiveBuffer[5];
var offset = 4;
UInt16 batH = (UInt16)LastReceiveBuffer[offset + 0];
UInt16 batL = (UInt16)LastReceiveBuffer[offset + 1];
batH = (UInt16)(batH << 8);
batH = (UInt16)(batH | batL);
Current_Volt = (float)(batH / 100.0);
newinfo.packVoltage = (float)(batH / 100.0);
//충방전전류
Int16 batHi = (Int16)LastReceiveBuffer[6];
Int16 batLi = (Int16)LastReceiveBuffer[7];
Int16 batHi = (Int16)LastReceiveBuffer[offset + 2];
Int16 batLi = (Int16)LastReceiveBuffer[offset + 3];
batHi = (Int16)(batHi << 8);
batHi = (Int16)(batHi | batLi);
Charge_Amp = (float)(batHi / 100.0);
newinfo.current = (float)(batHi / 100.0);
//잔량확인
batH = (UInt16)LastReceiveBuffer[8];
batL = (UInt16)LastReceiveBuffer[9];
batH = (UInt16)LastReceiveBuffer[offset + 4];
batL = (UInt16)LastReceiveBuffer[offset + 5];
batH = (UInt16)(batH << 8);
batH = (UInt16)(batH | batL);
var newamp = (int)batH;
var Changed = newamp != Current_Amp;
Current_Amp = newamp;
newinfo.remainingCapacity = (float)(batH / 100.0);
//총량확인
batH = (UInt16)LastReceiveBuffer[10];
batL = (UInt16)LastReceiveBuffer[11];
batH = (UInt16)LastReceiveBuffer[offset + 6];
batL = (UInt16)LastReceiveBuffer[offset + 7];
batH = (UInt16)(batH << 8);
batH = (UInt16)(batH | batL);
Current_MaxAmp = (int)batH;
newinfo.fullCapacity = (float)(batH / 100.0);
//남은 % 계산
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이므로 소수점이잇는 데이터로 직접 계산한다
//cycle
batH = (UInt16)LastReceiveBuffer[offset + 8];
batL = (UInt16)LastReceiveBuffer[offset + 9];
batH = (UInt16)(batH << 8);
batH = (UInt16)(batH | batL);
newinfo.cycleCount = batH;
//productiondate
batH = (UInt16)LastReceiveBuffer[offset + 10];
batL = (UInt16)LastReceiveBuffer[offset + 11];
batH = (UInt16)(batH << 8);
batH = (UInt16)(batH | batL);
var info_productiondateint = batH;
var date_year = (info_productiondateint >> 9) + 2000;
var date_month = (info_productiondateint >> 5) & 0x0F;
var date_day = info_productiondateint & 0x1F;
newinfo.productionDate = new DateTime(date_year, date_month, date_day);
//balnace status
batH = (UInt16)LastReceiveBuffer[offset + 12];
batL = (UInt16)LastReceiveBuffer[offset + 13];
batH = (UInt16)(batH << 8);
var balanceStatus = (UInt16)(batH | batL);
//balnace status(HIGH)
batH = (UInt16)LastReceiveBuffer[offset + 14];
batL = (UInt16)LastReceiveBuffer[offset + 15];
batH = (UInt16)(batH << 8);
var balanceStatusHigh = (UInt16)(batH | batL);
newinfo.fullBalance = (UInt32)(balanceStatus | (balanceStatusHigh << 16));
//protectionStatusRaw
batH = (UInt16)LastReceiveBuffer[offset + 16];
batL = (UInt16)LastReceiveBuffer[offset + 17];
batH = (UInt16)(batH << 8);
newinfo.raw_protection = batH;// view.getUint16(16, false);
var protectionStatusRaw = newinfo.raw_protection;
newinfo.protectionStatus.covp = (protectionStatusRaw & 1) > 0;
newinfo.protectionStatus.cuvp = ((protectionStatusRaw >> 1) & 1) > 0;
newinfo.protectionStatus.povp = ((protectionStatusRaw >> 2) & 1) > 0;
newinfo.protectionStatus.puvp = ((protectionStatusRaw >> 3) & 1) > 0;
newinfo.protectionStatus.chgot = ((protectionStatusRaw >> 4) & 1) > 0;
newinfo.protectionStatus.chgut = ((protectionStatusRaw >> 5) & 1) > 0;
newinfo.protectionStatus.dsgot = ((protectionStatusRaw >> 6) & 1) > 0;
newinfo.protectionStatus.dsgut = ((protectionStatusRaw >> 7) & 1) > 0;
newinfo.protectionStatus.chgoc = ((protectionStatusRaw >> 8) & 1) > 0;
newinfo.protectionStatus.dsgoc = ((protectionStatusRaw >> 9) & 1) > 0;
newinfo.protectionStatus.sc = ((protectionStatusRaw >> 10) & 1) > 0;
newinfo.protectionStatus.afe = ((protectionStatusRaw >> 11) & 1) > 0;
//version
newinfo.version = LastReceiveBuffer[offset + 18];
newinfo.rsoc = LastReceiveBuffer[offset + 19];
var mosfetRaw = LastReceiveBuffer[offset + 20];
newinfo.mosfetStatus.charge = (mosfetRaw & 1) == 1;
newinfo.mosfetStatus.discharge = ((mosfetRaw >> 1) & 1) == 1;
//250620 jwlee 추가
int temp1 = (LastReceiveBuffer[27] << 8) | LastReceiveBuffer[28];
int temp2 = (LastReceiveBuffer[29] << 8) | LastReceiveBuffer[30];
newinfo.ntcCount = LastReceiveBuffer[offset + 22]; //센서갯수
int temp1 = (LastReceiveBuffer[offset + 23] << 8) | LastReceiveBuffer[24];
int temp2 = (LastReceiveBuffer[offset + 25] << 8) | LastReceiveBuffer[36];
var Current_temp1 = (temp1 - 2731) / 10f;
var Current_temp2 = (temp2 - 2731) / 10f;
newinfo.ntcTemp = new float[] { (temp1 - 2731) / 10f, (temp2 - 2731) / 10f };
Current_temp1 = (temp1 - 2731) / 10f;
Current_temp2 = (temp2 - 2731) / 10f;
CheckManualCharge();
CheckManualCharge(newinfo);
Recv0 = true;
try
{
BMSDataReceive?.Invoke(this, new BMSInformationEventArgs(Current_Volt, Current_Amp, Current_MaxAmp, Current_Level, Changed));
this.BMSInformation = newinfo;
BMSDataReceive?.Invoke(this, new BMSInformationEventArgs(BMSInformation));
Current_DataTime = DateTime.Now;
return true;
}
@@ -263,16 +458,21 @@ namespace arDev
return false;
}
}
public BMSBasicInformation BMSInformation = new BMSBasicInformation();
public BMSCellInformation BMSCellVoltage = new BMSCellInformation();
/// <summary>
/// 현재 충전중인지?
/// </summary>
public bool IsCharging { get; private set; }
DateTime ChargeStart = DateTime.Now;
DateTime ChargeEnd = DateTime.Now;
void CheckManualCharge()
void CheckManualCharge(BMSBasicInformation info)
{
//충방전전력이 1보다 크면 충전으로 한다.
if (Charge_Amp > 0.1)
if (this.BMSInformation.current > 0.1)
{
//기존에 충전상태가 OFF였다면 충전중으로 알려준다
if (IsCharging == false)
@@ -282,7 +482,7 @@ namespace arDev
ChargeEnd = new DateTime(1982, 11, 23);
try
{
ChargeDetect?.Invoke(this, new ChargetDetectArgs(ChargeStart, true, Current_Level));
ChargeDetect?.Invoke(this, new ChargetDetectArgs(ChargeStart, true, info.RawLevel));
}
catch (Exception ex) { RaiseMessage(MessageType.Error, ex.Message); }
}
@@ -309,7 +509,7 @@ namespace arDev
IsCharging = false;
try
{
ChargeDetect?.Invoke(this, new ChargetDetectArgs(ChargeEnd, false, Current_Level));
ChargeDetect?.Invoke(this, new ChargetDetectArgs(ChargeEnd, false, info.RawLevel));
}
catch (Exception ex) { RaiseMessage(MessageType.Error, ex.Message); }
}
@@ -328,47 +528,6 @@ namespace arDev
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 Charge_Amp { get; set; } = 0f;
public Int16 Charge_watt
{
get
{
return (Int16)((Charge_Amp) * Current_Volt);
}
}
/// <summary>
/// 전압
/// </summary>
public float Current_Volt { get; set; }
/// <summary>
/// 남은 잔량(%)
/// </summary>
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; }
/// <summary>
/// 남은 전류량
/// </summary>
public int Current_Amp { get; set; }
/// <summary>
/// BMS 온도값1
/// </summary>
public double Current_temp1 { get; set; }
/// <summary>
/// BMS 온도값2
/// </summary>
public double Current_temp2 { get; set; }
/// <summary>
/// 총 전류량
/// </summary>
public int Current_MaxAmp { get; set; }
public DateTime Current_DataTime = DateTime.Parse("1982-11-23");
public DateTime Current_CellTime = DateTime.Parse("1982-11-23");