# -*- coding: utf-8 | |
""" 汇多协议封装 | |
""" | |
import codecs | |
import struct | |
IFT_NULL = 0 | |
IFT_ULONG = 1 | |
IFT_BYTE = 2 | |
IFT_USHORT = 3 | |
IFT_DATETIME = 4 | |
IFT_BUFFER = 5 | |
IFT_STRING = 6 | |
IFT_LONG = 7 | |
# (0,0,1,IFT_BYTE,0,"msgtype","消息码"), | |
FLD_NO_IDX = 0 | |
FLD_LENGTH_IDX = 2 | |
FLD_TYPE_IDX = 3 | |
FLD_FLAG_IDX = 4 | |
FLD_NAME_IDX = 5 | |
FLD_DESC_IDX = 6 | |
HDFieldsDef = [ | |
(1, 0, 0, IFT_NULL, 0, "extend", u"扩展域否"), | |
(2, 0, 4, IFT_ULONG, 0, "terminalsn", u" 终端序列号"), | |
(3, 0, 2, IFT_USHORT, 0, "address", u"CAN地址"), | |
(4, 0, 4, IFT_ULONG, 0, "cardnumber", u"卡号"), | |
(5, 0, 1, IFT_BYTE, 0, "indexofew", u" 钱包索引"), | |
(6, 0, 2, IFT_USHORT, 0, "traceofew", u" 钱包流水号"), | |
(7, 0, 4, IFT_LONG, 0, "amount", u" 交易金额"), | |
(8, 0, 4, IFT_LONG, 0, "additionalamount1", u"附加金额"), | |
(9, 0, 4, IFT_ULONG, 0, "traceofpos", u"POS流水号"), | |
(10, 0, 4, IFT_ULONG, 0, "orgtranstrace", u"原交易流水号"), | |
(11, 0, 7, IFT_DATETIME, 0, "datetime", u"交易日期和时间"), | |
(12, 0, 1, IFT_BYTE, 0, "responsecode", u"响应码"), | |
(13, 0, 2, IFT_USHORT, 0, "terminalid", u"终端号"), | |
(14, 0, 2, IFT_USHORT, 0, "terminaltype", u"终端类型"), | |
(15, 0, 2, IFT_USHORT, 0, "merchantid", u"商户(网点)号"), | |
(16, 0, 4, IFT_ULONG, 0, "operatorid", u"操作员号"), | |
(17, 0, 8, IFT_BUFFER, 0, "pin", u"个人识别码PIN"), | |
(18, 0, 4, IFT_ULONG, 0, "veroflist", u"黑名单版本"), | |
(19, 0, 4, IFT_ULONG, 0, "managefee", u"交易批次号(暂不用)"), | |
(20, 0, 360, IFT_BUFFER, 2, "additionaldata1", u"附加信息1"), | |
(21, 0, 528, IFT_BUFFER, 2, "additionaldata2", u"附加信息2"), | |
(22, 0, 528, IFT_STRING, 2, "additionaldata3", u"附加信息3"), | |
(23, 0, 528, IFT_STRING, 2, "additionaldata4", u"附加信息4"), | |
(24, 0, 2, IFT_USHORT, 0, "mac", u"消息认证码")] | |
crc16_tab = [ | |
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, | |
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, | |
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, | |
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, | |
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, | |
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, | |
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, | |
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, | |
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, | |
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, | |
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, | |
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, | |
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, | |
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, | |
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, | |
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, | |
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, | |
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, | |
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, | |
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, | |
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, | |
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, | |
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, | |
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, | |
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, | |
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, | |
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, | |
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, | |
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, | |
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, | |
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, | |
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0] | |
class HDPack(object): | |
""" 汇多 8583 协议包 """ | |
def __init__(self): | |
super(HDPack, self).__init__() | |
self.msg_type = 0 | |
self.value = {} | |
self.field_idx = {} | |
self.field_name = {} | |
self.msgtype = 0 | |
self.load_config() | |
def _calc_crc(self, data, work_key): | |
""" | |
uint16 crc; | |
unsigned char b, da; | |
uint16 charcnt; | |
crc = 0; | |
charcnt = 0; | |
while (len--) | |
{ | |
da = (unsigned char)(crc / 256); /* 以8位二进制数的形式暂存CRC的高8位 */ | |
crc <<= 8; /* 左移8位,相当于CRC的低8位乘以 */ | |
b = pBuf[charcnt]; // 新移进来的字节值 | |
crc ^= crc16_tab[da ^ b]; /* 高8位和当前字节相加后再查表求CRC ,再加上以前的CRC */ | |
charcnt++; | |
} | |
""" | |
datalen = len(data) | |
charcnt = 0 | |
crc = 0 | |
while datalen > 0: | |
datalen -= 1 | |
t1 = crc / 256 | |
crc <<= 8 | |
t2 = ord(data[charcnt]) | |
crc ^= crc16_tab[(t1 ^ t2) & 0xFF] | |
charcnt += 1 | |
crc = struct.pack('>H', crc & 0xFFFF) | |
return self._encrypt_crc(crc, work_key) | |
def _encrypt_crc(self, crc, workkey): | |
t1 = ord(crc[0]) | |
t2 = ord(crc[1]) | |
for i in range(4): | |
t1 ^= ord(workkey[i * 2]) | |
t2 ^= ord(workkey[i * 2 + 1]) | |
return "".join([chr(t1 & 0xFF), chr(t2 & 0xFF)]) | |
def __eq__(self, other): | |
if not isinstance(other, HDPack): | |
return False | |
return self.compare(other) | |
def __ne__(self, other): | |
if not isinstance(other, HDPack): | |
return True | |
return not self.compare(other) | |
def compare(self, other): | |
if self.msgtype != other.msgtype: | |
return False | |
for k, v in self.value.items(): | |
if k not in other.value: | |
print "field [%s] not equal" % k | |
return False | |
if v != other.get(k): | |
print "field [%s] value not equal [%s,%s]" % (k, v, other.get(k)) | |
return False | |
return True | |
def load_config(self): | |
for field in HDFieldsDef: | |
index, _, _, _, _, name, _ = field | |
self.field_idx[index] = field | |
self.field_name[name] = field | |
#print self.field_name | |
def get_field_def(self, index): | |
field_def = None | |
if isinstance(index, int): | |
if index not in self.field_idx: | |
raise ValueError("field index %d not exists" % index) | |
field_def = self.field_idx[index] | |
elif isinstance(index, str): | |
if index not in self.field_name: | |
raise ValueError("field index %s not exists" % index) | |
field_def = self.field_name[index] | |
else: | |
raise ValueError("field index type not support") | |
return field_def | |
def set(self, index, value): | |
field_def = self.get_field_def(index) | |
t = field_def[FLD_TYPE_IDX] | |
n = field_def[FLD_NAME_IDX] | |
l = field_def[FLD_LENGTH_IDX] | |
f = field_def[FLD_FLAG_IDX] | |
if t == IFT_USHORT or t == IFT_ULONG or t == IFT_NULL or t == IFT_BYTE or t == IFT_LONG: | |
if isinstance(value, int): | |
b = value | |
elif isinstance(value, str) or isinstance(value, unicode): | |
if value.startswith('0x'): | |
b = int(value, 16) | |
else: | |
b = int(value) | |
else: | |
raise ValueError("field %s value type not integer" % n) | |
elif t == IFT_DATETIME: | |
if len(value) != 12 and len(value) != 14: | |
raise ValueError("field %s length must be 12 or 14" % n) | |
if len(value) == 14: | |
value = value[2:] | |
b = value + "00" | |
else: | |
if not isinstance(value, str): | |
if not isinstance(value, unicode): | |
raise ValueError("field %s value type not string" % n) | |
value = value.encode("utf-8") | |
if t == IFT_DATETIME: | |
l *= 2 | |
elif value.startswith('0x'): | |
l *= 2 | |
l += 2 | |
if f != 2 and len(value) != l: | |
raise ValueError("field %s value data length not equal %d<>%d " % | |
(n, len(value), l)) | |
elif len(value) > l: | |
raise ValueError("field %s value data length exceed %d " % (n, l)) | |
b = value | |
self.value[n] = b | |
def get(self, index): | |
field_def = self.get_field_def(index) | |
#t = field_def[3] | |
n = field_def[5] | |
if n in self.value: | |
return self.value[n] | |
raise ValueError("Field %s not specified" % n) | |
def has_field(self, index): | |
field_def = self.get_field_def(index) | |
n = field_def[5] | |
return n in self.value | |
def encode_bitmap(self, bitmap): | |
buf = [] | |
offset = 0 | |
#print "bit len[%d]" % len(bitmap) | |
while offset < len(bitmap): | |
bit = 0 | |
for i in range(8): | |
t = ord(bitmap[offset + i]) - 0x30 | |
if t == 0: continue | |
bit |= (1 << (7 - i % 8)) | |
buf.append(chr(bit & 0xFF)) | |
offset += 8 | |
return "".join(buf) | |
def pack(self, workkey=None): | |
buf = [] | |
bitmap = ['0' for i in range(24)] | |
for field in HDFieldsDef: | |
index, x, fl, t, flag, name, desc = field | |
if name not in self.value: | |
continue | |
if t == IFT_USHORT or t == IFT_ULONG or t == IFT_NULL or t == IFT_BYTE or t == IFT_LONG: | |
buf.append(self.int_2_buffer(self.value[name], t, fl)) | |
else: | |
buf.append(self.string_2_buffer(self.value[name], t, fl, flag)) | |
bitmap[index - 1] = '1' | |
if workkey: | |
bitmap[23] = '1' | |
#print bitmap | |
bitmap_buffer = self.encode_bitmap(bitmap) | |
full_data = chr(self.msgtype & 0xFF) + bitmap_buffer + "".join(buf) | |
if workkey: | |
crc = self._calc_crc(full_data, workkey) | |
full_data += crc | |
header = self.data_header(full_data) | |
#print "h[%s]d[%s]" % (codecs.encode(header,'hex'),codecs.encode(full_data,'hex')) | |
return header + full_data | |
def data_header(self, buf): | |
if len(buf) > 0xFFFF: | |
raise ValueError("Buffer max length exceed %d", len(buf)) | |
header = struct.pack('<H', len(buf)) | |
return header | |
def int_2_buffer(self, value, datatype, length): | |
buf = '' | |
if datatype == IFT_BYTE: | |
buf = chr(value % 0x100) | |
elif datatype == IFT_NULL: | |
return buf | |
elif datatype == IFT_USHORT: | |
v = value % 0x10000 | |
buf = struct.pack('<H', v) | |
elif datatype == IFT_ULONG: | |
buf = struct.pack('<I', value) | |
elif datatype == IFT_LONG: | |
buf = struct.pack('<i', value) | |
else: | |
raise ValueError('Input Error') | |
return buf | |
def encode_bcd(self, value): | |
if len(value) % 2 != 0: | |
raise ValueError("value length must div 2") | |
i = 0 | |
#print "value [%s]" % value | |
buf = [] | |
while i < len(value): | |
t1 = (ord(value[i]) - 0x30) & 0xFF | |
t2 = (ord(value[i + 1]) - 0x30) & 0xFF | |
#print "t1[%d]t2[%d]" % (t1,t2) | |
t = ((t1 << 4) | t2) & 0xFF | |
#print "t1[%d]t2[%d]t[%d]" % (t1,t2,t) | |
buf.append(chr(t)) | |
i += 2 | |
#print "bcd value[%s]" % codecs.encode(buf,'hex') | |
return "".join(buf) | |
def decode_bcd(self, value): | |
buf = [] | |
#print "bcd [%s]" % codecs.encode(value,'hex') | |
for c in value: | |
t = ord(c) & 0xFF | |
t1 = (t >> 4) & 0x0F | |
t2 = t & 0x0F | |
#print "t[%02x]t1[%d]t2[%d]" % (t,t1,t2) | |
buf.append(chr(t1 + 0x30) + chr(t2 + 0x30)) | |
#print "bcd value[%s]" % buf | |
return "".join(buf) | |
def string_2_buffer(self, value, datatype, length, flag): | |
buf = '' | |
if datatype == IFT_STRING or datatype == IFT_BUFFER: | |
if value.startswith('0x'): | |
buf = codecs.decode(value[2:], 'hex') | |
else: | |
buf = value | |
elif datatype == IFT_DATETIME: | |
buf = '' | |
i = 0 | |
while i < len(value): | |
t = int(value[i:i + 2]) | |
buf += chr(t & 0xFF) | |
i += 2 | |
else: | |
raise ValueError('Input Error') | |
if flag == 2: | |
header = struct.pack('<H', len(buf) % 0x10000) | |
return header + buf | |
else: | |
#if type == IFT_BUFFER: | |
# if len(buffer) % 2 <> 0 or len(buffer)/2 <> length: | |
# raise ValueError("Value length not matched [%s]" % value) | |
# buffer = codecs.decode(buffer,'hex') | |
if len(buf) != length: | |
padlen = length - len(buf) | |
if padlen < 0: | |
raise ValueError("Value length not matched [%s]" % value) | |
pad = chr(0) * padlen | |
buf = pad + buf | |
return buf | |
def decode_bitmap(self, bitmap): | |
if len(bitmap) != 3: | |
raise ValueError("bitmap length error") | |
#print "bitmap[%s]" % codecs.encode(bitmap,'hex') | |
buf = [] | |
for t in bitmap: | |
v = ord(t) & 0xFF | |
for i in range(8): | |
x = (v & (1 << (7 - i))) | |
if x > 0: | |
buf.append('1') | |
else: | |
buf.append('0') | |
return buf | |
def unpack(self, data): | |
self.value = {} | |
data_len = struct.unpack('<H', data[:2])[0] | |
if len(data) != data_len + 2: | |
print "input[%d] extually[%d]" % (data_len, len(data)) | |
return False | |
offset = 2 | |
self.msgtype = ord(data[offset]) & 0xFF | |
offset += 1 | |
bitmap = self.decode_bitmap(data[offset:offset + 3]) | |
offset += 3 | |
#print bitmap | |
for i in range(len(bitmap)): | |
if bitmap[i] == '0': | |
continue | |
findex = i + 1 | |
field = self.get_field_def(findex) | |
ftype = field[FLD_TYPE_IDX] | |
flen = field[FLD_LENGTH_IDX] | |
fflag = field[FLD_FLAG_IDX] | |
#fname = field[FLD_NAME_IDX] | |
#print "parse field[%s] [%d]off[%d][%s]" % (fname,fflag,offset,codecs.encode(data[offset:offset+2],'hex')) | |
# 变长数据 | |
if fflag == 2: | |
#print "len [%s]" % codecs.encode(data[offset:offset+2],'hex') | |
endpos = self.buffer_2_int(data[offset:offset + 2], IFT_USHORT) | |
offset += 2 | |
endpos += offset | |
else: | |
endpos = offset + flen | |
if ftype == IFT_USHORT or ftype == IFT_ULONG or ftype == IFT_NULL or ftype == IFT_BYTE or ftype == IFT_LONG: | |
value = self.buffer_2_int(data[offset:endpos], ftype) | |
else: | |
value = self.buffer_2_string(data[offset:endpos], ftype) | |
self.set(findex, value) | |
#print "parse field[%s] OK =====" % fname | |
offset = endpos | |
#print self.value | |
return True | |
def buffer_2_int(self, data, datatype): | |
if datatype == IFT_BYTE: | |
return ord(data) | |
elif datatype == IFT_USHORT: | |
return struct.unpack('<H', data)[0] | |
elif datatype == IFT_ULONG: | |
return struct.unpack('<I', data)[0] | |
elif datatype == IFT_LONG: | |
return struct.unpack('<i', data)[0] | |
elif datatype == IFT_NULL: | |
return None | |
else: | |
raise ValueError("input data type error") | |
def buffer_2_string(self, data, type): | |
if type == IFT_STRING: | |
return data | |
elif type == IFT_BUFFER: | |
return data | |
elif type == IFT_DATETIME: | |
return self.decode_bcd(data) | |
else: | |
raise ValueError("input data type error") | |
def get_2byte_int(self, data): | |
return struct.unpack("<H", data)[0] | |
def get_3byte_int(self, data): | |
temp = data + chr(0) | |
return struct.unpack("<I", temp)[0] | |
def get_4byte_int(self, data): | |
return struct.unpack("<I", data)[0] | |
def get_byte_int(self, data, signed=False): | |
dl = len(data) | |
if dl <= 0 or dl > 4: | |
raise ValueError(u"Data length must between 1 and 4") | |
fmt = None | |
if dl == 2: | |
fmt = '<h' if signed else '<H' | |
elif dl == 4: | |
fmt = '<i' if signed else '<I' | |
elif dl == 1: | |
fmt = '<b' if signed else '<B' | |
else: | |
fmt = '<i' if signed else '<I' | |
data += chr(0) | |
return struct.unpack(fmt, data)[0] | |
def set_2byte_int(self, data): | |
return struct.pack('<H', data) | |
def set_3byte_int(self, data): | |
temp = struct.pack('<H', data) | |
return temp[:-1] | |
def set_4byte_int(self, data): | |
return struct.pack('<I', data) | |
def test(): | |
pack = HDPack() | |
pack.msgtype = 0xFF | |
pack.set(2, "0000000000") | |
print codecs.encode(pack.pack(), 'hex') | |
data = pack.pack(chr(0xFF) * 8) | |
print codecs.encode(data, 'hex') | |
if __name__ == "__main__": | |
test() |