# -*- coding: utf-8 | |
import codecs | |
import struct | |
import socket | |
import threading | |
import time | |
import errno | |
import logging | |
from datetime import datetime | |
def _b(v): | |
if isinstance(v, unicode): | |
return v.encode('lanti-1') | |
return v | |
# 获取设备时钟 | |
CMD_GET_DATETIME = 0xAA | |
# 设置设备时钟 | |
CMD_SET_DATETIME = 0xA9 | |
# 检查黑名单 | |
CMD_CHECK_BLK = 0xBC | |
#下载黑名单 | |
CMD_DOWNLOAD_BLK = 0xA4 | |
#删除所有黑名单 | |
CMD_CLEAR_BLK = 0xA6 | |
#获取收费机主参数 | |
CMD_GET_SYSPARA = 0xBF | |
#下传收费机主参数 | |
CMD_SET_SYSPARA = 0xB7 | |
#采集一条记录 | |
CMD_COLLECT_RECORD = 0x03 | |
#确认采集一条记录 | |
CMD_COLLECT_CONFIRM = 0x04 | |
#补采一条记录 | |
CMD_COLLECT_HISTORY = 0xBB | |
#下传管理员密码 | |
CMD_SET_SYSPSWD = 0x9F | |
#下传允许卡类别 | |
CMD_SET_FEETYPE = 0x67 | |
#上传允许卡类别 | |
CMD_GET_FEETYPE = 0x68 | |
#下传卡类搭伙费/折扣 | |
CMD_SET_FEERATE = 0x73 | |
#上传卡类搭伙费/折扣 | |
CMD_GET_FEERATE = 0x74 | |
#读取脱机工作时间 | |
CMD_GET_OFFLINE = 0x85 | |
#设置脱机工作时间 | |
CMD_SET_OFFLINE = 0x84 | |
#设置工作密钥 | |
CMD_SET_WORKKEY = 0x83 | |
Smart999CRCTable = ( | |
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) | |
def calc_data_crc(data): | |
accnum = 0xc78c | |
for c in data: | |
accnum = (accnum << 8) ^ (Smart999CRCTable[((accnum >> 8) ^ ord(c)) & 0xFF]) | |
return struct.pack('>H', accnum & 0xFFFF) | |
class ProtocolError(BaseException): | |
def __init__(self, msg): | |
super(ProtocolError, self).__init__() | |
self.msg = msg | |
class Smart999Connection(object): | |
def __init__(self, ip, port, debug=1): | |
self.ip = ip | |
self.port = port | |
self.sock = None | |
self.errmsg = "" | |
self.debug = debug | |
self._last_exec_err = 0 | |
def connect(self, timeout=10.0): | |
try: | |
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
sock.settimeout(timeout) | |
sock.connect((self.ip, self.port)) | |
self.sock = sock | |
logging.info(u"连接[%s:%d]成功" % (self.ip, self.port)) | |
return True | |
except socket.error, msg: | |
self.errmsg = unicode(msg) | |
logging.info(u"连接[%s:%d]失败[%s]" % (self.ip, self.port, self.errmsg)) | |
return False | |
def disconnect(self): | |
self._last_exec_err = 0 | |
if self.sock: | |
try: | |
self.sock.close() | |
self.sock = None | |
except: | |
self.sock = None | |
def is_connected(self): | |
return self.sock != None | |
def log(self, data): | |
if not self.debug: | |
return | |
try: | |
print "CMD[%s]" % codecs.encode(data, 'hex') | |
except: | |
pass | |
def send_command(self, request, response, timeout=10.0): | |
if not self.is_connected(): | |
if not self.connect(timeout): | |
logging.error(u"尝试重连设备失败") | |
return False | |
protocol = Smart999Protocol() | |
cmd = protocol.pack(request) | |
self.log(cmd) | |
begin_time = time.time() | |
try: | |
self.sock.settimeout(timeout) | |
self.sock.send(cmd) | |
resp_data = [] | |
while True: | |
data = self.sock.recv(1024) | |
if not data: | |
self.errmsg = u"接收应答超时" | |
return False | |
if not resp_data: | |
if ord(data[0]) != Smart999Protocol.RTX_BEGIN: | |
self.errmsg = u"接收数据包起始符不正确" | |
return False | |
resp_data.append(data) | |
if ord(data[-1]) == Smart999Protocol.RTX_END: | |
break | |
est = time.time() - begin_time | |
if est >= timeout: | |
self.errmsg = u"接收数据未收到结束符" | |
return False | |
self.sock.settimeout(timeout - est) | |
self._last_exec_err = 0 | |
except socket.error, msg: | |
if msg.errno != None and msg.errno != errno.EWOULDBLOCK: | |
self.disconnect() | |
logging.error(u"远程连接[%s]已关闭!" % self.ip) | |
else: | |
self._last_exec_err += 1 | |
if self._last_exec_err > 50: | |
self.disconnect() | |
self.errmsg = unicode(msg) | |
return False | |
except Exception, ex: | |
self.errmsg = unicode(ex) | |
return False | |
full_data = ''.join(resp_data) | |
if ord(full_data[0]) != Smart999Protocol.RTX_BEGIN or \ | |
ord(full_data[-1]) != Smart999Protocol.RTX_END: | |
self.errmsg = u"接收到的数据包不完整" | |
return False | |
self.log(full_data) | |
#protocol.clear_data() | |
try: | |
result = protocol.unpack(full_data) | |
response.update(result) | |
return True | |
except ProtocolError, msg: | |
self.errmsg = msg.msg | |
return False | |
class Smart999Protocol(object): | |
""" | |
三九智慧通讯协议封装 | |
""" | |
VERSION = "1.0" | |
RET_OK = 0 | |
STX_BEGIN = 0xC0 | |
STX_END = 0xC1 | |
RTX_BEGIN = 0xC2 | |
RTX_END = 0xC3 | |
BLACKCARD_ADD = 0 | |
BLACKCARD_DEL = 0xff | |
__protocols = {} | |
__protocol_cache = {} | |
__lock = threading.Lock() | |
ERRDEFINES = {2: u"时钟错误", | |
4: u"数据满", | |
5: u"流水号错", | |
6: u"成功确认", | |
7: u"参数错误", | |
8: u"命令使用错误", | |
9: u"重复操作", | |
10: u"无记录", | |
11: u"注册号错", | |
13: u"申请签到", | |
14: u"黑名单版本过期", | |
17: u"有未上传数据", | |
21: u"不成功的确认", | |
24: u"未授权,无法签到"} | |
@staticmethod | |
def register_protocol(command, clazz): | |
Smart999Protocol.__protocols[command] = clazz | |
@staticmethod | |
def unregister_protocol(command): | |
if command in Smart999Protocol.__protocols: | |
del Smart999Protocol.__protocols[command] | |
def __init__(self): | |
self.clear_data() | |
def add_parent(self, parent_devid, parent_addr, port): | |
""" | |
增加设备级别 | |
parent_devid - 上级设备物理ID | |
parent_addr - 上级设备机号 | |
port - 对应上级设备的地址号 | |
""" | |
self.parents.append((parent_devid, parent_addr, port)) | |
def clear_data(self): | |
self.command = None | |
def __find_protocol(self, command): | |
with Smart999Protocol.__lock: | |
if command in Smart999Protocol.__protocol_cache: | |
return Smart999Protocol.__protocol_cache[command] | |
if command not in Smart999Protocol.__protocols: | |
return None | |
clazz = Smart999Protocol.__protocols[command] | |
cache = dict(timestamp=time.time(), | |
obj=clazz()) | |
Smart999Protocol.__protocol_cache[command] = cache | |
return cache | |
def pack(self, request): | |
""" | |
打包三九通讯协议 | |
request : 请求数据 | |
return : 协议包数据 | |
Exception : ProtocolError | |
""" | |
command = request.get('command') | |
if not command: | |
raise ProtocolError(u"未指定命令字") | |
if isinstance(command, str) or isinstance(command, unicode): | |
command = int(command, 16) & 0xFF | |
proto_obj = self.__find_protocol(command) | |
if not proto_obj: | |
raise ProtocolError(u"不支持的指令,cmd=%02X" % command) | |
data = proto_obj['obj'].prepare(request) | |
if data is None: | |
raise ProtocolError(u"指令打包错误,cmd=%02X" % command) | |
self.command = command | |
return self.__pack_send_cmd(command, request, data) | |
def __pack_send_cmd(self, commmand, request, data): | |
""" | |
打包函数, | |
""" | |
# TODO: 增加 w-lport 级联设备协议打包支持 | |
addr = request.get('addr') | |
if isinstance(addr, str) or isinstance(addr, unicode): | |
addr = int(addr) & 0xFF | |
r = [chr(Smart999Protocol.STX_BEGIN), chr(addr), chr(commmand)] | |
if data: | |
r.append(chr(len(data) % 0xFF)) | |
r.append(data) | |
proto = ''.join(r) | |
r = [proto, calc_data_crc(proto), chr(Smart999Protocol.STX_END)] | |
return ''.join(r) | |
def __unpack_recv_cmd(self, recv_data): | |
""" | |
解包函数 | |
""" | |
# TODO: 增加 w-lport 级联设备协议解包支持 | |
# RTX + Addr + Retcode + Len CRC(2byte) + REX | |
if len(recv_data) < 4 + 3: | |
raise ProtocolError(u"接收数据长度至少6个字节,实际只有%d个字节" % len(recv_data)) | |
crc = calc_data_crc(recv_data[:-3]) | |
if crc != recv_data[-3:-1]: | |
raise ProtocolError(u"验证接收数据CRC错误") | |
addr = ord(recv_data[1]) | |
retcode = ord(recv_data[2]) | |
datalen = ord(recv_data[3]) | |
if len(recv_data[4:-3]) != datalen: | |
raise ProtocolError(u"接收数据长度验证错误") | |
return dict(addr=addr, retcode=retcode, | |
retmsg=self.__get_s9_errmsg(retcode), | |
data=recv_data[4:-3]) | |
def __get_s9_errmsg(self, errcode): | |
if errcode == Smart999Protocol.RET_OK: | |
return u"成功" | |
else: | |
return Smart999Protocol.ERRDEFINES.get(errcode, | |
u"未知错误ret=%d" % errcode) | |
def unpack(self, response): | |
""" | |
解包三九通讯协议包 | |
""" | |
assert self.command != None, u"调用顺序错误" | |
result = self.__unpack_recv_cmd(response) | |
proto_obj = self.__find_protocol(self.command) | |
if not proto_obj: | |
raise ProtocolError(u"不支持的指令,cmd=%02X" % self.command) | |
data = proto_obj['obj'].unprepare(result.get('retcode'), | |
result.get('data')) | |
del result['data'] | |
result.update(data) | |
return result | |
def prepare(self, request): | |
""" | |
返回 None 表示打包失败 | |
返回 空传 表示无数据 | |
""" | |
raise RuntimeError(u"函数 prepare 必须被重载") | |
def unprepare(self, retcode, response): | |
raise RuntimeError(u'函数 unprepare 必须被重载') | |
def encode_devphyid(self, devphyid): | |
return codecs.decode(devphyid, 'hex') | |
def decode_devphyid(self, devphyid): | |
return codecs.encode(devphyid, 'hex') | |
def encode_datetime(self, now): | |
if isinstance(now, str) or isinstance(now, unicode): | |
dt = datetime.strptime('%y%m%d%H%M%S', now) | |
dtstr = now | |
elif isinstance(now, datetime): | |
dt = now | |
dtstr = now.strftime('%y%m%d%H%M%S') | |
else: | |
raise ProtocolError(u"不合法的日期格式") | |
dl = len(dtstr) | |
r = [chr(int(dtstr[i:i + 2]) & 0xFF) for i in range(0, dl, 2)] | |
r.append(chr(dt.weekday() + 1)) | |
return ''.join(r) | |
def decode_datetime(self, datetime): | |
r = ['%02d' % ord(c) for c in datetime] | |
dt = ''.join(r) | |
return '20' + dt[:-2] | |
def _encode_bcd_num(self, num1, num2): | |
return chr(((ord(num1) - 0x30) << 4) | (ord(num2) - 0x30)) | |
def encode_bcd(self, data): | |
assert len(data) % 2 == 0, 'data length must divise by 2' | |
dl = len(data) | |
r = [self._encode_bcd_num(data[i], data[i + 1]) | |
for i in range(0, dl, 2)] | |
return ''.join(r) | |
def decode_bcd(self, data): | |
r = [] | |
for c in data: | |
t1 = chr(((ord(c) & 0xF0) >> 4) + 0x30) | |
t2 = chr((ord(c) & 0x0F) + 0x30) | |
r.append(t1) | |
r.append(t2) | |
return ''.join(r) | |
def encode_cardverno(self, cardverno): | |
return self.encode_bcd(cardverno) | |
def decode_cardverno(self, cardverno): | |
return self.decode_bcd(cardverno) | |
def set_data(self, data): | |
self.data = data | |
def get_int_be(self, data): | |
l = len(data) | |
assert (l <= 4 and l > 0), u"数据长度错误" | |
if l >= 3: | |
if l == 3: | |
data = chr(0) + data | |
fmt = '>I' | |
else: | |
if l == 1: | |
data = chr(0) + data | |
fmt = '>H' | |
return struct.unpack(fmt, data)[0] | |
def get_int_le(self, data): | |
l = len(data) | |
assert (l <= 4 and l > 0), u"数据长度错误" | |
if l >= 3: | |
if l == 3: | |
data = data + chr(0) | |
fmt = '<I' | |
else: | |
if l == 1: | |
data = data + chr(0) | |
fmt = '<H' | |
return struct.unpack(fmt, data)[0] | |
def hex_to_str(self, data): | |
r = [] | |
for c in data: | |
r.append('%02d' % (ord(c) & 0xFF)) | |
return ''.join(r) | |
def set_int_le(self, value, size): | |
assert size >= 1 and size <= 4, u"长度错误" | |
data = struct.pack('<I', value) | |
return data[:size] | |
def set_int_be(self, value, size): | |
assert size >= 1 and size <= 4, u"长度错误" | |
data = struct.pack('>I', value) | |
return data[-size:] | |
class s999_get_datetime(Smart999Protocol): | |
def prepare(self, request): | |
devphyid = request.get('devphyid', None) | |
if not devphyid: | |
raise ProtocolError(u'未定义 devphyid 属性') | |
return self.encode_devphyid(devphyid) | |
def unprepare(self, retcode, response): | |
if retcode != Smart999Protocol.RET_OK: | |
return True | |
if len(response) != 7: | |
raise ProtocolError(u'返回数据长度错误') | |
return dict(datetime=self.decode_datetime(response)) | |
class s999_set_datetime(Smart999Protocol): | |
def prepare(self, request): | |
devphyid = request.get('devphyid') | |
devphyid = self.encode_devphyid(devphyid) | |
dt = request.get('datetime') | |
dt = self.encode_datetime(dt) | |
return struct.pack('<4s7s', devphyid, dt) | |
def unprepare(self, retcode, response): | |
return dict() | |
class s999_download_blk(Smart999Protocol): | |
def prepare(self, request): | |
devphyid = request.get('devphyid') | |
cardverno = request.get('cardverno') | |
r = [self.encode_devphyid(devphyid), | |
self.encode_bcd(cardverno)] | |
blkdata = request.get('blkdata') | |
for flag, cardno in blkdata: | |
r.append(chr(flag)) | |
r.append(self.set_int_be(cardno, 3)) | |
return ''.join(r) | |
def unprepare(self, retcode, response): | |
if retcode != Smart999Protocol.RET_OK: | |
return dict() | |
if len(response) != 6: | |
raise ProtocolError(u"下载黑名单返回数据错误") | |
return dict(cardverno=self.decode_bcd(response)) | |
class s999_set_mainkey(Smart999Protocol): | |
def prepare(self, request): | |
devphyid = request.get('devphyid') | |
mainkey = request.get('workkey') | |
if len(mainkey) != 16: | |
return None | |
devphyid = self.encode_devphyid(devphyid) | |
mainkey = codecs.decode(mainkey, 'hex') | |
r = [devphyid, mainkey] | |
return ''.join(r) | |
def unprepare(self, retcode, response): | |
return dict() | |
class s999_get_syspara(Smart999Protocol): | |
def prepare(self, request): | |
devphyid = request.get('devphyid') | |
return self.encode_devphyid(devphyid) | |
def unprepare(self, retcode, response): | |
if retcode != Smart999Protocol.RET_OK: | |
return dict() | |
fmt = '<4sBBB2s2s3s3s3s3sBBB3s3sB' | |
if struct.calcsize(fmt) != len(response): | |
raise ProtocolError(u"接收数据包内容长度不合法") | |
fields = ('devphyid', 'addr', 'pswd_switch', 'trans_mode', | |
'paycnt_limit', 'rfu1', 'purse_max', 'purse_limit1', | |
'purse_min', 'pay_amount', 'function', 'baud_rate', 'purse_no', | |
'purse_once_limit', 'purse_day_limit', 'card_struct') | |
params = struct.unpack(fmt, response) | |
params = dict(zip(fields, params)) | |
self.__convert_params(params) | |
return dict(params=params) | |
def __convert_params(self, params): | |
params['devphyid'] = self.decode_devphyid(params['devphyid']) | |
params['paycnt_limit'] = self.get_int_be(params['paycnt_limit']) | |
params['purse_max'] = self.get_int_le(params['purse_max']) | |
params['purse_min'] = self.get_int_le(params['purse_min']) | |
params['pay_amount'] = self.get_int_le(params['pay_amount']) | |
params['purse_once_limit'] = self.get_int_le(params['purse_once_limit']) | |
params['purse_day_limit'] = self.get_int_le(params['purse_day_limit']) | |
class s999_set_syspara(Smart999Protocol): | |
def prepare(self, request): | |
#devphyid = request.get('devphyid') | |
return self.__pack_params(request['params']) | |
def unprepare(self, retcode, response): | |
return dict() | |
def __pack_params(self, params): | |
r = [] | |
r.append(self.encode_devphyid(params['devphyid'])) | |
#r.append(chr(params['addr'] & 0xFF)) | |
r.append(chr(params['pswd_switch'] & 0xFF)) | |
r.append(chr(params['trans_mode'] & 0xFF)) | |
r.append(self.set_int_be(params['paycnt_limit'], 2)) | |
r.append(chr(0xFF) * 2) | |
r.append(self.set_int_le(params['purse_max'], 3)) | |
# 单次消费金额与钱包余额一致 | |
r.append(self.set_int_le(params['purse_max'], 3)) | |
r.append(self.set_int_le(params['purse_min'], 3)) | |
r.append(self.set_int_le(params['pay_amount'], 3)) | |
r.append("\x80\x00\x00") | |
r.append(self.set_int_le(params['purse_once_limit'], 3)) | |
r.append(self.set_int_le(params['purse_day_limit'], 3)) | |
r.append('\x64') | |
data = ''.join(r) | |
assert len(data) == 0x20 | |
return data | |
class s999_collect_serial(Smart999Protocol): | |
def prepare(self, request): | |
devphyid = request.get('devphyid') | |
return self.encode_devphyid(devphyid) | |
def unprepare(self, retcode, response): | |
if retcode != Smart999Protocol.RET_OK: | |
return dict() | |
if len(response) != 39: | |
raise ProtocolError(u"记录流水长度错误") | |
fmt = '<6s4s3s3s3s3s2s3s2s3s4sB2s' | |
fields = ('samno', 'devphyid', 'cardno', 'transdate', 'transtime', | |
'cardbefbal', 'paycnt', 'amount', 'posseqno', 'managefee', 'tac', | |
'transmark', 'crc') | |
assert struct.calcsize(fmt) == 39, u"流水结构定义长度不正确" | |
r = struct.unpack(fmt, response) | |
record = dict(zip(fields, r)) | |
self.__convert_record(record) | |
# 验证流水crc, 从交易卡号开始计算的crc | |
crc = calc_data_crc(response[10:-2]) | |
record['checkok'] = crc == response[-2:] | |
return dict(record=record) | |
def __convert_record(self, record): | |
record['samno'] = codecs.encode(record['samno'], 'hex') | |
record['devphyid'] = codecs.encode(record['devphyid'], 'hex').upper() | |
record['cardno'] = self.get_int_be(record['cardno']) | |
record['transdate'] = self.hex_to_str(record['transdate']) | |
record['transtime'] = self.hex_to_str(record['transtime']) | |
record['cardbefbal'] = self.get_int_le(record['cardbefbal']) | |
record['paycnt'] = self.get_int_be(record['paycnt']) | |
record['amount'] = self.get_int_le(record['amount']) | |
record['posseqno'] = self.get_int_be(record['posseqno']) | |
record['managefee'] = self.get_int_le(record['managefee']) | |
record['tac'] = codecs.encode(record['tac'], 'hex') | |
record['crc'] = codecs.encode(record['crc'], 'hex') | |
class s999_collect_his_serial(s999_collect_serial): | |
def prepare(self, request): | |
devphyid = request['devphyid'] | |
serialno = request['serialno'] | |
return ''.join([self.encode_devphyid(devphyid), | |
self.set_int_be(serialno, 2)]) | |
class s999_collect_confirm(Smart999Protocol): | |
def prepare(self, request): | |
devphyid = request.get('devphyid') | |
r = [self.encode_devphyid(devphyid), | |
struct.pack('>H', request.get('posseqno'))] | |
return ''.join(r) | |
def unprepare(self, retcode, response): | |
return dict() | |
class s999_set_feetype(Smart999Protocol): | |
def prepare(self, request): | |
devphyid = request.get('devphyid') | |
bitmap = [chr(0) for i in range(32)] | |
for i, c in enumerate(request.get('feetype')): | |
if c == '1': | |
self.__get_feetype_right(i + 1, bitmap) | |
r = [self.encode_devphyid(devphyid), ''.join(bitmap)] | |
return ''.join(r) | |
def __get_feetype_right(self, feetype, bitmap, flag=True): | |
assert feetype < 256 and feetype >= 0, u"卡类别溢出" | |
byte_offset = 31 - (feetype / 8) | |
byte_data = ord(bitmap[byte_offset]) | |
bit_offset = feetype % 8 | |
if flag: | |
byte_data |= 1 << bit_offset | |
else: | |
byte_data &= ~(1 << bit_offset) | |
bitmap[byte_offset] = chr(byte_data & 0xFF) | |
def unprepare(self, retcode, response): | |
return dict() | |
class s999_get_feetype(Smart999Protocol): | |
def prepare(self, request): | |
devphyid = request.get('devphyid') | |
return self.encode_devphyid(devphyid) | |
def unprepare(self, retcode, response): | |
if retcode != Smart999Protocol.RET_OK: | |
return dict() | |
if len(response) != 32: | |
raise ProtocolError(u"应答数据长度错误") | |
feetype = [] | |
for c in reversed(response): | |
t = ord(c) | |
for i in range(8): | |
if (t & (1 << i)) == 0: | |
feetype.append('0') | |
else: | |
feetype.append('1') | |
return dict(feetype=feetype[1:]) | |
class s999_set_feerate(Smart999Protocol): | |
def prepare(self, request): | |
devphyid = request.get('devphyid') | |
return struct.pack('<4sBB', self.encode_devphyid(devphyid), | |
request.get('feetype'), | |
request.get('fee')) | |
def unprepare(self, retcode, response): | |
return dict() | |
class s999_get_feerate(Smart999Protocol): | |
def prepare(self, request): | |
devphyid = request.get('devphyid') | |
feetype = request.get('feetype') | |
if feetype < 1 or feetype > 255: | |
raise ProtocolError(u"获取卡类型溢出") | |
return ''.join([self.encode_devphyid(devphyid), struct.pack('>B', feetype)]) | |
def unprepare(self, retcode, response): | |
if retcode != Smart999Protocol.RET_OK: | |
return dict() | |
if len(response) != 1: | |
raise ProtocolError(u"应答卡费率数据长度错误") | |
fee = ord(response) | |
if fee == 0xFF: | |
fee = 100 | |
return dict(fee=fee) | |
class s999_clear_blacklist(Smart999Protocol): | |
def prepare(self, request): | |
devphyid = request.get('devphyid') | |
return self.encode_devphyid(devphyid) | |
def unprepare(self, retcode, response): | |
return dict() | |
class s999_set_offline_day(Smart999Protocol): | |
def prepare(self, request): | |
devphyid = request.get('devphyid') | |
offline = request['offline'] | |
return ''.join([self.encode_devphyid(devphyid), struct.pack('<B', offline)]) | |
def unprepare(self, retcode, response): | |
return dict() | |
class s999_get_offline_day(Smart999Protocol): | |
def prepare(self, request): | |
devphyid = request.get('devphyid') | |
return self.encode_devphyid(devphyid) | |
def unprepare(self, retcode, response): | |
if retcode != Smart999Protocol.RET_OK: | |
return dict() | |
if len(response) != 1: | |
raise ProtocolError(u"结束设备脱机工作天数错误") | |
return dict(offline=ord(response)) | |
class s999_check_blk(Smart999Protocol): | |
def prepare(self, request): | |
devphyid = request['devphyid'] | |
cardno = request['cardno'] | |
r = [self.encode_devphyid(devphyid), | |
self.set_int_be(cardno, 3)] | |
return ''.join(r) | |
def unprepare(self, retcode, response): | |
return dict() | |
_initialized = False | |
def register_protocol(): | |
global _initialized | |
if _initialized: | |
return | |
Smart999Protocol.register_protocol(CMD_GET_DATETIME, s999_get_datetime) | |
Smart999Protocol.register_protocol(CMD_SET_DATETIME, s999_set_datetime) | |
Smart999Protocol.register_protocol(CMD_DOWNLOAD_BLK, s999_download_blk) | |
Smart999Protocol.register_protocol(CMD_SET_WORKKEY, s999_set_mainkey) | |
Smart999Protocol.register_protocol(CMD_GET_SYSPARA, s999_get_syspara) | |
Smart999Protocol.register_protocol(CMD_SET_SYSPARA, s999_set_syspara) | |
Smart999Protocol.register_protocol(CMD_COLLECT_RECORD, s999_collect_serial) | |
Smart999Protocol.register_protocol(CMD_COLLECT_CONFIRM, s999_collect_confirm) | |
Smart999Protocol.register_protocol(CMD_COLLECT_HISTORY, s999_collect_his_serial) | |
Smart999Protocol.register_protocol(CMD_SET_FEETYPE, s999_set_feetype) | |
Smart999Protocol.register_protocol(CMD_SET_FEERATE, s999_set_feerate) | |
Smart999Protocol.register_protocol(CMD_GET_FEETYPE, s999_get_feetype) | |
Smart999Protocol.register_protocol(CMD_GET_FEERATE, s999_get_feerate) | |
Smart999Protocol.register_protocol(CMD_CLEAR_BLK, s999_clear_blacklist) | |
Smart999Protocol.register_protocol(CMD_SET_OFFLINE, s999_set_offline_day) | |
Smart999Protocol.register_protocol(CMD_GET_OFFLINE, s999_get_offline_day) | |
Smart999Protocol.register_protocol(CMD_CHECK_BLK, s999_check_blk) | |
_initialized = True |