Tang Cheng | 5d8cf50 | 2012-11-05 14:27:40 +0800 | [diff] [blame] | 1 | # -*- coding: utf-8
|
| 2 |
|
| 3 | import codecs
|
| 4 | import struct
|
| 5 | import socket
|
| 6 | import threading
|
| 7 | import time
|
| 8 | import errno
|
| 9 | import logging
|
| 10 | from datetime import datetime
|
| 11 |
|
| 12 |
|
| 13 | def _b(v):
|
| 14 | if isinstance(v, unicode):
|
| 15 | return v.encode('lanti-1')
|
| 16 | return v
|
| 17 |
|
| 18 |
|
| 19 | # 获取设备时钟
|
| 20 | CMD_GET_DATETIME = 0xAA
|
| 21 | # 设置设备时钟
|
| 22 | CMD_SET_DATETIME = 0xA9
|
| 23 | # 检查黑名单
|
| 24 | CMD_CHECK_BLK = 0xBC
|
| 25 | #下载黑名单
|
| 26 | CMD_DOWNLOAD_BLK = 0xA4
|
| 27 | #删除所有黑名单
|
| 28 | CMD_CLEAR_BLK = 0xA6
|
| 29 | #获取收费机主参数
|
| 30 | CMD_GET_SYSPARA = 0xBF
|
| 31 | #下传收费机主参数
|
| 32 | CMD_SET_SYSPARA = 0xB7
|
| 33 | #采集一条记录
|
| 34 | CMD_COLLECT_RECORD = 0x03
|
| 35 | #确认采集一条记录
|
| 36 | CMD_COLLECT_CONFIRM = 0x04
|
| 37 | #补采一条记录
|
| 38 | CMD_COLLECT_HISTORY = 0xBB
|
| 39 | #下传管理员密码
|
| 40 | CMD_SET_SYSPSWD = 0x9F
|
| 41 | #下传允许卡类别
|
| 42 | CMD_SET_FEETYPE = 0x67
|
| 43 | #上传允许卡类别
|
| 44 | CMD_GET_FEETYPE = 0x68
|
| 45 | #下传卡类搭伙费/折扣
|
| 46 | CMD_SET_FEERATE = 0x73
|
| 47 | #上传卡类搭伙费/折扣
|
| 48 | CMD_GET_FEERATE = 0x74
|
| 49 | #读取脱机工作时间
|
| 50 | CMD_GET_OFFLINE = 0x85
|
| 51 | #设置脱机工作时间
|
| 52 | CMD_SET_OFFLINE = 0x84
|
| 53 | #设置工作密钥
|
| 54 | CMD_SET_WORKKEY = 0x83
|
| 55 |
|
| 56 |
|
| 57 | Smart999CRCTable = (
|
| 58 | 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
|
| 59 | 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
|
| 60 | 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
|
| 61 | 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
|
| 62 | 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
|
| 63 | 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
|
| 64 | 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
|
| 65 | 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
|
| 66 | 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
|
| 67 | 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
|
| 68 | 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
|
| 69 | 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
|
| 70 | 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
|
| 71 | 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
|
| 72 | 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
|
| 73 | 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
|
| 74 | 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
|
| 75 | 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
|
| 76 | 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
|
| 77 | 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
|
| 78 | 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
|
| 79 | 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
|
| 80 | 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
|
| 81 | 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
|
| 82 | 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
|
| 83 | 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
|
| 84 | 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
|
| 85 | 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
|
| 86 | 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
|
| 87 | 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
|
| 88 | 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
|
| 89 | 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0)
|
| 90 |
|
| 91 |
|
| 92 | def calc_data_crc(data):
|
| 93 | accnum = 0xc78c
|
| 94 | for c in data:
|
| 95 | accnum = (accnum << 8) ^ (Smart999CRCTable[((accnum >> 8) ^ ord(c)) & 0xFF])
|
| 96 | return struct.pack('>H', accnum & 0xFFFF)
|
| 97 |
|
| 98 |
|
| 99 | class ProtocolError(BaseException):
|
| 100 | def __init__(self, msg):
|
| 101 | super(ProtocolError,self).__init__()
|
| 102 | self.msg = msg
|
| 103 |
|
| 104 |
|
| 105 | class Smart999Connection(object):
|
| 106 | def __init__(self, ip, port):
|
| 107 | self.ip = ip
|
| 108 | self.port = port
|
| 109 | self.sock = None
|
| 110 | self.errmsg = None
|
| 111 |
|
| 112 | def connect(self, timeout=10.0):
|
| 113 | try:
|
| 114 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
| 115 | sock.settimeout(timeout)
|
| 116 | sock.connect((self.ip, self.port))
|
| 117 | self.sock = sock
|
| 118 | return True
|
| 119 | except socket.error, msg:
|
| 120 | self.errmsg = unicode(msg)
|
| 121 | return False
|
| 122 |
|
| 123 | def disconnect(self):
|
| 124 | if self.sock:
|
| 125 | try:
|
| 126 | self.sock.close()
|
| 127 | self.sock = None
|
| 128 | except:
|
| 129 | self.sock = None
|
| 130 |
|
| 131 | def is_connected(self):
|
| 132 | return self.sock != None
|
| 133 |
|
| 134 | def log(self, data):
|
| 135 | try:
|
| 136 | print "CMD[%s]" % codecs.encode(data, 'hex')
|
| 137 | except:
|
| 138 | pass
|
| 139 |
|
| 140 | def send_command(self, request, response, timeout=10.0):
|
| 141 | protocol = Smart999Protocol()
|
| 142 | cmd = protocol.pack(request)
|
| 143 | self.log(cmd)
|
| 144 | begin_time = time.time()
|
| 145 | try:
|
| 146 | self.sock.settimeout(timeout)
|
| 147 | self.sock.send(cmd)
|
| 148 | resp_data = []
|
| 149 | while True:
|
| 150 | data = self.sock.recv(1024)
|
| 151 | if not data:
|
| 152 | self.errmsg = u"接收应答超时"
|
| 153 | return False
|
| 154 |
|
| 155 | if not resp_data:
|
| 156 | if ord(data[0]) != Smart999Protocol.RTX_BEGIN:
|
| 157 | self.errmsg = u"接收数据包起始符不正确"
|
| 158 | return False
|
| 159 |
|
| 160 | resp_data.append(data)
|
| 161 | if ord(data[-1]) == Smart999Protocol.RTX_END:
|
| 162 | break
|
| 163 | est = time.time() - begin_time
|
| 164 | if est >= timeout:
|
| 165 | self.errmsg = u"接收数据未收到结束符"
|
| 166 | return False
|
| 167 | self.sock.settimeout(timeout - est)
|
| 168 | except socket.error, msg:
|
| 169 | if msg.errno == errno.ECONNABORTED:
|
| 170 | self.sock.close()
|
| 171 | self.sock = None
|
| 172 | logging.error(u"远程连接[%s]已关闭!" % self.ip)
|
| 173 | self.errmsg = unicode(msg)
|
| 174 | return False
|
| 175 | except Exception, ex:
|
| 176 | self.errmsg = unicode(ex)
|
| 177 | return False
|
| 178 |
|
| 179 | full_data = ''.join(resp_data)
|
| 180 | if ord(full_data[0]) != Smart999Protocol.RTX_BEGIN or \
|
| 181 | ord(full_data[-1]) != Smart999Protocol.RTX_END:
|
| 182 | self.errmsg = u"接收到的数据包不完整"
|
| 183 | return False
|
| 184 |
|
| 185 | self.log(full_data)
|
| 186 | #protocol.clear_data()
|
| 187 | try:
|
| 188 | result = protocol.unpack(full_data)
|
| 189 | response.update(result)
|
| 190 | return True
|
| 191 | except ProtocolError, msg:
|
| 192 | self.errmsg = msg.msg
|
| 193 | return False
|
| 194 |
|
| 195 |
|
| 196 | class Smart999Protocol(object):
|
| 197 | """
|
| 198 | 三九智慧通讯协议封装
|
| 199 | """
|
| 200 | VERSION = "1.0"
|
| 201 | RET_OK = 0
|
| 202 |
|
| 203 | STX_BEGIN = 0xC0
|
| 204 | STX_END = 0xC1
|
| 205 |
|
| 206 | RTX_BEGIN = 0xC2
|
| 207 | RTX_END = 0xC3
|
| 208 |
|
| 209 | BLACKCARD_ADD = 0
|
| 210 | BLACKCARD_DEL = 0xff
|
| 211 |
|
| 212 | __protocols = {}
|
| 213 |
|
| 214 | __protocol_cache = {}
|
| 215 |
|
| 216 | __lock = threading.Lock()
|
| 217 |
|
| 218 | ERRDEFINES = {2: u"时钟错误",
|
| 219 | 4: u"数据满",
|
| 220 | 5: u"流水号错",
|
| 221 | 6: u"成功确认",
|
| 222 | 7: u"参数错误",
|
| 223 | 8: u"命令使用错误",
|
| 224 | 9: u"重复操作",
|
| 225 | 10: u"无记录",
|
| 226 | 11: u"注册号错",
|
| 227 | 13: u"申请签到",
|
| 228 | 14: u"黑名单版本过期",
|
| 229 | 17: u"有未上传数据",
|
| 230 | 21: u"不成功的确认",
|
| 231 | 24: u"未授权,无法签到"}
|
| 232 |
|
| 233 | @staticmethod
|
| 234 | def register_protocol(command, clazz):
|
| 235 | Smart999Protocol.__protocols[command] = clazz
|
| 236 |
|
| 237 | @staticmethod
|
| 238 | def unregister_protocol(command):
|
| 239 | if command in Smart999Protocol.__protocols:
|
| 240 | del Smart999Protocol.__protocols[command]
|
| 241 |
|
| 242 | def __init__(self):
|
| 243 | self.clear_data()
|
| 244 |
|
| 245 | def add_parent(self, parent_devid, parent_addr, port):
|
| 246 | """
|
| 247 | 增加设备级别
|
| 248 | parent_devid - 上级设备物理ID
|
| 249 | parent_addr - 上级设备机号
|
| 250 | port - 对应上级设备的地址号
|
| 251 | """
|
| 252 | self.parents.append((parent_devid, parent_addr, port))
|
| 253 |
|
| 254 | def clear_data(self):
|
| 255 | self.command = None
|
| 256 |
|
| 257 | def __find_protocol(self, command):
|
| 258 | with Smart999Protocol.__lock:
|
| 259 | if command in Smart999Protocol.__protocol_cache:
|
| 260 | return Smart999Protocol.__protocol_cache[command]
|
| 261 |
|
| 262 | if command not in Smart999Protocol.__protocols:
|
| 263 | return None
|
| 264 | clazz = Smart999Protocol.__protocols[command]
|
| 265 | cache = dict(timestamp=time.time(),
|
| 266 | obj=clazz())
|
| 267 | Smart999Protocol.__protocol_cache[command] = cache
|
| 268 | return cache
|
| 269 |
|
| 270 | def pack(self, request):
|
| 271 | """
|
| 272 | 打包三九通讯协议
|
| 273 | request : 请求数据
|
| 274 | return : 协议包数据
|
| 275 | Exception : ProtocolError
|
| 276 | """
|
| 277 | command = request.get('command')
|
| 278 | if not command:
|
| 279 | raise ProtocolError(u"未指定命令字")
|
| 280 | if isinstance(command, str) or isinstance(command, unicode):
|
| 281 | command = int(command, 16) & 0xFF
|
| 282 | proto_obj = self.__find_protocol(command)
|
| 283 | if not proto_obj:
|
| 284 | raise ProtocolError(u"不支持的指令,cmd=%02X" % command)
|
| 285 | data = proto_obj['obj'].prepare(request)
|
| 286 | if data is None:
|
| 287 | raise ProtocolError(u"指令打包错误,cmd=%02X" % command)
|
| 288 | self.command = command
|
| 289 | return self.__pack_send_cmd(command, request, data)
|
| 290 |
|
| 291 | def __pack_send_cmd(self, commmand, request, data):
|
| 292 | """
|
| 293 | 打包函数,
|
| 294 | """
|
| 295 | # TODO: 增加 w-lport 级联设备协议打包支持
|
| 296 | addr = request.get('addr')
|
| 297 | if isinstance(addr, str) or isinstance(addr, unicode):
|
| 298 | addr = int(addr) & 0xFF
|
| 299 |
|
| 300 | r = [chr(Smart999Protocol.STX_BEGIN), chr(addr), chr(commmand)]
|
| 301 | if data:
|
| 302 | r.append(chr(len(data) % 0xFF))
|
| 303 | r.append(data)
|
| 304 | proto = ''.join(r)
|
| 305 | r = [proto, calc_data_crc(proto), chr(Smart999Protocol.STX_END)]
|
| 306 | return ''.join(r)
|
| 307 |
|
| 308 | def __unpack_recv_cmd(self, recv_data):
|
| 309 | """
|
| 310 | 解包函数
|
| 311 | """
|
| 312 | # TODO: 增加 w-lport 级联设备协议解包支持
|
| 313 | # RTX + Addr + Retcode + Len CRC(2byte) + REX
|
| 314 | if len(recv_data) < 4 + 3:
|
| 315 | raise ProtocolError(u"接收数据长度至少6个字节,实际只有%d个字节" % len(recv_data))
|
| 316 |
|
| 317 | crc = calc_data_crc(recv_data[:-3])
|
| 318 | if crc != recv_data[-3:-1]:
|
| 319 | raise ProtocolError(u"验证接收数据CRC错误")
|
| 320 | addr = ord(recv_data[1])
|
| 321 | retcode = ord(recv_data[2])
|
| 322 | datalen = ord(recv_data[3])
|
| 323 | if len(recv_data[4:-3]) != datalen:
|
| 324 | raise ProtocolError(u"接收数据长度验证错误")
|
| 325 |
|
| 326 | return dict(addr=addr, retcode=retcode,
|
| 327 | retmsg=self.__get_s9_errmsg(retcode),
|
| 328 | data=recv_data[4:-3])
|
| 329 |
|
| 330 | def __get_s9_errmsg(self, errcode):
|
| 331 | if errcode == Smart999Protocol.RET_OK:
|
| 332 | return u"成功"
|
| 333 | else:
|
| 334 | return Smart999Protocol.ERRDEFINES.get(errcode,
|
| 335 | u"未知错误ret=%d" % errcode)
|
| 336 |
|
| 337 | def unpack(self, response):
|
| 338 | """
|
| 339 | 解包三九通讯协议包
|
| 340 | """
|
| 341 | assert self.command != None, u"调用顺序错误"
|
| 342 | result = self.__unpack_recv_cmd(response)
|
| 343 | proto_obj = self.__find_protocol(self.command)
|
| 344 | if not proto_obj:
|
| 345 | raise ProtocolError(u"不支持的指令,cmd=%02X" % self.command)
|
| 346 | data = proto_obj['obj'].unprepare(result.get('retcode'),
|
| 347 | result.get('data'))
|
| 348 | del result['data']
|
| 349 | result.update(data)
|
| 350 | return result
|
| 351 |
|
| 352 | def prepare(self, request):
|
| 353 | """
|
| 354 | 返回 None 表示打包失败
|
| 355 | 返回 空传 表示无数据
|
| 356 | """
|
| 357 | raise RuntimeError(u"函数 prepare 必须被重载")
|
| 358 |
|
| 359 | def unprepare(self, retcode, response):
|
| 360 | raise RuntimeError(u'函数 unprepare 必须被重载')
|
| 361 |
|
| 362 | def encode_devphyid(self, devphyid):
|
| 363 | return codecs.decode(devphyid, 'hex')
|
| 364 |
|
| 365 | def decode_devphyid(self, devphyid):
|
| 366 | return codecs.encode(devphyid, 'hex')
|
| 367 |
|
| 368 | def encode_datetime(self, now):
|
| 369 | if isinstance(now, str) or isinstance(now, unicode):
|
| 370 | dt = datetime.strptime('%y%m%d%H%M%S', now)
|
| 371 | dtstr = now
|
| 372 | elif isinstance(now, datetime):
|
| 373 | dt = now
|
| 374 | dtstr = now.strftime('%y%m%d%H%M%S')
|
| 375 | else:
|
| 376 | raise ProtocolError(u"不合法的日期格式")
|
| 377 |
|
| 378 | dl = len(dtstr)
|
| 379 | r = [chr(int(dtstr[i:i + 2]) & 0xFF) for i in range(0, dl, 2)]
|
| 380 |
|
| 381 | r.append(chr(dt.weekday() + 1))
|
| 382 | return ''.join(r)
|
| 383 |
|
| 384 | def decode_datetime(self, datetime):
|
| 385 | r = ['%02d' % ord(c) for c in datetime]
|
| 386 | dt = ''.join(r)
|
| 387 | return '20' + dt[:-2]
|
| 388 |
|
| 389 | def _encode_bcd_num(self, num1, num2):
|
| 390 | return chr(((ord(num1) - 0x30) << 4) | (ord(num2) - 0x30))
|
| 391 |
|
| 392 | def encode_bcd(self, data):
|
| 393 | assert len(data) % 2 == 0, 'data length must divise by 2'
|
| 394 | dl = len(data)
|
| 395 | r = [self._encode_bcd_num(data[i], data[i + 1])
|
| 396 | for i in range(0, dl, 2)]
|
| 397 | return ''.join(r)
|
| 398 |
|
| 399 | def decode_bcd(self, data):
|
| 400 | r = []
|
| 401 | for c in data:
|
| 402 | t1 = chr(((ord(c) & 0xF0) >> 4) + 0x30)
|
| 403 | t2 = chr((ord(c) & 0x0F) + 0x30)
|
| 404 | r.append(t1)
|
| 405 | r.append(t2)
|
| 406 | return ''.join(r)
|
| 407 |
|
| 408 | def encode_cardverno(self, cardverno):
|
| 409 | return self.encode_bcd(cardverno)
|
| 410 |
|
| 411 | def decode_cardverno(self, cardverno):
|
| 412 | return self.decode_bcd(cardverno)
|
| 413 |
|
| 414 | def set_data(self, data):
|
| 415 | self.data = data
|
| 416 |
|
| 417 | def get_int_be(self, data):
|
| 418 | l = len(data)
|
| 419 | assert (l <= 4 and l > 0), u"数据长度错误"
|
| 420 | if l >= 3:
|
| 421 | if l == 3:
|
| 422 | data = chr(0) + data
|
| 423 | fmt = '>I'
|
| 424 | else:
|
| 425 | if l == 1:
|
| 426 | data = chr(0) + data
|
| 427 | fmt = '>H'
|
| 428 | return struct.unpack(fmt, data)[0]
|
| 429 |
|
| 430 | def get_int_le(self, data):
|
| 431 | l = len(data)
|
| 432 | assert (l <= 4 and l > 0), u"数据长度错误"
|
| 433 | if l >= 3:
|
| 434 | if l == 3:
|
| 435 | data = data + chr(0)
|
| 436 | fmt = '<I'
|
| 437 | else:
|
| 438 | if l == 1:
|
| 439 | data = data + chr(0)
|
| 440 | fmt = '<H'
|
| 441 | return struct.unpack(fmt, data)[0]
|
| 442 |
|
| 443 | def hex_to_str(self, data):
|
| 444 | r = []
|
| 445 | for c in data:
|
| 446 | r.append('%02d' % (ord(c) & 0xFF))
|
| 447 | return ''.join(r)
|
| 448 |
|
| 449 | def set_int_le(self, value, size):
|
| 450 | assert size >= 1 and size <= 4, u"长度错误"
|
| 451 | data = struct.pack('<I', value)
|
| 452 | return data[:size]
|
| 453 |
|
| 454 | def set_int_be(self, value, size):
|
| 455 | assert size >= 1 and size <= 4, u"长度错误"
|
| 456 | data = struct.pack('>I', value)
|
| 457 | return data[-size:]
|
| 458 |
|
| 459 |
|
| 460 | class s999_get_datetime(Smart999Protocol):
|
| 461 | def prepare(self, request):
|
| 462 | devphyid = request.get('devphyid', None)
|
| 463 | if not devphyid:
|
| 464 | raise ProtocolError(u'未定义 devphyid 属性')
|
| 465 | return self.encode_devphyid(devphyid)
|
| 466 |
|
| 467 | def unprepare(self, retcode, response):
|
| 468 | if retcode != Smart999Protocol.RET_OK:
|
| 469 | return True
|
| 470 | if len(response) != 7:
|
| 471 | raise ProtocolError(u'返回数据长度错误')
|
| 472 | return dict(datetime=self.decode_datetime(response))
|
| 473 |
|
| 474 |
|
| 475 | class s999_set_datetime(Smart999Protocol):
|
| 476 | def prepare(self, request):
|
| 477 | devphyid = request.get('devphyid')
|
| 478 | devphyid = self.encode_devphyid(devphyid)
|
| 479 | dt = request.get('datetime')
|
| 480 | dt = self.encode_datetime(dt)
|
| 481 | return struct.pack('<4s7s', devphyid, dt)
|
| 482 |
|
| 483 | def unprepare(self, retcode, response):
|
| 484 | return dict()
|
| 485 |
|
| 486 |
|
| 487 | class s999_download_blk(Smart999Protocol):
|
| 488 | def prepare(self, request):
|
| 489 | devphyid = request.get('devphyid')
|
| 490 | cardverno = request.get('cardverno')
|
| 491 | r = [self.encode_devphyid(devphyid),
|
| 492 | self.encode_bcd(cardverno)]
|
| 493 | blkdata = request.get('blkdata')
|
| 494 | for flag, cardno in blkdata:
|
| 495 | r.append(chr(flag))
|
| 496 | r.append(self.set_int_be(cardno, 3))
|
| 497 |
|
| 498 | return ''.join(r)
|
| 499 |
|
| 500 | def unprepare(self, retcode, response):
|
| 501 | if retcode != Smart999Protocol.RET_OK:
|
| 502 | return dict()
|
| 503 |
|
| 504 | if len(response) != 6:
|
| 505 | raise ProtocolError(u"下载黑名单返回数据错误")
|
| 506 | return dict(cardverno=self.decode_bcd(response))
|
| 507 |
|
| 508 |
|
| 509 | class s999_set_mainkey(Smart999Protocol):
|
| 510 | def prepare(self, request):
|
| 511 | devphyid = request.get('devphyid')
|
| 512 | mainkey = request.get('workkey')
|
| 513 | if len(mainkey) != 16:
|
| 514 | return None
|
| 515 |
|
| 516 | devphyid = self.encode_devphyid(devphyid)
|
| 517 | mainkey = codecs.decode(mainkey, 'hex')
|
| 518 | r = [devphyid, mainkey]
|
| 519 | return ''.join(r)
|
| 520 |
|
| 521 | def unprepare(self, retcode, response):
|
| 522 | return dict()
|
| 523 |
|
| 524 |
|
| 525 | class s999_get_syspara(Smart999Protocol):
|
| 526 | def prepare(self, request):
|
| 527 | devphyid = request.get('devphyid')
|
| 528 | return self.encode_devphyid(devphyid)
|
| 529 |
|
| 530 | def unprepare(self, retcode, response):
|
| 531 | if retcode != Smart999Protocol.RET_OK:
|
| 532 | return dict()
|
| 533 |
|
| 534 | fmt = '<4sBBB2s2s3s3s3s3sBBB3s3sB'
|
| 535 | if struct.calcsize(fmt) != len(response):
|
| 536 | raise ProtocolError(u"接收数据包内容长度不合法")
|
| 537 |
|
| 538 | fields = ('devphyid', 'addr', 'pswd_switch', 'trans_mode',
|
| 539 | 'paycnt_limit', 'rfu1', 'purse_max', 'purse_limit1',
|
| 540 | 'purse_min', 'pay_amount', 'function', 'baud_rate', 'purse_no',
|
| 541 | 'purse_once_limit', 'purse_day_limit', 'card_struct')
|
| 542 | params = struct.unpack(fmt, response)
|
| 543 | params = dict(zip(fields, params))
|
| 544 | self.__convert_params(params)
|
| 545 | return dict(params=params)
|
| 546 |
|
| 547 | def __convert_params(self, params):
|
| 548 | params['devphyid'] = self.decode_devphyid(params['devphyid'])
|
| 549 | params['paycnt_limit'] = self.get_int_be(params['paycnt_limit'])
|
| 550 | params['purse_max'] = self.get_int_le(params['purse_max'])
|
| 551 | params['purse_min'] = self.get_int_le(params['purse_min'])
|
| 552 | params['pay_amount'] = self.get_int_le(params['pay_amount'])
|
| 553 | params['purse_once_limit'] = self.get_int_le(params['purse_once_limit'])
|
| 554 | params['purse_day_limit'] = self.get_int_le(params['purse_day_limit'])
|
| 555 |
|
| 556 |
|
| 557 | class s999_set_syspara(Smart999Protocol):
|
| 558 | def prepare(self, request):
|
| 559 | #devphyid = request.get('devphyid')
|
| 560 | return self.__pack_params(request['params'])
|
| 561 |
|
| 562 | def unprepare(self, retcode, response):
|
| 563 | return dict()
|
| 564 |
|
| 565 | def __pack_params(self, params):
|
| 566 | r = []
|
| 567 | r.append(self.encode_devphyid(params['devphyid']))
|
| 568 | #r.append(chr(params['addr'] & 0xFF))
|
| 569 | r.append(chr(params['pswd_switch'] & 0xFF))
|
| 570 | r.append(chr(params['trans_mode'] & 0xFF))
|
| 571 | r.append(self.set_int_be(params['paycnt_limit'], 2))
|
| 572 | r.append(chr(0xFF) * 2)
|
| 573 | r.append(self.set_int_le(params['purse_max'], 3))
|
| 574 | # 单次消费金额与钱包余额一致
|
| 575 | r.append(self.set_int_le(params['purse_max'], 3))
|
| 576 | r.append(self.set_int_le(params['purse_min'], 3))
|
| 577 | r.append(self.set_int_le(params['pay_amount'], 3))
|
| 578 | r.append("\x80\x00\x00")
|
| 579 | r.append(self.set_int_le(params['purse_once_limit'], 3))
|
| 580 | r.append(self.set_int_le(params['purse_day_limit'], 3))
|
| 581 | r.append('\x64')
|
| 582 | data = ''.join(r)
|
| 583 | assert len(data) == 0x20
|
| 584 | return data
|
| 585 |
|
| 586 |
|
| 587 | class s999_collect_serial(Smart999Protocol):
|
| 588 | def prepare(self, request):
|
| 589 | devphyid = request.get('devphyid')
|
| 590 | return self.encode_devphyid(devphyid)
|
| 591 |
|
| 592 | def unprepare(self, retcode, response):
|
| 593 | if retcode != Smart999Protocol.RET_OK:
|
| 594 | return dict()
|
| 595 |
|
| 596 | if len(response) != 39:
|
| 597 | raise ProtocolError(u"记录流水长度错误")
|
| 598 |
|
| 599 | fmt = '<6s4s3s3s3s3s2s3s2s3s4sB2s'
|
| 600 | fields = ('samno', 'devphyid', 'cardno', 'transdate', 'transtime',
|
| 601 | 'cardbefbal', 'paycnt', 'amount', 'posseqno', 'managefee', 'tac',
|
| 602 | 'transmark', 'crc')
|
| 603 | assert struct.calcsize(fmt) == 39, u"流水结构定义长度不正确"
|
| 604 | r = struct.unpack(fmt, response)
|
| 605 | record = dict(zip(fields, r))
|
| 606 | self.__convert_record(record)
|
| 607 | # 验证流水crc, 从交易卡号开始计算的crc
|
| 608 | crc = calc_data_crc(response[10:-2])
|
| 609 | record['checkok'] = crc == response[-2:]
|
| 610 | return dict(record=record)
|
| 611 |
|
| 612 | def __convert_record(self, record):
|
| 613 | record['samno'] = codecs.encode(record['samno'], 'hex')
|
| 614 | record['devphyid'] = codecs.encode(record['devphyid'], 'hex').upper()
|
| 615 | record['cardno'] = self.get_int_be(record['cardno'])
|
| 616 | record['transdate'] = self.hex_to_str(record['transdate'])
|
| 617 | record['transtime'] = self.hex_to_str(record['transtime'])
|
| 618 | record['cardbefbal'] = self.get_int_le(record['cardbefbal'])
|
| 619 | record['paycnt'] = self.get_int_be(record['paycnt'])
|
| 620 | record['amount'] = self.get_int_le(record['amount'])
|
| 621 | record['posseqno'] = self.get_int_be(record['posseqno'])
|
| 622 | record['managefee'] = self.get_int_le(record['managefee'])
|
| 623 | record['tac'] = codecs.encode(record['tac'], 'hex')
|
| 624 | record['crc'] = codecs.encode(record['crc'], 'hex')
|
| 625 |
|
| 626 |
|
| 627 | class s999_collect_his_serial(s999_collect_serial):
|
| 628 | def prepare(self, request):
|
| 629 | devphyid = request['devphyid']
|
| 630 | serialno = request['serialno']
|
| 631 | return ''.join([self.encode_devphyid(devphyid),
|
| 632 | self.set_int_be(serialno, 2)])
|
| 633 |
|
| 634 |
|
| 635 | class s999_collect_confirm(Smart999Protocol):
|
| 636 | def prepare(self, request):
|
| 637 | devphyid = request.get('devphyid')
|
| 638 |
|
| 639 | r = [self.encode_devphyid(devphyid),
|
| 640 | struct.pack('>H', request.get('posseqno'))]
|
| 641 | return ''.join(r)
|
| 642 |
|
| 643 | def unprepare(self, retcode, response):
|
| 644 | return dict()
|
| 645 |
|
| 646 |
|
| 647 | class s999_set_feetype(Smart999Protocol):
|
| 648 | def prepare(self, request):
|
| 649 | devphyid = request.get('devphyid')
|
| 650 | bitmap = [chr(0) for i in range(32)]
|
| 651 | for i, c in enumerate(request.get('feetype')):
|
| 652 | if c == '1':
|
| 653 | self.__get_feetype_right(i + 1, bitmap)
|
| 654 | r = [self.encode_devphyid(devphyid), ''.join(bitmap)]
|
| 655 | return ''.join(r)
|
| 656 |
|
| 657 | def __get_feetype_right(self, feetype, bitmap, flag=True):
|
| 658 | assert feetype < 256 and feetype >= 0, u"卡类别溢出"
|
| 659 | byte_offset = 31 - (feetype / 8)
|
| 660 | byte_data = ord(bitmap[byte_offset])
|
| 661 | bit_offset = feetype % 8
|
| 662 | if flag:
|
| 663 | byte_data |= 1 << bit_offset
|
| 664 | else:
|
| 665 | byte_data &= ~(1 << bit_offset)
|
| 666 | bitmap[byte_offset] = chr(byte_data & 0xFF)
|
| 667 |
|
| 668 | def unprepare(self, retcode, response):
|
| 669 | return dict()
|
| 670 |
|
| 671 |
|
| 672 | class s999_get_feetype(Smart999Protocol):
|
| 673 | def prepare(self, request):
|
| 674 | devphyid = request.get('devphyid')
|
| 675 | return self.encode_devphyid(devphyid)
|
| 676 |
|
| 677 | def unprepare(self, retcode, response):
|
| 678 | if retcode != Smart999Protocol.RET_OK:
|
| 679 | return dict()
|
| 680 |
|
| 681 | if len(response) != 32:
|
| 682 | raise ProtocolError(u"应答数据长度错误")
|
| 683 | feetype = []
|
| 684 | for c in reversed(response):
|
| 685 | t = ord(c)
|
| 686 | for i in range(8):
|
| 687 | if (t & (1 << i)) == 0:
|
| 688 | feetype.append('0')
|
| 689 | else:
|
| 690 | feetype.append('1')
|
| 691 | return dict(feetype=feetype[1:])
|
| 692 |
|
| 693 |
|
| 694 | class s999_set_feerate(Smart999Protocol):
|
| 695 | def prepare(self, request):
|
| 696 | devphyid = request.get('devphyid')
|
| 697 | return struct.pack('<4sBB', self.encode_devphyid(devphyid),
|
| 698 | request.get('feetype'),
|
| 699 | request.get('fee'))
|
| 700 |
|
| 701 | def unprepare(self, retcode, response):
|
| 702 | return dict()
|
| 703 |
|
| 704 |
|
| 705 | class s999_get_feerate(Smart999Protocol):
|
| 706 | def prepare(self, request):
|
| 707 | devphyid = request.get('devphyid')
|
| 708 | feetype = request.get('feetype')
|
| 709 | if feetype < 1 or feetype > 255:
|
| 710 | raise ProtocolError(u"获取卡类型溢出")
|
| 711 | return ''.join([self.encode_devphyid(devphyid), struct.pack('>B', feetype)])
|
| 712 |
|
| 713 | def unprepare(self, retcode, response):
|
| 714 | if retcode != Smart999Protocol.RET_OK:
|
| 715 | return dict()
|
| 716 | if len(response) != 1:
|
| 717 | raise ProtocolError(u"应答卡费率数据长度错误")
|
| 718 | fee = ord(response)
|
| 719 | if fee == 0xFF:
|
| 720 | fee = 100
|
| 721 | return dict(fee=fee)
|
| 722 |
|
| 723 |
|
| 724 | class s999_clear_blacklist(Smart999Protocol):
|
| 725 | def prepare(self, request):
|
| 726 | devphyid = request.get('devphyid')
|
| 727 | return self.encode_devphyid(devphyid)
|
| 728 |
|
| 729 | def unprepare(self, retcode, response):
|
| 730 | return dict()
|
| 731 |
|
| 732 |
|
| 733 | class s999_set_offline_day(Smart999Protocol):
|
| 734 | def prepare(self, request):
|
| 735 | devphyid = request.get('devphyid')
|
| 736 | offline = request['offline']
|
| 737 | return ''.join([self.encode_devphyid(devphyid), struct.pack('<B', offline)])
|
| 738 |
|
| 739 | def unprepare(self, retcode, response):
|
| 740 | return dict()
|
| 741 |
|
| 742 |
|
| 743 | class s999_get_offline_day(Smart999Protocol):
|
| 744 | def prepare(self, request):
|
| 745 | devphyid = request.get('devphyid')
|
| 746 | return self.encode_devphyid(devphyid)
|
| 747 |
|
| 748 | def unprepare(self, retcode, response):
|
| 749 | if retcode != Smart999Protocol.RET_OK:
|
| 750 | return dict()
|
| 751 |
|
| 752 | if len(response) != 1:
|
| 753 | raise ProtocolError(u"结束设备脱机工作天数错误")
|
| 754 |
|
| 755 | return dict(offline=ord(response))
|
| 756 |
|
| 757 |
|
| 758 | class s999_check_blk(Smart999Protocol):
|
| 759 | def prepare(self, request):
|
| 760 | devphyid = request['devphyid']
|
| 761 | cardno = request['cardno']
|
| 762 | r = [self.encode_devphyid(devphyid),
|
| 763 | self.set_int_be(cardno, 3)]
|
| 764 | return ''.join(r)
|
| 765 |
|
| 766 | def unprepare(self, retcode, response):
|
| 767 | return dict()
|
| 768 |
|
| 769 |
|
| 770 | _initialized = False
|
| 771 |
|
| 772 |
|
| 773 | def register_protocol():
|
| 774 | global _initialized
|
| 775 | if _initialized:
|
| 776 | return
|
| 777 |
|
| 778 | Smart999Protocol.register_protocol(CMD_GET_DATETIME, s999_get_datetime)
|
| 779 | Smart999Protocol.register_protocol(CMD_SET_DATETIME, s999_set_datetime)
|
| 780 | Smart999Protocol.register_protocol(CMD_DOWNLOAD_BLK, s999_download_blk)
|
| 781 | Smart999Protocol.register_protocol(CMD_SET_WORKKEY, s999_set_mainkey)
|
| 782 | Smart999Protocol.register_protocol(CMD_GET_SYSPARA, s999_get_syspara)
|
| 783 | Smart999Protocol.register_protocol(CMD_SET_SYSPARA, s999_set_syspara)
|
| 784 | Smart999Protocol.register_protocol(CMD_COLLECT_RECORD, s999_collect_serial)
|
| 785 | Smart999Protocol.register_protocol(CMD_COLLECT_CONFIRM, s999_collect_confirm)
|
| 786 | Smart999Protocol.register_protocol(CMD_COLLECT_HISTORY, s999_collect_his_serial)
|
| 787 | Smart999Protocol.register_protocol(CMD_SET_FEETYPE, s999_set_feetype)
|
| 788 | Smart999Protocol.register_protocol(CMD_SET_FEERATE, s999_set_feerate)
|
| 789 | Smart999Protocol.register_protocol(CMD_GET_FEETYPE, s999_get_feetype)
|
| 790 | Smart999Protocol.register_protocol(CMD_GET_FEERATE, s999_get_feerate)
|
| 791 | Smart999Protocol.register_protocol(CMD_CLEAR_BLK, s999_clear_blacklist)
|
| 792 | Smart999Protocol.register_protocol(CMD_SET_OFFLINE, s999_set_offline_day)
|
| 793 | Smart999Protocol.register_protocol(CMD_GET_OFFLINE, s999_get_offline_day)
|
| 794 | Smart999Protocol.register_protocol(CMD_CHECK_BLK, s999_check_blk)
|
| 795 |
|
| 796 | _initialized = True
|