215 lines
8.7 KiB
Python
215 lines
8.7 KiB
Python
import struct
|
||
import logging
|
||
import binascii
|
||
from datetime import datetime
|
||
|
||
|
||
class Command25:
|
||
def __init__(self):
|
||
self.command = 0x25 # 充电信息命令
|
||
|
||
def parse_25h_charging_info(self, data):
|
||
"""
|
||
解析25H充电信息命令
|
||
|
||
:param data: 完整的25H命令报文
|
||
:return: 解析后的字典或None
|
||
"""
|
||
try:
|
||
# 验证基本帧格式
|
||
if len(data) < 14 or data[0:2] != b'JX' or data[2] != 0x25:
|
||
logging.warning(f"25H命令帧格式不正确,原始报文: {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
|
||
charging_voltage = struct.unpack("<H", data[current_index:current_index + 2])[0] / 10 # 0.1V
|
||
current_index += 2
|
||
|
||
charging_current = struct.unpack("<H", data[current_index:current_index + 2])[0] / 10 # 0.1A
|
||
current_index += 2
|
||
|
||
charging_power = struct.unpack("<I", data[current_index:current_index + 4])[0] / 100 # 0.01kW
|
||
current_index += 4
|
||
|
||
charging_duration = struct.unpack("<I", data[current_index:current_index + 4])[0] # 秒
|
||
current_index += 4
|
||
|
||
charging_amount = struct.unpack("<I", data[current_index:current_index + 4])[0] / 100 # 0.01kWh
|
||
current_index += 4
|
||
|
||
charging_fee = struct.unpack("<I", data[current_index:current_index + 4])[0] / 100 # 0.01元
|
||
current_index += 4
|
||
|
||
# 解析充电模块接入数量
|
||
charging_module_count = data[current_index]
|
||
current_index += 1
|
||
|
||
# 解析充电电费
|
||
charging_electricity_fee = struct.unpack("<I", data[current_index:current_index + 4])[0] / 100 # 0.01元
|
||
current_index += 4
|
||
|
||
# 解析服务费
|
||
service_fee = struct.unpack("<I", data[current_index:current_index + 4])[0] / 100 # 0.01元
|
||
current_index += 4
|
||
|
||
# 解析充电订单号(17字节)
|
||
charging_order_number = data[current_index:current_index + 17].decode('ascii').rstrip('\x00')
|
||
current_index += 17
|
||
|
||
# 解析时间段信息
|
||
time_periods = []
|
||
time_period_count = data[current_index]
|
||
current_index += 1
|
||
|
||
for _ in range(time_period_count):
|
||
# 每个时间段的解析
|
||
period_start_time = datetime(
|
||
year, month, day,
|
||
data[current_index],
|
||
data[current_index + 1]
|
||
)
|
||
current_index += 2
|
||
|
||
period_type = data[current_index] # 1-尖;2-峰;3-平;4-谷
|
||
current_index += 1
|
||
|
||
period_electricity_price = struct.unpack("<I", data[current_index:current_index + 4])[
|
||
0] / 10000 # 0.0001元/kWh
|
||
current_index += 4
|
||
|
||
period_service_price = struct.unpack("<I", data[current_index:current_index + 4])[
|
||
0] / 10000 # 0.0001元/kWh
|
||
current_index += 4
|
||
|
||
period_electricity_amount = struct.unpack("<H", data[current_index:current_index + 2])[
|
||
0] / 100 # 0.01kWh
|
||
current_index += 2
|
||
|
||
period_electricity_fee = struct.unpack("<I", data[current_index:current_index + 4])[0] / 100 # 0.01元
|
||
current_index += 4
|
||
|
||
period_service_fee = struct.unpack("<I", data[current_index:current_index + 4])[0] / 100 # 0.01元
|
||
current_index += 4
|
||
|
||
time_periods.append({
|
||
"start_time": period_start_time,
|
||
"type": self.get_period_type_text(period_type),
|
||
"electricity_price": period_electricity_price,
|
||
"service_price": period_service_price,
|
||
"electricity_amount": period_electricity_amount,
|
||
"electricity_fee": period_electricity_fee,
|
||
"service_fee": period_service_fee
|
||
})
|
||
|
||
# 打印解析结果
|
||
print("\n25H充电信息命令解析结果:")
|
||
print(f"桩号: {pile_id_bytes.hex()}")
|
||
print(f"时间标识: {timestamp}")
|
||
print(f"充电电压: {charging_voltage}V")
|
||
print(f"充电电流: {charging_current}A")
|
||
print(f"充电功率: {charging_power}kW")
|
||
print(f"充电时长: {charging_duration}秒")
|
||
print(f"充电电量: {charging_amount}kWh")
|
||
print(f"充电金额: {charging_fee}元")
|
||
print(f"充电模块接入数量: {charging_module_count}")
|
||
print(f"充电电费: {charging_electricity_fee}元")
|
||
print(f"服务费: {service_fee}元")
|
||
print(f"充电订单号: {charging_order_number}")
|
||
print("时间段信息:")
|
||
for period in time_periods:
|
||
print(f" - 开始时间: {period['start_time']}")
|
||
print(f" 类型: {period['type']}")
|
||
print(f" 电价: {period['electricity_price']}元/kWh")
|
||
print(f" 服务费率: {period['service_price']}元/kWh")
|
||
print(f" 电量: {period['electricity_amount']}kWh")
|
||
print(f" 电费: {period['electricity_fee']}元")
|
||
print(f" 服务费: {period['service_fee']}元")
|
||
|
||
return {
|
||
"pile_id": pile_id_bytes.hex(),
|
||
"timestamp": timestamp,
|
||
"charging_voltage": charging_voltage,
|
||
"charging_current": charging_current,
|
||
"charging_power": charging_power,
|
||
"charging_duration": charging_duration,
|
||
"charging_amount": charging_amount,
|
||
"charging_fee": charging_fee,
|
||
"charging_module_count": charging_module_count,
|
||
"charging_electricity_fee": charging_electricity_fee,
|
||
"service_fee": service_fee,
|
||
"charging_order_number": charging_order_number,
|
||
"time_periods": time_periods
|
||
}
|
||
|
||
except Exception as e:
|
||
logging.error(f"解析25H命令失败: {str(e)}")
|
||
logging.error(f"原始报文: {binascii.hexlify(data)}")
|
||
return None
|
||
|
||
def get_period_type_text(self, period_type):
|
||
"""
|
||
解析时间段类型
|
||
|
||
:param period_type: 时间段类型字节
|
||
:return: 时间段类型文本描述
|
||
"""
|
||
type_map = {
|
||
1: "尖",
|
||
2: "峰",
|
||
3: "平",
|
||
4: "谷"
|
||
}
|
||
return type_map.get(period_type, f"未知类型 (0x{period_type:02X})")
|
||
|
||
def process_25h_charging_info(self, data):
|
||
"""
|
||
处理25H充电信息命令
|
||
|
||
:param data: 完整的25H命令报文
|
||
:return: 是否成功处理
|
||
"""
|
||
try:
|
||
parsed_data = self.parse_25h_charging_info(data)
|
||
|
||
if parsed_data is None:
|
||
logging.warning("25H命令解析失败")
|
||
return False
|
||
|
||
# 记录充电信息日志
|
||
logging.info(
|
||
f"收到桩号 {parsed_data['pile_id']} 的充电信息: "
|
||
f"充电电量 {parsed_data['charging_amount']}kWh, "
|
||
f"充电时长 {parsed_data['charging_duration']}秒, "
|
||
f"充电订单号 {parsed_data['charging_order_number']}"
|
||
)
|
||
|
||
return True
|
||
|
||
except Exception as e:
|
||
logging.error(f"处理25H命令出错: {str(e)}")
|
||
return False
|
||
|
||
|
||
# 测试用例
|
||
if __name__ == "__main__":
|
||
# 25H命令测试报文
|
||
test_25_data = bytes.fromhex(
|
||
"4A 58 25 03 17 66 56 11 36 06 37 01 61 00 19 01 09 0B 25 13 01 DA 07 00 00 00 00 00 00 22 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 31 38 37 37 31 39 37 38 30 31 36 31 35 35 35 36 36 31 30 00 00 00 00 00 00 00 00 00 00 00 00 00 01 19 01 09 0B 24 2D 19 01 09 0B 25 13 32 0F 00 00 AC 0D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3B 4A 58 30 03 17 66 56 11 36 06 37 01 1C 00 19 01 09 0B 25 13 01 00 00 A0 0F 02 DA 07 A0 0F 00 00 00 00 00 00 00 00 00 00 00 00 ED")
|
||
|
||
parser = Command25()
|
||
|
||
# 测试解析25H命令
|
||
parser.process_25h_charging_info(test_25_data) |