增加三九新版POS协议封装
diff --git a/supwisdom/protocol/smart999protocol.py b/supwisdom/protocol/smart999protocol.py
new file mode 100644
index 0000000..176b7a1
--- /dev/null
+++ b/supwisdom/protocol/smart999protocol.py
@@ -0,0 +1,796 @@
+# -*- 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):

+        self.ip = ip

+        self.port = port

+        self.sock = None

+        self.errmsg = None

+

+    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

+            return True

+        except socket.error, msg:

+            self.errmsg = unicode(msg)

+            return False

+

+    def disconnect(self):

+        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):

+        try:

+            print "CMD[%s]" % codecs.encode(data, 'hex')

+        except:

+            pass

+

+    def send_command(self, request, response, timeout=10.0):

+        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)

+        except socket.error, msg:

+            if msg.errno == errno.ECONNABORTED:

+                self.sock.close()

+                self.sock = None

+                logging.error(u"远程连接[%s]已关闭!" % self.ip)

+            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