185 lines
6.0 KiB
Python
185 lines
6.0 KiB
Python
import struct
|
||
import logging
|
||
import binascii
|
||
|
||
|
||
class Command191A:
|
||
def __init__(self):
|
||
self.command_19 = 0x19 # 卡鉴权上报命令
|
||
self.command_1a = 0x1A # 平台回复卡鉴权命令
|
||
|
||
def parse_19_card_auth(self, data):
|
||
"""
|
||
解析19H卡鉴权上报命令
|
||
|
||
:param data: 完整的19H命令报文
|
||
:return: 解析后的字典或None
|
||
"""
|
||
try:
|
||
# 验证基本帧格式
|
||
if len(data) < 14 or data[0:2] != b'JX' or data[2] != 0x19:
|
||
logging.warning(f"19H命令帧格式不正确,原始报文: {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}"
|
||
|
||
# 提取卡号(通常是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')
|
||
|
||
# 打印解析结果
|
||
print("\n19H卡鉴权上报命令解析结果:")
|
||
print(f"桩号: {pile_id_bytes.hex()}")
|
||
print(f"时间标识: {timestamp}")
|
||
print(f"卡号: {card_number}")
|
||
|
||
return {
|
||
"pile_id": pile_id_bytes.hex(),
|
||
"timestamp": timestamp,
|
||
"card_number": card_number
|
||
}
|
||
|
||
except Exception as e:
|
||
logging.error(f"解析19H命令失败: {str(e)}")
|
||
logging.error(f"原始报文: {binascii.hexlify(data)}")
|
||
return None
|
||
|
||
def generate_1a_card_auth_response(self, card_number):
|
||
"""
|
||
生成1AH卡鉴权响应命令
|
||
|
||
:param card_number: 卡号
|
||
:return: 1AH响应报文
|
||
"""
|
||
try:
|
||
# 构建帧
|
||
frame = bytearray()
|
||
frame.extend(b'JX') # 帧起始标志
|
||
frame.append(self.command_1a) # 命令码
|
||
frame.extend(bytes.fromhex('0317665611360637')) # 桩号(固定值)
|
||
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))
|
||
|
||
# 卡号(16字节ASCII,不足补0)
|
||
card_number_bytes = card_number.ljust(16, '\x00').encode('ascii')
|
||
data.extend(card_number_bytes)
|
||
|
||
# 卡余额(假设为0)
|
||
data.extend(struct.pack("<I", 0)) # 4字节,分辨率0.01元
|
||
|
||
# 允许充电标志(1-可充电;2-禁止充电)
|
||
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)
|
||
|
||
# 计算校验码
|
||
check = 0
|
||
for b in frame[2:]:
|
||
check ^= b
|
||
frame.append(check)
|
||
|
||
print("1AH卡鉴权响应数据构建成功:")
|
||
print(f"数据内容: {frame.hex()}")
|
||
print(f"数据长度: {len(frame)}字节")
|
||
|
||
return bytes(frame)
|
||
|
||
except Exception as e:
|
||
logging.error(f"生成1AH卡鉴权响应出错: {str(e)}")
|
||
return None
|
||
|
||
def process_19_card_auth(self, data):
|
||
"""
|
||
处理19H卡鉴权上报命令
|
||
|
||
:param data: 完整的19H命令报文
|
||
:return: 是否成功处理
|
||
"""
|
||
try:
|
||
parsed_data = self.parse_19_card_auth(data)
|
||
|
||
if parsed_data is None:
|
||
logging.warning("19H命令解析失败")
|
||
return False
|
||
|
||
# 记录卡鉴权信息日志
|
||
logging.info(f"收到桩号 {parsed_data['pile_id']} 的卡鉴权请求,卡号 {parsed_data['card_number']}")
|
||
|
||
return True
|
||
|
||
except Exception as e:
|
||
logging.error(f"处理19H命令出错: {str(e)}")
|
||
return False
|
||
|
||
|
||
# 测试用例
|
||
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()) |