208 lines
7.5 KiB
Python
208 lines
7.5 KiB
Python
import struct
|
||
import logging
|
||
import binascii
|
||
|
||
|
||
class Command30:
|
||
def __init__(self):
|
||
self.command = 0x30 # BMS状态信息命令
|
||
|
||
def parse_30h_bms_status(self, data):
|
||
"""
|
||
解析30H BMS状态需求报文
|
||
|
||
:param data: 完整的30H命令报文
|
||
:return: 解析后的字典或None
|
||
"""
|
||
try:
|
||
# 验证基本帧格式
|
||
if len(data) < 14 or data[0:2] != b'JX' or data[2] != 0x30:
|
||
logging.warning(f"30H命令帧格式不正确,原始报文: {binascii.hexlify(data)}")
|
||
return None
|
||
|
||
# 打印完整的原始报文以便调试
|
||
print(f"完整原始报文: {binascii.hexlify(data)}")
|
||
|
||
# 提取桩号
|
||
pile_id_bytes = data[3:11]
|
||
|
||
# 提取时间标识
|
||
time_bytes = data[14:20]
|
||
year = time_bytes[0] + 2000
|
||
month, day, hour, minute, second = time_bytes[1:6]
|
||
timestamp = f"{year:04d}-{month:02d}-{day:02d} {hour:02d}:{minute:02d}:{second:02d}"
|
||
|
||
# 初始化解析索引
|
||
current_index = 20
|
||
|
||
# 解析电压需求
|
||
voltage_request = struct.unpack("<H", data[current_index:current_index + 2])[0] / 10 # 0.1V
|
||
current_index += 2
|
||
|
||
# 解析电流需求
|
||
current_request = struct.unpack("<H", data[current_index:current_index + 2])[0] / 10 # 0.1A
|
||
current_index += 2
|
||
|
||
# 解析充电模式
|
||
charging_mode = data[current_index]
|
||
current_index += 1
|
||
|
||
# 解析最高单体电压
|
||
max_cell_voltage = struct.unpack("<H", data[current_index:current_index + 2])[0] / 1000 # 0.001V
|
||
current_index += 2
|
||
|
||
# 解析最高单体电压所在电池组号
|
||
max_cell_group = data[current_index]
|
||
current_index += 1
|
||
|
||
# 解析SOC
|
||
soc = data[current_index]
|
||
current_index += 1
|
||
|
||
# 解析剩余充电时间
|
||
remaining_charging_time = struct.unpack("<H", data[current_index:current_index + 2])[0]
|
||
current_index += 2
|
||
|
||
# 解析最高单体电压电池组编号
|
||
max_voltage_group_number = data[current_index]
|
||
current_index += 1
|
||
|
||
# 解析最高动力蓄电池温度
|
||
max_battery_temperature = data[current_index] - 50 # 偏移量-50
|
||
current_index += 1
|
||
|
||
# 解析最高温度检测点编号
|
||
max_temperature_point = data[current_index]
|
||
current_index += 1
|
||
|
||
# 解析最低动力蓄电池温度
|
||
min_battery_temperature = data[current_index] - 50 # 偏移量-50
|
||
current_index += 1
|
||
|
||
# 解析最低温度监测点编号
|
||
min_temperature_point = data[current_index]
|
||
current_index += 1
|
||
|
||
# 解析告警信息
|
||
warning_bytes = data[current_index]
|
||
warnings = {
|
||
"单体电压过高/过低": (warning_bytes & 0x03),
|
||
"SOC过高/过低": ((warning_bytes >> 2) & 0x03),
|
||
"充电过流": ((warning_bytes >> 4) & 0x03),
|
||
"动力蓄电池温度过高": ((warning_bytes >> 6) & 0x03)
|
||
}
|
||
current_index += 1
|
||
|
||
# 打印解析结果
|
||
print("\n30H BMS状态需求报文解析结果:")
|
||
print(f"桩号: {pile_id_bytes.hex()}")
|
||
print(f"时间标识: {timestamp}")
|
||
print(f"电压需求: {voltage_request}V")
|
||
print(f"电流需求: {current_request}A")
|
||
print(f"充电模式: {self.get_charging_mode_text(charging_mode)}")
|
||
print(f"最高单体电压: {max_cell_voltage}V")
|
||
print(f"最高单体电压所在电池组号: {max_cell_group}")
|
||
print(f"SOC: {soc}%")
|
||
print(f"剩余充电时间: {remaining_charging_time}分钟")
|
||
print(f"最高单体电压电池组编号: {max_voltage_group_number}")
|
||
print(f"最高动力蓄电池温度: {max_battery_temperature}°C")
|
||
print(f"最高温度检测点编号: {max_temperature_point}")
|
||
print(f"最低动力蓄电池温度: {min_battery_temperature}°C")
|
||
print(f"最低温度监测点编号: {min_temperature_point}")
|
||
print("告警信息:")
|
||
for warning, level in warnings.items():
|
||
print(f" {warning}: {self.get_warning_level_text(level)}")
|
||
|
||
return {
|
||
"pile_id": pile_id_bytes.hex(),
|
||
"timestamp": timestamp,
|
||
"voltage_request": voltage_request,
|
||
"current_request": current_request,
|
||
"charging_mode": self.get_charging_mode_text(charging_mode),
|
||
"max_cell_voltage": max_cell_voltage,
|
||
"max_cell_group": max_cell_group,
|
||
"soc": soc,
|
||
"remaining_charging_time": remaining_charging_time,
|
||
"max_voltage_group_number": max_voltage_group_number,
|
||
"max_battery_temperature": max_battery_temperature,
|
||
"max_temperature_point": max_temperature_point,
|
||
"min_battery_temperature": min_battery_temperature,
|
||
"min_temperature_point": min_temperature_point,
|
||
"warnings": {k: self.get_warning_level_text(v) for k, v in warnings.items()}
|
||
}
|
||
|
||
except Exception as e:
|
||
logging.error(f"解析30H命令失败: {str(e)}")
|
||
logging.error(f"原始报文: {binascii.hexlify(data)}")
|
||
return None
|
||
|
||
def get_charging_mode_text(self, mode):
|
||
"""
|
||
解析充电模式
|
||
|
||
:param mode: 充电模式字节
|
||
:return: 充电模式文本描述
|
||
"""
|
||
mode_map = {
|
||
0x01: "恒流充电",
|
||
0x02: "恒压充电",
|
||
0x03: "涓流充电",
|
||
0x04: "充电完成",
|
||
0x05: "充电终止"
|
||
}
|
||
return mode_map.get(mode, f"未知模式 (0x{mode:02X})")
|
||
|
||
def get_warning_level_text(self, level):
|
||
"""
|
||
解析告警级别
|
||
|
||
:param level: 告警级别
|
||
:return: 告警级别文本描述
|
||
"""
|
||
level_map = {
|
||
0x00: "正常",
|
||
0x01: "预警",
|
||
0x02: "严重告警",
|
||
0x03: "故障"
|
||
}
|
||
return level_map.get(level, f"未知级别 (0x{level:02X})")
|
||
|
||
def process_30h_bms_status(self, data):
|
||
"""
|
||
处理30H BMS状态信息命令
|
||
|
||
:param data: 完整的30H命令报文
|
||
:return: 是否成功处理
|
||
"""
|
||
try:
|
||
parsed_data = self.parse_30h_bms_status(data)
|
||
|
||
if parsed_data is None:
|
||
logging.warning("30H命令解析失败")
|
||
return False
|
||
|
||
# 记录BMS状态信息日志
|
||
logging.info(
|
||
f"收到桩号 {parsed_data['pile_id']} 的BMS状态信息: "
|
||
f"SOC {parsed_data['soc']}%, "
|
||
f"电压需求 {parsed_data['voltage_request']}V, "
|
||
f"电流需求 {parsed_data['current_request']}A"
|
||
)
|
||
|
||
return True
|
||
|
||
except Exception as e:
|
||
logging.error(f"处理30H命令出错: {str(e)}")
|
||
return False
|
||
|
||
|
||
# 测试用例
|
||
if __name__ == "__main__":
|
||
# 30H命令测试报文
|
||
test_30_data = bytes.fromhex(
|
||
"4A 58 30 03 17 66 56 11 36 06 37 01 1C 00 19 01 09 0B 25 15 01 60 1B 8D 07 02 DA 07 A0 0F 4A C1 49 78 00 01 40 03 3C 05 00 10 64")
|
||
|
||
parser = Command30()
|
||
|
||
# 测试解析30H命令
|
||
parser.process_30h_bms_status(test_30_data) |