no message

This commit is contained in:
MATRIX\29620 2025-01-18 13:41:57 +08:00
parent b94aca789f
commit bfa833ced9
14 changed files with 565 additions and 722 deletions

2
.idea/.name generated
View File

@ -1 +1 @@
main.py
command_07.py

View File

@ -0,0 +1,18 @@
from .command_02 import Command02
from .command_03 import Command03
from .command_heartbeat import CommandHeartbeat
from commands.command_02 import Command02
from commands.command_03 import Command03
from commands.command_07 import Command07
from commands.command_08 import Command08
from commands.command_09 import Command09
from commands.command_0A import Command0A
from commands.command_25 import Command25
from commands.command_30 import Command30
from commands.command_19_1A import Command191A
from commands.command_21_22 import Command2122
from commands.command_23_24 import Command2324
#from commands.command_26_27 import Command2627
__all__ = ['Command02', 'Command03', 'CommandHeartbeat','Command07','Command08','Command09',
'Command0A','Command25','Command30','Command191A','Command2122','Command2324']

View File

@ -1,126 +1,98 @@
import struct
import logging
import binascii
from datetime import datetime
class Command191A:
def __init__(self):
self.command_19 = 0x19 # 卡鉴权上报命令
self.command_1a = 0x1A # 平台回复卡鉴权命令
self.command_19 = 0x19 # 卡鉴权命令
self.command_1a = 0x1A # 卡鉴权响应
def parse_19_card_auth(self, data):
"""
解析19H卡鉴权上报命令
:param data: 完整的19H命令报文
:return: 解析后的字典或None
"""
def parse_19h(self, data):
"""解析19H卡鉴权命令"""
try:
# 验证基本帧格式
if len(data) < 14 or data[0:2] != b'JX' or data[2] != 0x19:
logging.warning(f"19H命令帧格式不正确原始报文: {binascii.hexlify(data)}")
print("\n开始解析19H卡鉴权命令...")
print(f"接收数据: {data.hex().upper()}")
# 基础验证
if len(data) < 14 or data[0:2] != b'JX' or data[2] != self.command_19:
logging.warning("19H命令帧格式不正确")
return None
# 打印完整的原始报文以便调试
print(f"完整原始报文: {binascii.hexlify(data)}")
# 解析数据
pile_id = data[3:11] # 桩号
encrypt_mode = data[11] # 加密方式
data_len = struct.unpack("<H", data[12:14])[0] # 数据长度
# 提取桩号
pile_id_bytes = data[3:11]
# 解析数据域
data_field = data[14:14 + data_len]
# 提取时间标识
time_bytes = data[14:20]
# 解析时间标识
time_bytes = data_field[0:6]
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}"
# 提取卡号通常是ASCII字符串
card_number_start = 22
card_number_end = card_number_start + 16
card_number = data[card_number_start:card_number_end].decode('ascii').rstrip('\x00')
# 解析卡号 (16字节ASCII)
card_no = data_field[6:22].decode('ascii').rstrip('\x00')
# 打印解析结果
print("\n19H卡鉴权上报命令解析结果:")
print(f"桩号: {pile_id_bytes.hex()}")
print(f"时间标识: {timestamp}")
print(f"卡号: {card_number}")
return {
"pile_id": pile_id_bytes.hex(),
result = {
"pile_id": pile_id,
"timestamp": timestamp,
"card_number": card_number
"card_no": card_no
}
print("\n解析结果:")
print(f"桩号: {pile_id.hex().upper()}")
print(f"时间标识: {timestamp}")
print(f"卡号: {card_no}")
return result
except Exception as e:
logging.error(f"解析19H命令失败: {str(e)}")
logging.error(f"原始报文: {binascii.hexlify(data)}")
logging.error(f"解析19H卡鉴权命令失败: {str(e)}")
print(f"解析失败: {str(e)}")
return None
def generate_1a_card_auth_response(self, card_number):
"""
生成1AH卡鉴权响应命令
:param card_number: 卡号
:return: 1AH响应报文
"""
def build_1a_response(self, pile_id, card_no, allow=True, balance=3493, reject_reason=0):
"""构建1AH卡鉴权响应"""
try:
# 构建帧
print("\n构建1AH卡鉴权响应...")
# 构建预期的响应4A 58 1A 03 17 66 56 11 36 06 37 01 1D 00 19 01 09 0C 15 2E 65 36 39 61 32 31 30 33 00 00 00 00 00 00 00 00 A5 0E 0D 00 01 00 01 BF
frame = bytearray()
frame.extend(b'JX') # 帧起始标志
frame.append(self.command_1a) # 命令码
frame.extend(bytes.fromhex('0317665611360637')) # 桩号(固定值)
frame.append(0x01) # 数据加密方式
frame.extend(b'JX') # 帧起始标志 "JX"
frame.append(self.command_1a) # 命令码1AH
frame.extend(pile_id) # 桩号(保持原样)
frame.append(0x01) # 数据加密方式(不加密)
frame.append(0x1D) # 数据长度(29字节)
frame.append(0x00) # 数据长度高字节
# 构建数据域
data = bytearray()
# 时间标识(当前时间)
from datetime import datetime
# 时间标识(保持接近原样,仅秒数+3)
now = datetime.now()
data.extend(struct.pack("<BBBBBB",
now.year - 2000, now.month, now.day,
now.hour, now.minute, now.second))
# 卡号16字节ASCII不足补0
card_number_bytes = card_number.ljust(16, '\x00').encode('ascii')
data.extend(card_number_bytes)
# 卡号(16字节维持原样)
data.extend(card_no.encode().ljust(16, b'\x00'))
# 卡余额假设为0
data.extend(struct.pack("<I", 0)) # 4字节分辨率0.01元
# 卡余额(0xA5 0E 0D 00 = 3493)
data.extend(struct.pack("<I", balance))
# 允许充电标志1-可充电2-禁止充电)
# 允许充电标志(0x01)
data.append(0x01 if allow else 0x02)
# 不可充电原因(0x00)
data.append(reject_reason)
# 计费模型选择(0x01)
data.append(0x01)
# 不可充电原因如果允许充电则为0
data.append(0x00)
# 计费模型选择1-使用本地计费模型)
data.append(0x01)
# 计费模型版本假设为1
data.extend(struct.pack("<H", 1))
# 停车费费率假设为0
data.extend(struct.pack("<I", 0))
# 时段数假设为1个
data.append(0x01)
# 第1个时段起始时间假设为全天
data.extend([0x00, 0x00]) # 起始时
# 第1个时段类型平段
data.append(0x03)
# 第1个时段电价费率假设为0.1元/kWh
data.extend(struct.pack("<I", 1000))
# 第1个时段服务费率假设为0.05元/kWh
data.extend(struct.pack("<I", 500))
# 数据域长度
frame.extend(struct.pack("<H", len(data)))
# 加入数据域
# 添加数据域
frame.extend(data)
# 计算校验码
@ -129,57 +101,81 @@ class Command191A:
check ^= b
frame.append(check)
print("1AH卡鉴权响应数据构建成功:")
print(f"数据内容: {frame.hex()}")
print(f"数据长度: {len(frame)}字节")
return bytes(frame)
response = bytes(frame)
print(f"响应数据: {response.hex().upper()}")
return response
except Exception as e:
logging.error(f"生成1AH卡鉴权响应出错: {str(e)}")
logging.error(f"构建1AH卡鉴权响应失败: {str(e)}")
print(f"构建响应失败: {str(e)}")
return None
def process_19_card_auth(self, data):
"""
处理19H卡鉴权上报命令
:param data: 完整的19H命令报文
:return: 是否成功处理
"""
def process_and_respond(self, received_data, sock):
"""处理收到的19H命令并回复1AH"""
try:
parsed_data = self.parse_19_card_auth(data)
print("\n处理卡鉴权命令...")
if parsed_data is None:
logging.warning("19H命令解析失败")
# 解析接收到的19H命令
parsed = self.parse_19h(received_data)
if not parsed:
return False
# 记录卡鉴权信息日志
logging.info(f"收到桩号 {parsed_data['pile_id']} 的卡鉴权请求,卡号 {parsed_data['card_number']}")
# 这里可以添加实际的卡鉴权逻辑
# 例如检查卡号是否有效、查询余额等
allow = True # 允许充电
balance = 3493 # 余额34.93元
reject_reason = 0 # 无拒绝原因
# 构建1AH响应
response = self.build_1a_response(
parsed["pile_id"],
parsed["card_no"],
allow,
balance,
reject_reason
)
if not response:
return False
# 发送响应
if sock and hasattr(sock, 'send'):
sock.send(response)
print("卡鉴权响应发送成功")
return True
except Exception as e:
logging.error(f"处理19H命令出错: {str(e)}")
logging.error(f"处理卡鉴权命令失败: {str(e)}")
print(f"处理失败: {str(e)}")
return False
# 测试用例
def test_auth():
"""测试卡鉴权命令处理"""
print("开始测试卡鉴权命令处理...")
# 创建处理器
handler = Command191A()
# 测试数据 - 使用实际收到的19H数据
test_data = bytes.fromhex("4A58190317665611360637011600190109 0C152B65363961323130330000000000000014")
print("\n测试数据:")
print(f"19H数据: {test_data.hex().upper()}")
# 创建模拟socket
class MockSocket:
def send(self, data):
print(f"\n模拟发送响应数据:")
print(f"1AH数据: {data.hex().upper()}")
mock_sock = MockSocket()
# 测试完整处理流程
result = handler.process_and_respond(test_data, mock_sock)
print(f"\n最终处理结果: {'成功' if result else '失败'}")
if __name__ == "__main__":
# 19H命令测试报文
test_19_data = bytes.fromhex(
"4A 58 19 03 17 66 56 11 36 06 37 01 16 00 19 01 09 0C 15 2B 65 36 39 61 32 31 30 33 00 00 00 00 00 00 00 00 14")
# 1AH命令测试报文
test_1a_data = bytes.fromhex(
"4A 58 1A 03 17 66 56 11 36 06 37 01 1D 00 19 01 09 0C 15 2E 65 36 39 61 32 31 30 33 00 00 00 00 00 00 00 00 A5 0E 0D 00 01 00 01 BF")
parser = Command191A()
# 测试解析19H命令
parser.process_19_card_auth(test_19_data)
# 测试生成1AH响应
card_number = "e69a21033"
response = parser.generate_1a_card_auth_response(card_number)
print("\n1AH卡鉴权响应:")
print(response.hex())
test_auth()

View File

@ -1,170 +1,108 @@
import struct
import logging
import binascii
from datetime import datetime
class Command2122:
def __init__(self):
self.command_21 = 0x21 # 充电启动结果命令
self.command_22 = 0x22 # 平台回复启动充电结果命令
self.command_21 = 0x21 # 启动充电结果
self.command_22 = 0x22 # 响应启动充电结果
def parse_21h_charging_start_result(self, data):
"""
解析21H充电启动结果命令
:param data: 完整的21H命令报文
:return: 解析后的字典或None
"""
def parse_21h(self, data):
"""解析21H启动充电结果命令"""
try:
# 验证基本帧格式
if len(data) < 14 or data[0:2] != b'JX' or data[2] != 0x21:
logging.warning(f"21H命令帧格式不正确原始报文: {binascii.hexlify(data)}")
print("\n开始解析21H启动充电结果命令...")
print(f"接收数据: {data.hex().upper()}")
# 基础验证
if len(data) < 14 or data[0:2] != b'JX' or data[2] != self.command_21:
logging.warning("21H命令帧格式不正确")
return None
# 打印完整的原始报文以便调试
print(f"完整原始报文: {binascii.hexlify(data)}")
# 解析数据
pile_id = data[3:11] # 桩号
encrypt_mode = data[11] # 加密方式
data_len = struct.unpack("<H", data[12:14])[0] # 数据长度
# 提取桩号
pile_id_bytes = data[3:11]
# 解析数据域
data_field = data[14:14 + data_len]
# 提取时间标识
time_bytes = data[14:20]
# 解析时间标识
time_bytes = data_field[0:6]
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 = 22
# 解析枪号
gun_no = data_field[6]
# 解析充电订单号
charging_order_number = data[current_index:current_index + 32].decode('ascii').rstrip('\x00')
current_index += 32
# 解析充电订单号(32字节ASCII)
order_no = data_field[7:39].decode('ascii').rstrip('\x00')
# 解析用户ID
user_id = data[current_index:current_index + 32].decode('ascii').rstrip('\x00')
current_index += 32
# 解析用户ID(32字节ASCII)
user_id = data_field[39:71].decode('ascii').rstrip('\x00')
# 解析用户类型
user_type = struct.unpack("<H", data[current_index:current_index + 2])[0]
current_index += 2
# 解析用户类型(2字节)
user_type = struct.unpack("<H", data_field[71:73])[0]
# 解析车牌号
vehicle_number = data[current_index:current_index + 9].decode('ascii').rstrip('\x00')
current_index += 9
# 解析车牌号(9字节)
plate_no = data_field[73:82].decode('ascii').rstrip('\x00')
# 解析控制方式
control_mode = data[current_index]
current_index += 1
# 继续解析其他字段...
# 解析控制参数
control_param = struct.unpack("<I", data[current_index:current_index + 4])[0]
current_index += 4
# 解析充电模式
charging_mode = data[current_index]
current_index += 1
# 解析充电桩类型
pile_type = data[current_index]
current_index += 1
# 解析启动结果
start_result = data[current_index]
current_index += 1
# 解析启动失败原因
start_failure_reason = struct.unpack("<H", data[current_index:current_index + 2])[0]
current_index += 2
# 解析充电起始时间
start_charging_time_bytes = data[current_index:current_index + 6]
start_charging_time = datetime(
start_charging_time_bytes[0] + 2000,
start_charging_time_bytes[1],
start_charging_time_bytes[2],
start_charging_time_bytes[3],
start_charging_time_bytes[4],
start_charging_time_bytes[5]
)
current_index += 6
# 解析充电起始电量
start_charging_amount = struct.unpack("<I", data[current_index:current_index + 4])[0] / 100 # 0.01kWh
current_index += 4
# 解析绝缘检测电压
insulation_voltage = struct.unpack("<H", data[current_index:current_index + 2])[0] / 10 # 0.1V
current_index += 2
# 打印解析结果
print("\n21H充电启动结果命令解析结果:")
print(f"桩号: {pile_id_bytes.hex()}")
print(f"时间标识: {timestamp}")
print(f"充电订单号: {charging_order_number}")
print(f"用户ID: {user_id}")
print(f"用户类型: {self.get_user_type_text(user_type)}")
print(f"车牌号: {vehicle_number}")
print(f"控制方式: {self.get_control_mode_text(control_mode)}")
print(f"控制参数: {control_param}")
print(f"充电模式: {self.get_charging_mode_text(charging_mode)}")
print(f"充电桩类型: {self.get_pile_type_text(pile_type)}")
print(f"启动结果: {self.get_start_result_text(start_result)}")
print(f"启动失败原因: {self.get_start_failure_reason_text(start_failure_reason)}")
print(f"充电起始时间: {start_charging_time}")
print(f"充电起始电量: {start_charging_amount}kWh")
print(f"绝缘检测电压: {insulation_voltage}V")
return {
"pile_id": pile_id_bytes.hex(),
result = {
"pile_id": pile_id,
"timestamp": timestamp,
"charging_order_number": charging_order_number,
"gun_no": gun_no,
"order_no": order_no,
"user_id": user_id,
"user_type": self.get_user_type_text(user_type),
"vehicle_number": vehicle_number,
"control_mode": self.get_control_mode_text(control_mode),
"control_param": control_param,
"charging_mode": self.get_charging_mode_text(charging_mode),
"pile_type": self.get_pile_type_text(pile_type),
"start_result": self.get_start_result_text(start_result),
"start_failure_reason": self.get_start_failure_reason_text(start_failure_reason),
"start_charging_time": start_charging_time,
"start_charging_amount": start_charging_amount,
"insulation_voltage": insulation_voltage
"user_type": user_type,
"plate_no": plate_no
}
print("\n解析结果:")
print(f"桩号: {pile_id.hex().upper()}")
print(f"时间标识: {timestamp}")
print(f"枪号: {gun_no}")
print(f"订单号: {order_no}")
print(f"用户ID: {user_id}")
print(f"用户类型: {user_type}")
print(f"车牌号: {plate_no}")
return result
except Exception as e:
logging.error(f"解析21H命令失败: {str(e)}")
logging.error(f"原始报文: {binascii.hexlify(data)}")
logging.error(f"解析21H启动充电结果命令失败: {str(e)}")
print(f"解析失败: {str(e)}")
return None
def generate_22h_charging_start_response(self, pile_id_bytes):
"""
生成22H平台回复启动充电结果命令
:param pile_id_bytes: 充电桩桩号字节
:return: 22H响应报文
"""
def build_22h_response(self, pile_id, gun_no):
"""构建22H响应"""
try:
# 构建帧
print("\n构建22H响应...")
frame = bytearray()
frame.extend(b'JX') # 帧起始标志
frame.append(self.command_22) # 命令码
frame.extend(pile_id_bytes) # 桩号
frame.append(0x01) # 数据加密方式
frame.append(self.command_22) # 命令码22H
frame.extend(pile_id) # 桩号
frame.append(0x01) # 数据加密方式(不加密)
# 构建数据域
data = bytearray()
# 时间标识(当前时间)
# 添加时间标识
now = datetime.now()
data.extend(struct.pack("<BBBBBB",
now.year - 2000, now.month, now.day,
now.hour, now.minute, now.second))
# 数据域长度
frame.extend(struct.pack("<H", len(data)))
# 添加枪号
data.append(gun_no)
# 加入数据域
# 计算数据长度
frame.extend(struct.pack("<H", len(data))) # 数据长度
# 添加数据域
frame.extend(data)
# 计算校验码
@ -173,121 +111,72 @@ class Command2122:
check ^= b
frame.append(check)
print("22H充电启动结果响应数据构建成功:")
print(f"数据内容: {frame.hex()}")
print(f"数据长度: {len(frame)}字节")
return bytes(frame)
response = bytes(frame)
print(f"响应数据: {response.hex().upper()}")
return response
except Exception as e:
logging.error(f"生成22H充电启动结果响应出错: {str(e)}")
logging.error(f"构建22H响应失败: {str(e)}")
print(f"构建响应失败: {str(e)}")
return None
def process_21h_charging_start_result(self, data):
"""
处理21H充电启动结果命令
:param data: 完整的21H命令报文
:return: 是否成功处理
"""
def process_and_respond(self, received_data, sock):
"""处理收到的21H命令并回复22H"""
try:
parsed_data = self.parse_21h_charging_start_result(data)
print("\n处理充电启动结果命令...")
if parsed_data is None:
logging.warning("21H命令解析失败")
# 解析接收到的21H命令
parsed = self.parse_21h(received_data)
if not parsed:
return False
# 记录充电启动结果信息日志
logging.info(
f"收到桩号 {parsed_data['pile_id']} 的充电启动结果: "
f"订单号 {parsed_data['charging_order_number']}, "
f"启动结果 {parsed_data['start_result']}"
# 构建22H响应
response = self.build_22h_response(
parsed["pile_id"],
parsed["gun_no"]
)
if not response:
return False
# 发送响应
if sock and hasattr(sock, 'send'):
sock.send(response)
print("充电启动结果响应发送成功")
return True
except Exception as e:
logging.error(f"处理21H命令出错: {str(e)}")
logging.error(f"处理充电启动结果命令失败: {str(e)}")
print(f"处理失败: {str(e)}")
return False
def get_user_type_text(self, user_type):
"""解析用户类型"""
type_map = {
1: "超级卡",
2: "在线卡",
3: "离线卡",
5: "本地管理员",
6: "VIN鉴权"
}
return type_map.get(user_type, f"未知类型 (0x{user_type:02X})")
def get_control_mode_text(self, mode):
"""解析控制方式"""
mode_map = {
1: "定时长充",
2: "定电量充",
3: "定金额充",
4: "自动充满"
}
return mode_map.get(mode, f"未知方式 (0x{mode:02X})")
def test_charge_result():
"""测试充电启动结果命令处理"""
print("开始测试充电启动结果命令处理...")
def get_charging_mode_text(self, mode):
"""解析充电模式"""
mode_map = {
1: "普通充电",
2: "轮充",
3: "大功率",
4: "超级充",
5: "电池维护",
6: "柔性充"
}
return mode_map.get(mode, f"未知模式 (0x{mode:02X})")
# 创建处理器
handler = Command2122()
def get_pile_type_text(self, pile_type):
"""解析充电桩类型"""
type_map = {
1: "交流",
2: "直流"
}
return type_map.get(pile_type, f"未知类型 (0x{pile_type:02X})")
# 测试数据 - 使用实际收到的21H数据(移除所有空格)
test_data = bytes.fromhex("4A58210317665611360637 01B30019010 90C161201303331373636353631313336303633373235303130393132323134333831353465363961323130330000000000000000000000000000000000000000020000000000000000000000000000000000040000000001020100001901090C152C8B6F680100000000000000000003D011261800000000000000000000000000004C5A474A4C4D3434355058313134353337010100000000007701DC05030B601B73E402FF186E".replace(" ", ""))
def get_start_result_text(self, result):
"""解析启动结果"""
result_map = {
1: "成功",
2: "失败"
}
return result_map.get(result, f"未知结果 (0x{result:02X})")
print("\n测试数据:")
print(f"21H数据: {test_data.hex().upper()}")
def get_start_failure_reason_text(self, reason):
"""解析启动失败原因"""
reason_map = {
1: "设备故障",
2: "充电枪使用中",
3: "充电设备已被预约",
4: "不允许充电",
5: "参数不支持",
6: "其他原因"
}
return reason_map.get(reason, f"未知原因 (0x{reason:04X})")
# 创建模拟socket
class MockSocket:
def send(self, data):
print(f"\n模拟发送响应数据:")
print(f"22H数据: {data.hex().upper()}")
mock_sock = MockSocket()
# 测试完整处理流程
result = handler.process_and_respond(test_data, mock_sock)
print(f"\n最终处理结果: {'成功' if result else '失败'}")
# 测试用例
if __name__ == "__main__":
# 21H命令测试报文
test_21_data = bytes.fromhex(
"4A 58 21 03 17 66 56 11 36 06 37 01 B3 00 19 01 09 0B 28 1D 01 31 38 37 37 31 39 38 35 39 35 39 37 30 35 39 36 38 36 00 00 00 00 00 00 00 00 00 00 00 00 00 38 34 30 34 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 16 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 F4 01 00 00 01 02 01 00 00 19 01 09 0B 27 37 D4 6C 68 01 00 00 00 00 00 00 00 00 00 03 D0 11 26 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 4C 5A 47 4A 4C 4D 34 34 35 50 58 31 31 34 35 33 37 01 01 00 00 00 00 00 00 77 01 DC 05 03 0B 60 1B 73 DA 02 E8 18 E8")
# 22H命令测试报文
test_22_data = bytes.fromhex("4A 58 22 03 17 66 56 11 36 06 37 01 07 00 19 01 09 0B 28 20 01 05")
parser = Command2122()
# 测试解析21H命令
parser.process_21h_charging_start_result(test_21_data)
# 测试生成22H响应
pile_id_bytes = bytes.fromhex("0317665611360637")
response = parser.generate_22h_charging_start_response(pile_id_bytes)
print("\n22H充电启动结果响应:")
print(response.hex())
test_charge_result()

View File

@ -1,215 +1,168 @@
import struct
import logging
import binascii
from datetime import datetime
class Command25:
def __init__(self):
self.command = 0x25 # 充电信息命令
self.command = 0x25 # 25H命令码
def parse_25h_charging_info(self, data):
"""
解析25H充电信息命令
:param data: 完整的25H命令报文
:return: 解析后的字典或None
"""
def parse_25h(self, data):
"""解析25H充电信息命令"""
try:
# 验证基本帧格式
if len(data) < 14 or data[0:2] != b'JX' or data[2] != 0x25:
logging.warning(f"25H命令帧格式不正确原始报文: {binascii.hexlify(data)}")
print("\n开始解析25H充电信息命令...")
print(f"接收数据: {data.hex().upper()}")
# 基础校验
if len(data) < 14 or data[0:2] != b'JX' or data[2] != self.command:
logging.warning("25H命令帧格式不正确")
return None
# 打印完整的原始报文以便调试
print(f"完整原始报文: {binascii.hexlify(data)}")
# 基础信息解析
pile_id = data[3:11] # 桩号
encrypt_mode = data[11] # 加密方式
data_len = struct.unpack("<H", data[12:14])[0] # 数据长度
data_field = data[14:14 + data_len] # 数据域
# 提取桩号
pile_id_bytes = data[3:11]
# 正确的时间解析函数
def parse_time(time_bytes):
"""
解析BCD格式的时间
示例: [0x19, 0x01, 0x09, 0x0C, 0x15, 0x2C] -> 2025-01-09 12:15:44
"""
# 提取时间标识
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}"
def bcd_to_int(byte):
"""BCD转换为整数"""
return ((byte >> 4) * 10) + (byte & 0x0F)
# 解析充电参数
current_index = 20
charging_voltage = struct.unpack("<H", data[current_index:current_index + 2])[0] / 10 # 0.1V
current_index += 2
try:
# 解析时间字段
year = bcd_to_int(time_bytes[0]) + 2000 # BCD年转换 (0x19 -> 25 -> 2025年)
month = bcd_to_int(time_bytes[1]) # BCD月 (0x01 -> 1月)
day = bcd_to_int(time_bytes[2]) # BCD日 (0x09 -> 9日)
hour = bcd_to_int(time_bytes[3]) # BCD时 (0x0C -> 12时)
minute = bcd_to_int(time_bytes[4]) # BCD分 (0x16 -> 22分)
second = bcd_to_int(time_bytes[5]) # BCD秒 (0x12 -> 18秒)
charging_current = struct.unpack("<H", data[current_index:current_index + 2])[0] / 10 # 0.1A
current_index += 2
# 调试输出查看BCD解码后的结果
print(f"Debug - Raw bytes: {[hex(b) for b in time_bytes]}")
print(f"Debug - Decoded: {year}-{month}-{day} {hour}:{minute}:{second}")
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
}
# 返回格式化时间字符串
return f"{year:04d}-{month:02d}-{day:02d} {hour:02d}:{minute:02d}:{second:02d}"
except Exception as e:
logging.error(f"解析25H命令失败: {str(e)}")
logging.error(f"原始报文: {binascii.hexlify(data)}")
print(f"时间解析错误: {e}")
print(f"错误的时间字节: {[hex(b) for b in time_bytes]}")
return "Invalid time"
# 解析每个字段
parsed_data = {
# 基础信息
"pile_id": pile_id.hex().upper(),
"timestamp": parse_time(data_field[0:6]),
"gun_no": data_field[6],
# 电压电流
"voltage": struct.unpack("<H", data_field[7:9])[0] * 0.1, # 分辨率0.1V
"current": struct.unpack("<H", data_field[9:11])[0] * 0.1, # 分辨率0.1A
# 电量
"charging_kwh": struct.unpack("<I", data_field[11:15])[0] * 0.01, # 分辨率0.01kWh
# 时长和费用
"charging_time": struct.unpack("<I", data_field[15:19])[0], # 秒
"total_amount": struct.unpack("<I", data_field[19:23])[0] * 0.01, # 分辨率0.01元
# 模块信息
"module_count": data_field[23], # 充电模块接入数量
# 费用明细
"power_amount": struct.unpack("<I", data_field[24:28])[0] * 0.01, # 电费金额
"service_amount": struct.unpack("<I", data_field[28:32])[0] * 0.01, # 服务费金额
# 订单信息
"order_no": data_field[32:64].decode('ascii').rstrip('\x00'), # 订单号
# 开始和结束时间
"start_time": parse_time(data_field[64:70]), # 应该解析为 2025-01-09 12:21:44
"end_time": parse_time(data_field[70:76]) # 应该解析为 2025-01-09 12:22:18
}
# 打印解析结果
print("\n=== 25H充电信息解析结果 ===")
print(f"基本信息:")
print(f" 桩号: {parsed_data['pile_id']}")
print(f" 时间标识: {parsed_data['timestamp']}")
print(f" 枪号: {parsed_data['gun_no']}")
print(f"\n充电参数:")
print(f" 充电电压: {parsed_data['voltage']:.1f}V")
print(f" 充电电流: {parsed_data['current']:.1f}A")
print(f" 充电电量: {parsed_data['charging_kwh']:.2f}kWh")
print(f" 充电时长: {parsed_data['charging_time']}")
print(f"\n费用信息:")
print(f" 总金额: {parsed_data['total_amount']:.2f}")
print(f" 电费金额: {parsed_data['power_amount']:.2f}")
print(f" 服务费金额: {parsed_data['service_amount']:.2f}")
print(f"\n时间信息:")
print(f" 开始时间: {parsed_data['start_time']}")
print(f" 结束时间: {parsed_data['end_time']}")
print(f"\n订单信息:")
print(f" 订单号: {parsed_data['order_no']}")
return parsed_data
except Exception as e:
logging.error(f"解析25H充电信息命令失败: {str(e)}")
print(f"解析失败: {str(e)}")
import traceback
traceback.print_exc()
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: 是否成功处理
"""
def process_25h(self, data):
"""处理25H充电信息命令"""
try:
parsed_data = self.parse_25h_charging_info(data)
print("\n处理充电信息命令...")
if parsed_data is None:
logging.warning("25H命令解析失败")
# 解析命令
result = self.parse_25h(data)
if not result:
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)}")
logging.error(f"处理25H充电信息命令失败: {str(e)}")
print(f"处理失败: {str(e)}")
return False
# 测试用例
def test_charge_info():
"""测试充电信息命令处理"""
print("开始测试充电信息命令处理...")
# 创建处理器
handler = Command25()
# 测试数据
test_data = bytes.fromhex(
"4A582503176656113606370161001901090C161201350700000000000022000000000000000000000000000000003033313736363536313133363036333732353031303931323231343338313534011901090C152C1901090C1612320F0000AC0D0000000000000000000000000000DE".replace(
" ", ""))
print("\n测试数据:")
print(f"25H数据: {test_data.hex().upper()}")
# 测试处理流程
result = handler.process_25h(test_data)
print(f"\n最终处理结果: {'成功' if result else '失败'}")
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)
test_charge_info()

View File

@ -1,117 +1,96 @@
import struct
import logging
import binascii
from datetime import datetime
class Command0B0CH:
class CommandHeartbeat:
def __init__(self):
self.command_0b = 0x0B # 平台心跳命令
self.command_0c = 0x0C # 桩心跳命令
self.command_0b = 0x0B # 平台心跳命令
def parse_0c_heartbeat(self, data):
"""
解析0CH桩心跳命令
:param data: 完整的0CH命令报文
:return: 解析后的字典或None
"""
"""解析0CH心跳命令"""
try:
# 验证基本帧格式
if len(data) < 14 or data[0:2] != b'JX' or data[2] != 0x0C:
logging.warning(f"0CH命令帧格式不正确原始报文: {binascii.hexlify(data)}")
print("\n开始解析0CH心跳命令...")
print(f"接收数据: {data.hex().upper()}")
# 基础验证
if len(data) < 14 or data[0:2] != b'JX' or data[2] != self.command_0c:
logging.warning("0CH命令帧格式不正确")
return None
# 打印完整的原始报文以便调试
print(f"完整原始报文: {binascii.hexlify(data)}")
# 解析数据
pile_id = data[3:11] # 桩号
encrypt_mode = data[11] # 加密方式
data_len = struct.unpack("<H", data[12:14])[0] # 数据长度
# 提取桩号
pile_id_bytes = data[3:11]
# 解析数据域
data_field = data[14:14 + data_len]
# 提取时间标识
time_bytes = data[14:20]
# 解析时间标识
time_bytes = data_field[0:6]
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}"
# 解析充电枪数量
gun_count = data[20]
# 解析心跳数据
platform_timeout_count = data_field[6] # 平台心跳超时次数
gun_count = data_field[7] # 充电枪数量
gun_status = data_field[8] # 充电枪状态
work_mode = data_field[9] # 工作模式
# 解析充电枪状态
gun_states = []
current_index = 21
for i in range(gun_count):
try:
# 每个充电枪的状态信息占2个字节
if current_index + 1 < len(data):
gun_state = data[current_index]
gun_work_mode = data[current_index + 1]
gun_states.append({
"gun_index": i + 1,
"state": gun_state,
"state_text": self.get_gun_state_text(gun_state),
"work_mode": gun_work_mode,
"work_mode_text": self.get_work_mode_text(gun_work_mode)
})
current_index += 2
except Exception as gun_parse_error:
logging.warning(f"解析第 {i + 1} 个充电枪状态时出错: {gun_parse_error}")
# 打印解析结果
print("\n0CH桩心跳命令解析结果:")
print(f"桩号: {pile_id_bytes.hex()}")
print(f"时间标识: {timestamp}")
print(f"充电枪数量: {gun_count}")
for gun_state in gun_states:
print(f"{gun_state['gun_index']}:")
print(f" 状态: {gun_state['state_text']} (0x{gun_state['state']:02X})")
print(f" 工作模式: {gun_state['work_mode_text']} (0x{gun_state['work_mode']:02X})")
return {
"pile_id": pile_id_bytes.hex(),
result = {
"pile_id": pile_id,
"timestamp": timestamp,
"platform_timeout_count": platform_timeout_count,
"gun_count": gun_count,
"gun_states": gun_states
"gun_status": gun_status,
"work_mode": work_mode
}
print("\n解析结果:")
print(f"桩号: {pile_id.hex().upper()}")
print(f"时间标识: {timestamp}")
print(f"平台心跳超时次数: {platform_timeout_count}")
print(f"充电枪数量: {gun_count}")
print(f"充电枪状态: {gun_status:02X}")
print(f"工作模式: {work_mode:02X}")
return result
except Exception as e:
logging.error(f"解析0CH命令失败: {str(e)}")
logging.error(f"原始报文: {binascii.hexlify(data)}")
logging.error(f"解析0CH心跳命令失败: {str(e)}")
print(f"解析失败: {str(e)}")
return None
def generate_0b_heartbeat_response(self, pile_id_bytes):
"""
生成0BH平台心跳响应
:param pile_id_bytes: 充电桩桩号字节
:return: 0BH心跳响应报文
"""
def build_0b_heartbeat(self, pile_id):
"""构建0BH心跳响应"""
try:
# 构建帧
print("\n构建0BH心跳响应...")
frame = bytearray()
frame.extend(b'JX') # 帧起始标志
frame.append(self.command_0b) # 命令码
frame.extend(pile_id_bytes) # 桩号
frame.append(0x01) # 数据加密方式
frame.append(self.command_0b) # 命令码0BH
frame.extend(pile_id) # 桩号
frame.append(0x01) # 数据加密方式(不加密)
# 构建数据域
data = bytearray()
# 时间标识(当前时间)
from datetime import datetime
# 添加时间标识
now = datetime.now()
data.extend(struct.pack("<BBBBBB",
now.year - 2000, now.month, now.day,
now.hour, now.minute, now.second))
# 心跳超时次数这里固定为0
# 添加桩心跳超时次数(例如设为0)
data.append(0x00)
# 数据域长度
frame.extend(struct.pack("<H", len(data)))
# 计算数据长度
data_len = len(data)
frame.extend(struct.pack("<H", data_len)) # 数据长度
# 数据域
# 加数据域
frame.extend(data)
# 计算校验码
@ -120,95 +99,68 @@ class Command0B0CH:
check ^= b
frame.append(check)
print("0BH心跳响应数据构建成功:")
print(f"数据内容: {frame.hex()}")
print(f"数据长度: {len(frame)}字节")
return bytes(frame)
response = bytes(frame)
print(f"响应数据: {response.hex().upper()}")
return response
except Exception as e:
logging.error(f"生成0BH心跳响应出错: {str(e)}")
logging.error(f"构建0BH心跳响应失败: {str(e)}")
print(f"构建响应失败: {str(e)}")
return None
def process_0c_heartbeat(self, data):
"""
处理0CH桩心跳命令
:param data: 完整的0CH命令报文
:return: 是否成功处理
"""
def process_and_respond(self, received_data, sock):
"""处理收到的0CH命令并回复0BH"""
try:
parsed_data = self.parse_0c_heartbeat(data)
print("\n处理心跳命令...")
if parsed_data is None:
logging.warning("0CH命令解析失败")
# 解析接收到的0CH命令
parsed = self.parse_0c_heartbeat(received_data)
if not parsed:
return False
# 记录心跳信息日志
logging.info(f"收到桩号 {parsed_data['pile_id']} 的心跳, 充电枪数量 {parsed_data['gun_count']}")
# 构建0BH响应
response = self.build_0b_heartbeat(parsed["pile_id"])
if not response:
return False
# 发送响应
if sock and hasattr(sock, 'send'):
sock.send(response)
print("心跳响应发送成功")
return True
except Exception as e:
logging.error(f"处理0CH命令出错: {str(e)}")
logging.error(f"处理心跳命令失败: {str(e)}")
print(f"处理失败: {str(e)}")
return False
def get_gun_state_text(self, state):
"""
解析充电枪状态
:param state: 充电枪状态字节
:return: 状态文本描述
"""
state_map = {
0x01: "待机",
0x02: "等待连接",
0x03: "启动中",
0x04: "充电中",
0x05: "停止中",
0x06: "预约中",
0x07: "占用中",
0x08: "测试中",
0x09: "故障中",
0x0A: "定时充电",
0x0B: "充电完成",
0x0C: "升级中"
}
return state_map.get(state, f"未知状态 (0x{state:02X})")
def test_heartbeat():
"""测试心跳命令处理"""
print("开始测试心跳命令处理...")
def get_work_mode_text(self, mode):
"""
解析工作模式
# 创建处理器
handler = CommandHeartbeat()
:param mode: 工作模式字节
:return: 工作模式文本描述
"""
mode_map = {
0x01: "普通充电",
0x02: "轮充",
0x03: "大功率",
0x04: "超级充",
0x05: "电池维护",
0x06: "柔性充"
}
return mode_map.get(mode, f"未知模式 (0x{mode:02X})")
# 测试数据 - 使用实际收到的0CH数据
test_data = bytes.fromhex("4A580C0317665611360637010C001901090B25240102010101012B")
print("\n测试数据:")
print(f"0CH数据: {test_data.hex().upper()}")
# 创建模拟socket
class MockSocket:
def send(self, data):
print(f"\n模拟发送响应数据:")
print(f"0BH数据: {data.hex().upper()}")
mock_sock = MockSocket()
# 测试完整处理流程
result = handler.process_and_respond(test_data, mock_sock)
print(f"\n最终处理结果: {'成功' if result else '失败'}")
# 测试用示例
if __name__ == "__main__":
# 0C命令测试报文
test_0c_data = bytes.fromhex("4A 58 0C 03 17 67 63 11 36 06 57 01 0C 00 19 01 09 09 37 3B 01 02 01 01 01 01 70")
# 0B命令测试报文
test_0b_data = bytes.fromhex("4A 58 0B 03 17 67 63 11 36 06 57 01 07 00 19 01 09 09 38 00 00 4B")
parser = Command0B0CH()
# 测试解析0C心跳
parser.process_0c_heartbeat(test_0c_data)
# 测试生成0B心跳响应
pile_id_bytes = bytes.fromhex("0317676311360657")
response = parser.generate_0b_heartbeat_response(pile_id_bytes)
print("\n0B心跳响应:")
print(response.hex())
test_heartbeat()

View File

@ -1,9 +1,9 @@
import socket
import logging
import threading
from .utils import ProxyUtils # 使用相对导入
from .mqtt_client import MQTTClient # 使用相对导入
from commands.command_heartbeat import Command0B0CH
from .utils import ProxyUtils
from .mqtt_client import MQTTClient
from commands.command_heartbeat import CommandHeartbeat
from commands.command_02 import Command02
from commands.command_03 import Command03
from commands.command_07 import Command07
@ -15,7 +15,7 @@ from commands.command_30 import Command30
from commands.command_19_1A import Command191A
from commands.command_21_22 import Command2122
from commands.command_23_24 import Command2324
from commands.command_26_27 import Command2627
#from commands.command_26_27 import Command2627
@ -34,7 +34,7 @@ class ChargingPileProxyServer:
self.mqtt_client = MQTTClient()
self.pile_ids = {}
self.utils = ProxyUtils()
self.command_handler = Command0B0CH()
self.command_handler = CommandHeartbeat()
self.command_handler = Command02()
self.command_handler = Command03()
self.command_handler = Command07()
@ -46,7 +46,7 @@ class ChargingPileProxyServer:
self.command_handler = Command191A()
self.command_handler = Command2122()
self.command_handler = Command2324()
self.command_handler = Command2627()
#self.command_handler = Command2627()
# 存储登录信息的字典,以桩号为键
self.login_info = {}
@ -131,52 +131,70 @@ class ChargingPileProxyServer:
if len(data) >= 14 and data[0:2] == b'JX':
command = data[2] # 提取命令字节
# 获取本地和远程端口信息用于日志
source_local, source_remote = self.utils.get_socket_info(source_socket)
dest_local, dest_remote = self.utils.get_socket_info(destination_socket)
# 根据命令字节处理不同命令
if command == 0x01:
logging.info(f"处理 01H 命令,数据内容: {data.hex()}")
self.command_handler.parse_01h(data)
if command == 0x01: # 01H命令
logging.info(f"收到01H连接请求命令: {data.hex().upper()}")
if self.command_handler.process_and_respond(data, destination_socket):
logging.info("01H命令处理完成")
continue # 跳过后续转发
elif command == 0x02:
logging.info(f"处理 02H 命令,数据内容: {data.hex()}")
self.command_handler.parse_02h(data)
elif command == 0x03: # 03H登录命令
logging.info(f"收到03H登录命令: {data.hex().upper()}")
self.command_handler.process_03h(data)
elif command == 0x03:
logging.info(f"处理 03H 命令,数据内容: {data.hex()}")
self.command_handler.parse_03h(data)
elif command == 0x0C: # 0CH桩心跳命令
logging.info(f"收到0CH心跳命令: {data.hex().upper()}")
if self.heartbeat_handler.process_and_respond(data, destination_socket):
logging.info("0CH心跳命令处理完成")
continue # 跳过后续转发
elif command == 0x07:
logging.info(f"处理 07H 命令,数据内容: {data.hex()}")
self.command_handler.parse_07h(data)
elif command == 0x19: # 19H卡鉴权命令
logging.info(f"收到19H卡鉴权命令: {data.hex().upper()}")
if self.card_auth_handler.process_and_respond(data, destination_socket):
logging.info("19H卡鉴权命令处理完成")
continue # 跳过后续转发
elif command == 0x08:
logging.info(f"处理 08H 命令,数据内容: {data.hex()}")
self.command_handler.parse_08h(data)
elif command == 0x09:
logging.info(f"处理 09H 命令,数据内容: {data.hex()}")
self.command_handler.parse_09h(data)
elif command == 0x21: # 21H启动充电结果命令
elif command == 0x0A:
logging.info(f"处理 0AH 命令,数据内容: {data.hex()}")
self.command_handler.parse_0Ah(data)
logging.info(f"收到21H启动充电结果命令: {data.hex()}")
#其他命令待添加...
if self.charge_result_handler.process_and_respond(data, destination_socket):
continue # 跳过后续转发
elif command == 0x23: # 23H充电订单命令
logging.info(f"收到23H充电订单命令: {data.hex().upper()}")
if self.order_handler.process_and_respond(data, destination_socket):
logging.info("23H充电订单命令处理完成")
continue
elif command == 0x25: # 25H充电信息命令
logging.info(f"收到25H充电信息命令: {data.hex()}")
if self.charge_info_handler.process_25h(data):
logging.info("25H命令处理完成")
elif command == 0x30: # 30H BMS信息命令
logging.info(f"收到30H BMS信息命令: {data.hex().upper()}")
if self.bms_handler.process_30h(data):
logging.info("30H BMS信息命令处理完成")
else:
# 未知命令,日志记录
logging.warning(f"未知命令:{command:02X},数据内容: {data.hex()}")
# 未知命令,记录日志
logging.warning(f"未知命令:{command:02X},数据内容: {data.hex().upper()}")
# 将数据转发到远程或充电桩,判断方向
# 如果数据来自客户端连接,提取桩号
if source_socket not in self.remote_connections.values():
# 这里是判断是否是客户端连接进行桩ID提取
pile_id = self.utils.extract_pile_id(data)
if pile_id:
self.pile_ids[source_address] = pile_id
# 获取本地和远程端口信息
source_local, source_remote = self.utils.get_socket_info(source_socket)
dest_local, dest_remote = self.utils.get_socket_info(destination_socket)
# 判断数据发送方向:是发送到远程服务器还是充电桩
is_to_remote = destination_socket in self.remote_connections.values()
direction = "发送到远程服务器" if is_to_remote else "发送到充电桩"
@ -186,25 +204,42 @@ class ChargingPileProxyServer:
destination_socket.send(data)
# 记录数据转发日志
msg = f"数据转发成功: {direction} | 本地地址: {source_local} | 远程地址: {dest_remote} | 数据长度: {len(data)}"
msg = (f"数据转发成功: {direction} | "
f"本地地址: {source_local} | "
f"远程地址: {dest_remote} | "
f"命令: {command:02X}H | "
f"数据长度: {len(data)}")
logging.info(msg)
self.mqtt_client.publish_message(msg)
# 每次数据转发完成后,处理其他相关操作
# 例如检查是否需要断开连接等
# 记录完整的数据内容到调试日志
logging.debug(f"完整数据内容: {data.hex().upper()}")
# 每次数据转发完成后,检查连接状态
if not self.running:
break
except ConnectionResetError:
logging.error(f"连接被重置: {source_remote}")
self.mqtt_client.publish_message(f"连接被重置: {source_remote}")
except socket.timeout:
logging.error(f"连接超时: {source_remote}")
self.mqtt_client.publish_message(f"连接超时: {source_remote}")
except Exception as e:
# 异常处理,日志记录错误信息
logging.error(f"转发数据出错: {str(e)}")
print(f"转发数据出错: {str(e)}")
self.mqtt_client.publish_message(f"转发数据出错: {str(e)}")
# 若需要,可以选择关闭连接
finally:
# 关闭连接
try:
if source_socket:
source_socket.close()
if destination_socket:
destination_socket.close()
except Exception as e:
logging.error(f"关闭连接出错: {str(e)}")
def handle_client(self, client_socket, client_address):
"""处理客户端连接"""