blob: c5a8ee35a60de5f21e1de1fcfc2ad53f90be9389 [file] [log] [blame]
Tang Cheng5d8cf502012-11-05 14:27:40 +08001# -*- coding: utf-8
2
3import codecs
4import struct
5import socket
6import threading
7import time
8import errno
9import logging
10from datetime import datetime
11
12
13def _b(v):
14 if isinstance(v, unicode):
15 return v.encode('lanti-1')
16 return v
17
18
19# 获取设备时钟
20CMD_GET_DATETIME = 0xAA
21# 设置设备时钟
22CMD_SET_DATETIME = 0xA9
23# 检查黑名单
24CMD_CHECK_BLK = 0xBC
25#下载黑名单
26CMD_DOWNLOAD_BLK = 0xA4
27#删除所有黑名单
28CMD_CLEAR_BLK = 0xA6
29#获取收费机主参数
30CMD_GET_SYSPARA = 0xBF
31#下传收费机主参数
32CMD_SET_SYSPARA = 0xB7
33#采集一条记录
34CMD_COLLECT_RECORD = 0x03
35#确认采集一条记录
36CMD_COLLECT_CONFIRM = 0x04
37#补采一条记录
38CMD_COLLECT_HISTORY = 0xBB
39#下传管理员密码
40CMD_SET_SYSPSWD = 0x9F
41#下传允许卡类别
42CMD_SET_FEETYPE = 0x67
43#上传允许卡类别
44CMD_GET_FEETYPE = 0x68
45#下传卡类搭伙费/折扣
46CMD_SET_FEERATE = 0x73
47#上传卡类搭伙费/折扣
48CMD_GET_FEERATE = 0x74
49#读取脱机工作时间
50CMD_GET_OFFLINE = 0x85
51#设置脱机工作时间
52CMD_SET_OFFLINE = 0x84
53#设置工作密钥
54CMD_SET_WORKKEY = 0x83
55
56
57Smart999CRCTable = (
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
92def 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
99class ProtocolError(BaseException):
100 def __init__(self, msg):
Tang Chengd36592e2012-12-20 14:24:27 +0800101 super(ProtocolError, self).__init__()
Tang Cheng5d8cf502012-11-05 14:27:40 +0800102 self.msg = msg
103
104
105class Smart999Connection(object):
Tang Chengd36592e2012-12-20 14:24:27 +0800106 def __init__(self, ip, port, debug=1):
Tang Cheng5d8cf502012-11-05 14:27:40 +0800107 self.ip = ip
108 self.port = port
109 self.sock = None
Tang Chengd36592e2012-12-20 14:24:27 +0800110 self.errmsg = ""
111 self.debug = debug
112 self._last_exec_err = 0
Tang Cheng5d8cf502012-11-05 14:27:40 +0800113
114 def connect(self, timeout=10.0):
115 try:
116 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
117 sock.settimeout(timeout)
118 sock.connect((self.ip, self.port))
119 self.sock = sock
Tang Chengd36592e2012-12-20 14:24:27 +0800120 logging.info(u"连接[%s:%d]成功" % (self.ip, self.port))
Tang Cheng5d8cf502012-11-05 14:27:40 +0800121 return True
122 except socket.error, msg:
123 self.errmsg = unicode(msg)
Tang Chengd36592e2012-12-20 14:24:27 +0800124 logging.info(u"连接[%s:%d]失败[%s]" % (self.ip, self.port, self.errmsg))
Tang Cheng5d8cf502012-11-05 14:27:40 +0800125 return False
126
127 def disconnect(self):
Tang Chengd36592e2012-12-20 14:24:27 +0800128 self._last_exec_err = 0
Tang Cheng5d8cf502012-11-05 14:27:40 +0800129 if self.sock:
130 try:
131 self.sock.close()
132 self.sock = None
133 except:
134 self.sock = None
135
136 def is_connected(self):
137 return self.sock != None
138
139 def log(self, data):
Tang Chengd36592e2012-12-20 14:24:27 +0800140 if not self.debug:
141 return
Tang Cheng5d8cf502012-11-05 14:27:40 +0800142 try:
143 print "CMD[%s]" % codecs.encode(data, 'hex')
144 except:
145 pass
146
147 def send_command(self, request, response, timeout=10.0):
Tang Chengd36592e2012-12-20 14:24:27 +0800148 if not self.is_connected():
149 if not self.connect(timeout):
150 logging.error(u"尝试重连设备失败")
151 return False
Tang Cheng5d8cf502012-11-05 14:27:40 +0800152 protocol = Smart999Protocol()
153 cmd = protocol.pack(request)
154 self.log(cmd)
155 begin_time = time.time()
156 try:
157 self.sock.settimeout(timeout)
158 self.sock.send(cmd)
159 resp_data = []
160 while True:
161 data = self.sock.recv(1024)
162 if not data:
163 self.errmsg = u"接收应答超时"
164 return False
165
166 if not resp_data:
167 if ord(data[0]) != Smart999Protocol.RTX_BEGIN:
168 self.errmsg = u"接收数据包起始符不正确"
169 return False
170
171 resp_data.append(data)
172 if ord(data[-1]) == Smart999Protocol.RTX_END:
173 break
174 est = time.time() - begin_time
175 if est >= timeout:
176 self.errmsg = u"接收数据未收到结束符"
177 return False
178 self.sock.settimeout(timeout - est)
Tang Chengd36592e2012-12-20 14:24:27 +0800179 self._last_exec_err = 0
Tang Cheng5d8cf502012-11-05 14:27:40 +0800180 except socket.error, msg:
Tang Chengd36592e2012-12-20 14:24:27 +0800181 if msg.errno != None and msg.errno != errno.EWOULDBLOCK:
182 self.disconnect()
Tang Cheng5d8cf502012-11-05 14:27:40 +0800183 logging.error(u"远程连接[%s]已关闭!" % self.ip)
Tang Chengd36592e2012-12-20 14:24:27 +0800184 else:
185 self._last_exec_err += 1
186 if self._last_exec_err > 50:
187 self.disconnect()
Tang Cheng5d8cf502012-11-05 14:27:40 +0800188 self.errmsg = unicode(msg)
189 return False
190 except Exception, ex:
191 self.errmsg = unicode(ex)
192 return False
193
194 full_data = ''.join(resp_data)
195 if ord(full_data[0]) != Smart999Protocol.RTX_BEGIN or \
196 ord(full_data[-1]) != Smart999Protocol.RTX_END:
197 self.errmsg = u"接收到的数据包不完整"
198 return False
199
200 self.log(full_data)
201 #protocol.clear_data()
202 try:
203 result = protocol.unpack(full_data)
204 response.update(result)
205 return True
206 except ProtocolError, msg:
207 self.errmsg = msg.msg
208 return False
209
210
211class Smart999Protocol(object):
212 """
213 三九智慧通讯协议封装
214 """
215 VERSION = "1.0"
216 RET_OK = 0
217
218 STX_BEGIN = 0xC0
219 STX_END = 0xC1
220
221 RTX_BEGIN = 0xC2
222 RTX_END = 0xC3
223
224 BLACKCARD_ADD = 0
225 BLACKCARD_DEL = 0xff
226
227 __protocols = {}
228
229 __protocol_cache = {}
230
231 __lock = threading.Lock()
232
233 ERRDEFINES = {2: u"时钟错误",
234 4: u"数据满",
235 5: u"流水号错",
236 6: u"成功确认",
237 7: u"参数错误",
238 8: u"命令使用错误",
239 9: u"重复操作",
240 10: u"无记录",
241 11: u"注册号错",
242 13: u"申请签到",
243 14: u"黑名单版本过期",
244 17: u"有未上传数据",
245 21: u"不成功的确认",
246 24: u"未授权,无法签到"}
247
248 @staticmethod
249 def register_protocol(command, clazz):
250 Smart999Protocol.__protocols[command] = clazz
251
252 @staticmethod
253 def unregister_protocol(command):
254 if command in Smart999Protocol.__protocols:
255 del Smart999Protocol.__protocols[command]
256
257 def __init__(self):
258 self.clear_data()
259
260 def add_parent(self, parent_devid, parent_addr, port):
261 """
262 增加设备级别
263 parent_devid - 上级设备物理ID
264 parent_addr - 上级设备机号
265 port - 对应上级设备的地址号
266 """
267 self.parents.append((parent_devid, parent_addr, port))
268
269 def clear_data(self):
270 self.command = None
271
272 def __find_protocol(self, command):
273 with Smart999Protocol.__lock:
274 if command in Smart999Protocol.__protocol_cache:
275 return Smart999Protocol.__protocol_cache[command]
276
277 if command not in Smart999Protocol.__protocols:
278 return None
279 clazz = Smart999Protocol.__protocols[command]
280 cache = dict(timestamp=time.time(),
281 obj=clazz())
282 Smart999Protocol.__protocol_cache[command] = cache
283 return cache
284
285 def pack(self, request):
286 """
287 打包三九通讯协议
288 request : 请求数据
289 return : 协议包数据
290 Exception : ProtocolError
291 """
292 command = request.get('command')
293 if not command:
294 raise ProtocolError(u"未指定命令字")
295 if isinstance(command, str) or isinstance(command, unicode):
296 command = int(command, 16) & 0xFF
297 proto_obj = self.__find_protocol(command)
298 if not proto_obj:
299 raise ProtocolError(u"不支持的指令,cmd=%02X" % command)
300 data = proto_obj['obj'].prepare(request)
301 if data is None:
302 raise ProtocolError(u"指令打包错误,cmd=%02X" % command)
303 self.command = command
304 return self.__pack_send_cmd(command, request, data)
305
306 def __pack_send_cmd(self, commmand, request, data):
307 """
308 打包函数,
309 """
310 # TODO: 增加 w-lport 级联设备协议打包支持
311 addr = request.get('addr')
312 if isinstance(addr, str) or isinstance(addr, unicode):
313 addr = int(addr) & 0xFF
314
315 r = [chr(Smart999Protocol.STX_BEGIN), chr(addr), chr(commmand)]
316 if data:
317 r.append(chr(len(data) % 0xFF))
318 r.append(data)
319 proto = ''.join(r)
320 r = [proto, calc_data_crc(proto), chr(Smart999Protocol.STX_END)]
321 return ''.join(r)
322
323 def __unpack_recv_cmd(self, recv_data):
324 """
325 解包函数
326 """
327 # TODO: 增加 w-lport 级联设备协议解包支持
328 # RTX + Addr + Retcode + Len CRC(2byte) + REX
329 if len(recv_data) < 4 + 3:
330 raise ProtocolError(u"接收数据长度至少6个字节,实际只有%d个字节" % len(recv_data))
331
332 crc = calc_data_crc(recv_data[:-3])
333 if crc != recv_data[-3:-1]:
334 raise ProtocolError(u"验证接收数据CRC错误")
335 addr = ord(recv_data[1])
336 retcode = ord(recv_data[2])
337 datalen = ord(recv_data[3])
338 if len(recv_data[4:-3]) != datalen:
339 raise ProtocolError(u"接收数据长度验证错误")
340
341 return dict(addr=addr, retcode=retcode,
342 retmsg=self.__get_s9_errmsg(retcode),
343 data=recv_data[4:-3])
344
345 def __get_s9_errmsg(self, errcode):
346 if errcode == Smart999Protocol.RET_OK:
347 return u"成功"
348 else:
349 return Smart999Protocol.ERRDEFINES.get(errcode,
350 u"未知错误ret=%d" % errcode)
351
352 def unpack(self, response):
353 """
354 解包三九通讯协议包
355 """
356 assert self.command != None, u"调用顺序错误"
357 result = self.__unpack_recv_cmd(response)
358 proto_obj = self.__find_protocol(self.command)
359 if not proto_obj:
360 raise ProtocolError(u"不支持的指令,cmd=%02X" % self.command)
361 data = proto_obj['obj'].unprepare(result.get('retcode'),
362 result.get('data'))
363 del result['data']
364 result.update(data)
365 return result
366
367 def prepare(self, request):
368 """
369 返回 None 表示打包失败
370 返回 空传 表示无数据
371 """
372 raise RuntimeError(u"函数 prepare 必须被重载")
373
374 def unprepare(self, retcode, response):
375 raise RuntimeError(u'函数 unprepare 必须被重载')
376
377 def encode_devphyid(self, devphyid):
378 return codecs.decode(devphyid, 'hex')
379
380 def decode_devphyid(self, devphyid):
381 return codecs.encode(devphyid, 'hex')
382
383 def encode_datetime(self, now):
384 if isinstance(now, str) or isinstance(now, unicode):
385 dt = datetime.strptime('%y%m%d%H%M%S', now)
386 dtstr = now
387 elif isinstance(now, datetime):
388 dt = now
389 dtstr = now.strftime('%y%m%d%H%M%S')
390 else:
391 raise ProtocolError(u"不合法的日期格式")
392
393 dl = len(dtstr)
394 r = [chr(int(dtstr[i:i + 2]) & 0xFF) for i in range(0, dl, 2)]
395
396 r.append(chr(dt.weekday() + 1))
397 return ''.join(r)
398
399 def decode_datetime(self, datetime):
400 r = ['%02d' % ord(c) for c in datetime]
401 dt = ''.join(r)
402 return '20' + dt[:-2]
403
404 def _encode_bcd_num(self, num1, num2):
405 return chr(((ord(num1) - 0x30) << 4) | (ord(num2) - 0x30))
406
407 def encode_bcd(self, data):
408 assert len(data) % 2 == 0, 'data length must divise by 2'
409 dl = len(data)
410 r = [self._encode_bcd_num(data[i], data[i + 1])
411 for i in range(0, dl, 2)]
412 return ''.join(r)
413
414 def decode_bcd(self, data):
415 r = []
416 for c in data:
417 t1 = chr(((ord(c) & 0xF0) >> 4) + 0x30)
418 t2 = chr((ord(c) & 0x0F) + 0x30)
419 r.append(t1)
420 r.append(t2)
421 return ''.join(r)
422
423 def encode_cardverno(self, cardverno):
424 return self.encode_bcd(cardverno)
425
426 def decode_cardverno(self, cardverno):
427 return self.decode_bcd(cardverno)
428
429 def set_data(self, data):
430 self.data = data
431
432 def get_int_be(self, data):
433 l = len(data)
434 assert (l <= 4 and l > 0), u"数据长度错误"
435 if l >= 3:
436 if l == 3:
437 data = chr(0) + data
438 fmt = '>I'
439 else:
440 if l == 1:
441 data = chr(0) + data
442 fmt = '>H'
443 return struct.unpack(fmt, data)[0]
444
445 def get_int_le(self, data):
446 l = len(data)
447 assert (l <= 4 and l > 0), u"数据长度错误"
448 if l >= 3:
449 if l == 3:
450 data = data + chr(0)
451 fmt = '<I'
452 else:
453 if l == 1:
454 data = data + chr(0)
455 fmt = '<H'
456 return struct.unpack(fmt, data)[0]
457
458 def hex_to_str(self, data):
459 r = []
460 for c in data:
461 r.append('%02d' % (ord(c) & 0xFF))
462 return ''.join(r)
463
464 def set_int_le(self, value, size):
465 assert size >= 1 and size <= 4, u"长度错误"
466 data = struct.pack('<I', value)
467 return data[:size]
468
469 def set_int_be(self, value, size):
470 assert size >= 1 and size <= 4, u"长度错误"
471 data = struct.pack('>I', value)
472 return data[-size:]
473
474
475class s999_get_datetime(Smart999Protocol):
476 def prepare(self, request):
477 devphyid = request.get('devphyid', None)
478 if not devphyid:
479 raise ProtocolError(u'未定义 devphyid 属性')
480 return self.encode_devphyid(devphyid)
481
482 def unprepare(self, retcode, response):
483 if retcode != Smart999Protocol.RET_OK:
484 return True
485 if len(response) != 7:
486 raise ProtocolError(u'返回数据长度错误')
487 return dict(datetime=self.decode_datetime(response))
488
489
490class s999_set_datetime(Smart999Protocol):
491 def prepare(self, request):
492 devphyid = request.get('devphyid')
493 devphyid = self.encode_devphyid(devphyid)
494 dt = request.get('datetime')
495 dt = self.encode_datetime(dt)
496 return struct.pack('<4s7s', devphyid, dt)
497
498 def unprepare(self, retcode, response):
499 return dict()
500
501
502class s999_download_blk(Smart999Protocol):
503 def prepare(self, request):
504 devphyid = request.get('devphyid')
505 cardverno = request.get('cardverno')
506 r = [self.encode_devphyid(devphyid),
507 self.encode_bcd(cardverno)]
508 blkdata = request.get('blkdata')
509 for flag, cardno in blkdata:
510 r.append(chr(flag))
511 r.append(self.set_int_be(cardno, 3))
512
513 return ''.join(r)
514
515 def unprepare(self, retcode, response):
516 if retcode != Smart999Protocol.RET_OK:
517 return dict()
518
519 if len(response) != 6:
520 raise ProtocolError(u"下载黑名单返回数据错误")
521 return dict(cardverno=self.decode_bcd(response))
522
523
524class s999_set_mainkey(Smart999Protocol):
525 def prepare(self, request):
526 devphyid = request.get('devphyid')
527 mainkey = request.get('workkey')
528 if len(mainkey) != 16:
529 return None
530
531 devphyid = self.encode_devphyid(devphyid)
532 mainkey = codecs.decode(mainkey, 'hex')
533 r = [devphyid, mainkey]
534 return ''.join(r)
535
536 def unprepare(self, retcode, response):
537 return dict()
538
539
540class s999_get_syspara(Smart999Protocol):
541 def prepare(self, request):
542 devphyid = request.get('devphyid')
543 return self.encode_devphyid(devphyid)
544
545 def unprepare(self, retcode, response):
546 if retcode != Smart999Protocol.RET_OK:
547 return dict()
548
549 fmt = '<4sBBB2s2s3s3s3s3sBBB3s3sB'
550 if struct.calcsize(fmt) != len(response):
551 raise ProtocolError(u"接收数据包内容长度不合法")
552
553 fields = ('devphyid', 'addr', 'pswd_switch', 'trans_mode',
554 'paycnt_limit', 'rfu1', 'purse_max', 'purse_limit1',
555 'purse_min', 'pay_amount', 'function', 'baud_rate', 'purse_no',
556 'purse_once_limit', 'purse_day_limit', 'card_struct')
557 params = struct.unpack(fmt, response)
558 params = dict(zip(fields, params))
559 self.__convert_params(params)
560 return dict(params=params)
561
562 def __convert_params(self, params):
563 params['devphyid'] = self.decode_devphyid(params['devphyid'])
564 params['paycnt_limit'] = self.get_int_be(params['paycnt_limit'])
565 params['purse_max'] = self.get_int_le(params['purse_max'])
566 params['purse_min'] = self.get_int_le(params['purse_min'])
567 params['pay_amount'] = self.get_int_le(params['pay_amount'])
568 params['purse_once_limit'] = self.get_int_le(params['purse_once_limit'])
569 params['purse_day_limit'] = self.get_int_le(params['purse_day_limit'])
570
571
572class s999_set_syspara(Smart999Protocol):
573 def prepare(self, request):
574 #devphyid = request.get('devphyid')
575 return self.__pack_params(request['params'])
576
577 def unprepare(self, retcode, response):
578 return dict()
579
580 def __pack_params(self, params):
581 r = []
582 r.append(self.encode_devphyid(params['devphyid']))
583 #r.append(chr(params['addr'] & 0xFF))
584 r.append(chr(params['pswd_switch'] & 0xFF))
585 r.append(chr(params['trans_mode'] & 0xFF))
586 r.append(self.set_int_be(params['paycnt_limit'], 2))
587 r.append(chr(0xFF) * 2)
588 r.append(self.set_int_le(params['purse_max'], 3))
589 # 单次消费金额与钱包余额一致
590 r.append(self.set_int_le(params['purse_max'], 3))
591 r.append(self.set_int_le(params['purse_min'], 3))
592 r.append(self.set_int_le(params['pay_amount'], 3))
593 r.append("\x80\x00\x00")
594 r.append(self.set_int_le(params['purse_once_limit'], 3))
595 r.append(self.set_int_le(params['purse_day_limit'], 3))
596 r.append('\x64')
597 data = ''.join(r)
598 assert len(data) == 0x20
599 return data
600
601
602class s999_collect_serial(Smart999Protocol):
603 def prepare(self, request):
604 devphyid = request.get('devphyid')
605 return self.encode_devphyid(devphyid)
606
607 def unprepare(self, retcode, response):
608 if retcode != Smart999Protocol.RET_OK:
609 return dict()
610
611 if len(response) != 39:
612 raise ProtocolError(u"记录流水长度错误")
613
614 fmt = '<6s4s3s3s3s3s2s3s2s3s4sB2s'
615 fields = ('samno', 'devphyid', 'cardno', 'transdate', 'transtime',
616 'cardbefbal', 'paycnt', 'amount', 'posseqno', 'managefee', 'tac',
617 'transmark', 'crc')
618 assert struct.calcsize(fmt) == 39, u"流水结构定义长度不正确"
619 r = struct.unpack(fmt, response)
620 record = dict(zip(fields, r))
621 self.__convert_record(record)
622 # 验证流水crc, 从交易卡号开始计算的crc
623 crc = calc_data_crc(response[10:-2])
624 record['checkok'] = crc == response[-2:]
625 return dict(record=record)
626
627 def __convert_record(self, record):
628 record['samno'] = codecs.encode(record['samno'], 'hex')
629 record['devphyid'] = codecs.encode(record['devphyid'], 'hex').upper()
630 record['cardno'] = self.get_int_be(record['cardno'])
631 record['transdate'] = self.hex_to_str(record['transdate'])
632 record['transtime'] = self.hex_to_str(record['transtime'])
633 record['cardbefbal'] = self.get_int_le(record['cardbefbal'])
634 record['paycnt'] = self.get_int_be(record['paycnt'])
635 record['amount'] = self.get_int_le(record['amount'])
636 record['posseqno'] = self.get_int_be(record['posseqno'])
637 record['managefee'] = self.get_int_le(record['managefee'])
638 record['tac'] = codecs.encode(record['tac'], 'hex')
639 record['crc'] = codecs.encode(record['crc'], 'hex')
640
641
642class s999_collect_his_serial(s999_collect_serial):
643 def prepare(self, request):
644 devphyid = request['devphyid']
645 serialno = request['serialno']
646 return ''.join([self.encode_devphyid(devphyid),
647 self.set_int_be(serialno, 2)])
648
649
650class s999_collect_confirm(Smart999Protocol):
651 def prepare(self, request):
652 devphyid = request.get('devphyid')
653
654 r = [self.encode_devphyid(devphyid),
655 struct.pack('>H', request.get('posseqno'))]
656 return ''.join(r)
657
658 def unprepare(self, retcode, response):
659 return dict()
660
661
662class s999_set_feetype(Smart999Protocol):
663 def prepare(self, request):
664 devphyid = request.get('devphyid')
665 bitmap = [chr(0) for i in range(32)]
666 for i, c in enumerate(request.get('feetype')):
667 if c == '1':
668 self.__get_feetype_right(i + 1, bitmap)
669 r = [self.encode_devphyid(devphyid), ''.join(bitmap)]
670 return ''.join(r)
671
672 def __get_feetype_right(self, feetype, bitmap, flag=True):
673 assert feetype < 256 and feetype >= 0, u"卡类别溢出"
674 byte_offset = 31 - (feetype / 8)
675 byte_data = ord(bitmap[byte_offset])
676 bit_offset = feetype % 8
677 if flag:
678 byte_data |= 1 << bit_offset
679 else:
680 byte_data &= ~(1 << bit_offset)
681 bitmap[byte_offset] = chr(byte_data & 0xFF)
682
683 def unprepare(self, retcode, response):
684 return dict()
685
686
687class s999_get_feetype(Smart999Protocol):
688 def prepare(self, request):
689 devphyid = request.get('devphyid')
690 return self.encode_devphyid(devphyid)
691
692 def unprepare(self, retcode, response):
693 if retcode != Smart999Protocol.RET_OK:
694 return dict()
695
696 if len(response) != 32:
697 raise ProtocolError(u"应答数据长度错误")
698 feetype = []
699 for c in reversed(response):
700 t = ord(c)
701 for i in range(8):
702 if (t & (1 << i)) == 0:
703 feetype.append('0')
704 else:
705 feetype.append('1')
706 return dict(feetype=feetype[1:])
707
708
709class s999_set_feerate(Smart999Protocol):
710 def prepare(self, request):
711 devphyid = request.get('devphyid')
712 return struct.pack('<4sBB', self.encode_devphyid(devphyid),
713 request.get('feetype'),
714 request.get('fee'))
715
716 def unprepare(self, retcode, response):
717 return dict()
718
719
720class s999_get_feerate(Smart999Protocol):
721 def prepare(self, request):
722 devphyid = request.get('devphyid')
723 feetype = request.get('feetype')
724 if feetype < 1 or feetype > 255:
725 raise ProtocolError(u"获取卡类型溢出")
726 return ''.join([self.encode_devphyid(devphyid), struct.pack('>B', feetype)])
727
728 def unprepare(self, retcode, response):
729 if retcode != Smart999Protocol.RET_OK:
730 return dict()
731 if len(response) != 1:
732 raise ProtocolError(u"应答卡费率数据长度错误")
733 fee = ord(response)
734 if fee == 0xFF:
735 fee = 100
736 return dict(fee=fee)
737
738
739class s999_clear_blacklist(Smart999Protocol):
740 def prepare(self, request):
741 devphyid = request.get('devphyid')
742 return self.encode_devphyid(devphyid)
743
744 def unprepare(self, retcode, response):
745 return dict()
746
747
748class s999_set_offline_day(Smart999Protocol):
749 def prepare(self, request):
750 devphyid = request.get('devphyid')
751 offline = request['offline']
752 return ''.join([self.encode_devphyid(devphyid), struct.pack('<B', offline)])
753
754 def unprepare(self, retcode, response):
755 return dict()
756
757
758class s999_get_offline_day(Smart999Protocol):
759 def prepare(self, request):
760 devphyid = request.get('devphyid')
761 return self.encode_devphyid(devphyid)
762
763 def unprepare(self, retcode, response):
764 if retcode != Smart999Protocol.RET_OK:
765 return dict()
766
767 if len(response) != 1:
768 raise ProtocolError(u"结束设备脱机工作天数错误")
769
770 return dict(offline=ord(response))
771
772
773class s999_check_blk(Smart999Protocol):
774 def prepare(self, request):
775 devphyid = request['devphyid']
776 cardno = request['cardno']
777 r = [self.encode_devphyid(devphyid),
778 self.set_int_be(cardno, 3)]
779 return ''.join(r)
780
781 def unprepare(self, retcode, response):
782 return dict()
783
784
785_initialized = False
786
787
788def register_protocol():
789 global _initialized
790 if _initialized:
791 return
792
793 Smart999Protocol.register_protocol(CMD_GET_DATETIME, s999_get_datetime)
794 Smart999Protocol.register_protocol(CMD_SET_DATETIME, s999_set_datetime)
795 Smart999Protocol.register_protocol(CMD_DOWNLOAD_BLK, s999_download_blk)
796 Smart999Protocol.register_protocol(CMD_SET_WORKKEY, s999_set_mainkey)
797 Smart999Protocol.register_protocol(CMD_GET_SYSPARA, s999_get_syspara)
798 Smart999Protocol.register_protocol(CMD_SET_SYSPARA, s999_set_syspara)
799 Smart999Protocol.register_protocol(CMD_COLLECT_RECORD, s999_collect_serial)
800 Smart999Protocol.register_protocol(CMD_COLLECT_CONFIRM, s999_collect_confirm)
801 Smart999Protocol.register_protocol(CMD_COLLECT_HISTORY, s999_collect_his_serial)
802 Smart999Protocol.register_protocol(CMD_SET_FEETYPE, s999_set_feetype)
803 Smart999Protocol.register_protocol(CMD_SET_FEERATE, s999_set_feerate)
804 Smart999Protocol.register_protocol(CMD_GET_FEETYPE, s999_get_feetype)
805 Smart999Protocol.register_protocol(CMD_GET_FEERATE, s999_get_feerate)
806 Smart999Protocol.register_protocol(CMD_CLEAR_BLK, s999_clear_blacklist)
807 Smart999Protocol.register_protocol(CMD_SET_OFFLINE, s999_set_offline_day)
808 Smart999Protocol.register_protocol(CMD_GET_OFFLINE, s999_get_offline_day)
809 Smart999Protocol.register_protocol(CMD_CHECK_BLK, s999_check_blk)
810
811 _initialized = True