def recv_msg_max_wait_time(self): # 接收数据 msg_bytes_hex = self.socket_client.recv(1024).hex() msg_str = CommonUtil.transfer_recv_command(msg_bytes_hex) return msg_str
def etc_toll(rsu_client: RsuSocket): """ 根据检测到的obu信息判断是否扣费 @param rsu_client: @return: """ while True: # socket监听,接受数据 try: msg_str = rsu_client.recv_msg_max_wait_time() except: DBOPeration.update_rsu_pid_status(rsu_client, 0) err_msg = traceback.format_exc() logger.error(err_msg) if err_msg.find('远程主机强迫关闭了一个现有的连接') != -1 or err_msg.find('你的主机中的软件中止了一个已建立的连接') != -1: time.sleep(30) else: time.sleep(10) continue # 有接收到数据,表明天线还在工作,更新心跳时间 rsu_client.rsu_heartbeat_time = datetime.now() if msg_str[8: 12] == 'b200': # 心跳指令 logger.info('lane_num:{} 心跳指令:{}, 天线时间:{}, 当前时间:{}'.format(rsu_client.lane_num, msg_str, rsu_client.rsu_heartbeat_time, datetime.now())) DBOPeration.update_rsu_heartbeat(rsu_client) # 心跳更新入库 continue elif msg_str[8: 12] == 'b201': logger.error('射频初始化异常: {}'.format(msg_str)) break elif msg_str[8: 12] == 'b202': logger.error('PSAM卡初始化异常或无卡: {}'.format(msg_str)) break elif msg_str[8: 10] == 'b4': logger.info('检测到车辆信息') logger.info(msg=msg_str) b4_info = rsu_client.command_recv_set.parse_b4(msg_str) plate_no = b4_info['VehicleLicencePlateNumber'] plate_color = b4_info['VehicleLicencePlateColor'] # TODO 待删 # if (plate_no == 'ffffffffffffffffffffffff' and plate_color == 'ffff') or \ # (plate_no == '000000000000000000000000' and plate_color == '0000'): # 测试卡 # logger.info('测试卡:{}'.format(msg_str)) # logger.info('b4信息: {}'.format(json.dumps(b4_info))) # plate_no, plate_color = 'd4c141313131313100000000', '0000' plate_no = CommonUtil.parse_plate_code(plate_no) logger.info('车牌号: {}, 车颜色:{}'.format(plate_no, plate_color)) park_code = rsu_client.rsu_conf['park_code'] # 停车场 if plate_no: # 上传车辆信息 threading.Thread(target=ThirdEtcApi.upload_vehicle_plate_no, args=(park_code, plate_no, plate_color)).start() else: logger.error('读到的车牌号为空。。。。。。。') # TODO 该处为测试,待删,扣所有obu的车费 if plate_no: plate_no_test = '鲁L12345' query_vehicle_owe: VehicleOweOrm = DBClient.query_vehicle_owe('鲁L12345', '0000') # 查询欠费车辆信息 query_vehicle_owe.plate_no = plate_no else: query_vehicle_owe = None # TODO 待恢复 # query_vehicle_owe: VehicleOweOrm = DBClient.query_vehicle_owe(plate_no, plate_color) # 查询欠费车辆信息 if query_vehicle_owe: # 如果车辆欠费,开启扣费 logger.info('-------------车牌号:{}, 车颜色:{} 欠费,开始扣费----------'.format(plate_no, plate_color)) # etc_result = rsu_client.fee_deduction(body) etc_result = EtcToll.toll(query_vehicle_owe, rsu_client) if etc_result['flag'] is True: logger.info('------------------------扣费成功--------------------------') # TODO 后续成功扣费后需要删除数据库中的数据 else: logger.error('------------------------扣费失败--------------------------') else: logger.info('车牌号:{}, 车颜色:{} 没有欠费'.format(plate_no, plate_color)) else: logger.info('lane_num:{} 接收到指令: {}'.format(rsu_client.lane_num, msg_str)) if not msg_str: time.sleep(3)
def fee_deduction(self, obu_body: ETCRequestInfoOrm): """ etc扣费, 正常扣费指令流程, 耗时大约700ms c0->b0->b2->c1->b3->c1->b4->c6->b5,其中 c0: 发送初始化指令 --> init_rsu()中已经初始化 b0: 接收设备状态信息帧 --> nit_rsu()中已经初始化 b2: 接收电子标签信息帧 c1: 发送继续交易指令 b3: 接收设备车辆信息帧 c1: 发送继续交易指令 b4: 接收速通卡信息帧 c6: 发送消费交易指令,出口消费写过站 b5: 接收交易信息帧,表示此次交易成功结束 :return: """ current_time = time.time() logger.info( '==========================================开始扣费==========================================' ) logger.info('订单号:{}, 车牌号:{}, 车牌颜色:{}'.format( obu_body.trans_order_no, obu_body.plate_no, obu_body.plate_color_code)) logger.info('最新obu检查时间差:{}'.format(current_time - self.detect_obu_time_latest)) # self.etc_charge_flag=True表示交易成功,self.etc_charge_flag=False表示交易失败 self.etc_charge_flag = False obuid = None result = dict(flag=False, data=None, error_msg="error") while True: # 接收数据 try: msg_bytes = self.recv_msg() except: logger.error('搜索obu超时') result['error_msg'] = '没有搜索到obu' return result # 指令转义 msg_str = msg_bytes.hex().replace('fe01', 'ff').replace('fe00', 'fe') # 字节转十六进制 if msg_str[8:10] == 'b2': logger.info('接收b2心跳数据:{}'.format(msg_str)) if msg_str[10:12] == '00': self.rsu_heartbeat_time = datetime.datetime.now() else: logger.error('天线心跳出现问题') return result elif msg_str[8:10] == 'b4': info_b4 = self.command_recv_set.parse_b4(b4=msg_str) logger.info('接收b4: {}'.format(info_b4)) obuid = info_b4['ObuID'] # TODO 测试卡,正式环境要检测obu_status obu_status = info_b4['OBUStatus'] # if obu_status == '01': # result['error_msg'] = 'obu非法拆卸!!!' # return result black_list_status = info_b4['BlackListStatus'] if black_list_status == '01': result['error_msg'] = '属于黑名单车辆!!!' return result # 当前日期 data_of_now = CommonUtil.timestamp_format( int(time.time()), '%Y%m%d') if data_of_now > info_b4['DataOfExpire']: result['error_msg'] = '过期日期:{}'.format( info_b4['DataOfExpire']) return result plate_no = info_b4['VehicleLicencePlateNumber'] try: plate_no = CommonUtil.parse_plate_code(plate_no).replace( '测A', '鲁L') except: logger.error(traceback.format_exc()) result['error_msg'] = '车牌号解析错误:{}'.format(repr(plate_no)) return result plate_color = str(int(info_b4['VehicleLicencePlateColor'], 16)) if (plate_no != obu_body.plate_no) or ( plate_color != obu_body.plate_color_code): error_msg = "车牌号或车颜色不匹配: 监控获取的车牌号:%s, 车颜色:%s; obu获取的车牌号:%s,车颜色:%s" % ( obu_body.plate_no, obu_body.plate_color_code, plate_no, plate_color) logger.error(error_msg) result['error_msg'] = error_msg return result consume_money = CommonUtil.etcfee_to_hex( obu_body.deduct_amount) # 扣费金额 purchase_time = CommonUtil.timestamp_format( int(time.time()), format='%Y%m%d%H%M%S') station_info = info_b4['StationInfo'] entry_time = CommonUtil.timestamp_format(int(time.time()) - 100, format='%Y%m%d%H%M%S') c6 = CommandSendSet.combine_c6(obuid=obuid, consume_money=consume_money, purchase_time=purchase_time, station_info=station_info, entry_time=entry_time) logger.info('b4后发送c6指令:{}'.format(c6)) self.socket_client.send(bytes.fromhex(c6)) elif msg_str[8:10] == 'b5': self.command_recv_set.parse_b5(b5=msg_str) logger.info('c6后接收b5指令:{}'.format(msg_str)) if self.command_recv_set.info_b5['ErrorCode'] == '01': logger.error('读取obu卡超时。。。。。。。。。。。。') # logger.info('b5后发送c6指令:{}'.format(c6)) # self.socket_client.send(bytes.fromhex(c6)) continue c1 = CommandSendSet.combine_c1(obuid) logger.info('b5后发送c1指令:{}'.format(c1)) self.socket_client.send(bytes.fromhex(c1)) self.etc_charge_flag = True self.monitor_rsu_status_on = True # 打开心跳检测 result['flag'] = True result['data'] = self.command_recv_set result['error_msg'] = None return result else: logger.info('未能解析到的指令:{}'.format(msg_str)) time.sleep(1)
def handle_data(self, body: ETCRequestInfoOrm): """ 处理self.command_recv_set,也就是收到的天线的信息 :param body: 接收到的数据 :return: """ # 结果存储 result = dict(flag=False, data=None, error_msg=None) # TODO 待待删打印信息 # self.command_recv_set.print_obu_info() # 判断card_net和card_sn 物理卡号是否存在于黑名单中 card_sn_in_blacklist_flag, error_msg = self.card_sn_in_blacklist() # 物理卡号存在于黑名单中直接返回 if card_sn_in_blacklist_flag: result['error_msg'] = error_msg return result # 入场时间戳格式化 yyyyMMddHHmmss entrance_time = CommonUtil.timestamp_format(body.entrance_time, format='%Y%m%d%H%M%S') # 交易时间格式化(yyyyMMddHHmmss) # exit_time = CommonUtil.timestamp_format(body.exit_time, format='%Y%m%d%H%M%S') exit_time = self.command_recv_set.info_b5['TransTime'] exit_time_stamp = CommonUtil.str_to_timestamp(timestr=exit_time, format='%Y%m%d%H%M%S') # 计算停车时长 park_record_time = CommonUtil.time_difference(body.entrance_time, exit_time_stamp) # 交易后余额 balance = self.command_recv_set.info_b5['CardRestMoney'] # 交易前余额 1999918332 单位分 trans_before_balance = self.command_recv_set.info_b4['CardRestMoney'] if CommonConf.ETC_CONF_DICT['debug'] == 'true': deduct_amount = 0.01 else: deduct_amount = body.deduct_amount card_net_no = self.command_recv_set.info_b4['CardNetNo'] # 网络编号 # ETC 卡片类型 16 储值卡 17 记账卡,转换成十进制分别对应22,23 card_type = str(int(self.command_recv_set.info_b4['CardType'], 16)) # PSAM 卡编号 10字节 try: psam_id = self.command_recv_set.info_ba['PsamSN'] except: print(self.command_recv_set.info_ba) # card_sn 物理卡号 8字节 card_sn = psam_id[4:] # TODO 待确认 logger.info('ETC 卡片类型(22:储值卡;23:记账卡): {}'.format(card_type)) params = dict( algorithm_type="1", # TODO 金额位数待确定 balance=CommonUtil.hex_to_etcfee(balance, unit='fen'), # 交易后余额 # TODO 待确认 card_net_no=card_net_no, # 网络编号 card_rnd=CommonUtil.random_str(8), # 卡内随机数 card_serial_no=self.command_recv_set. info_b5['ETCTradNo'], # 卡内交易序号 card_sn=card_sn, # self.command_recv_set.info_b4['CardID'], # "1030230218354952",ETC 支付时与卡物理号一致;非 ETC 时上传车牌号 card_type=card_type, # "23", # ETC 卡片类型(22:储值卡;23:记账卡) charging_type="0", # 扣费方式(0:天线 1:刷卡器) deduct_amount=CommonUtil.yuan_to_fen(deduct_amount), # 扣款金额 device_no=self.rsu_conf['device_no'], # 设备号 device_type=self.rsu_conf['device_type'], # 设备类型(0:天线;1:刷卡器;9:其它) discount_amount=CommonUtil.yuan_to_fen( body.discount_amount), # 折扣金额 entrance_time=entrance_time, # 入场时间 yyyyMMddHHmmss exit_time=exit_time, # 交易时间(yyyyMMddHHmmss) issuer_identifier=self.command_recv_set. info_b4['IssuerIdentifier'].upper(), # 发行商代码 obu_id=self.command_recv_set.info_b4['ObuID'].upper(), # OBU 序号编码 park_code=self.rsu_conf['park_code'], # 车场编号 park_record_time=park_record_time, # 停车时长,时间精确到秒, 6小时50分钟 plate_color_code=body. plate_color_code, # 车牌颜色编码 0:蓝色、1:黄色、2:黑色、3:白色、4:渐变绿色、5:黄绿双拼、6:绿色、7:蓝白渐变 plate_no=body.plate_no, # 车牌号码 "皖LX4652", plate_type_code=body.plate_type_code, # 车辆类型编码 0:小车 1:大车 2:超大车 psam_id=psam_id, # PSAM 卡编号 "37010101000000295460" psam_serial_no=self.command_recv_set. info_b5['PSAMTransSerial'], # PSAM 流水号 "00005BA2", receivable_total_amount=CommonUtil.yuan_to_fen( body.receivable_total_amount), # 应收金额 serial_number=self.command_recv_set. info_b4['ApplySerial'], # 合同序列号"340119126C6AFEDE" tac=self.command_recv_set.info_b5['TAC'], # 交易认证码 terminal_id=self.command_recv_set. info_b5['PsamTerminalID'], # 终端编号 trans_before_balance=CommonUtil.hex_to_etcfee( trans_before_balance, unit='fen'), # 交易前余额 1999918332 单位分 trans_order_no=body.trans_order_no, # 交易订单号 "6711683258167489287" # TODO 待确认 trans_type='09', # 交易类型(06:传统;09:复合) vehicle_type=str(int( self.command_recv_set.info_b4['VehicleClass'])) # 收费车型 ) etc_deduct_info_dict = {"method": "etcPayUpload", "params": params} # 业务编码报文json格式 etc_deduct_info_json = json.dumps(etc_deduct_info_dict, ensure_ascii=False) # TODO 进行到此步骤,表示etc扣费成功,调用强哥接口 payTime = CommonUtil.timeformat_convert(exit_time, format1='%Y%m%d%H%M%S', format2='%Y-%m-%d %H:%M:%S') res_etc_deduct_notify_flag = ThirdEtcApi.etc_deduct_notify( self.rsu_conf['park_code'], body.trans_order_no, body.discount_amount, body.deduct_amount, payTime) if res_etc_deduct_notify_flag: # 接收到强哥返回值后,上传etc扣费数据 upload_flag, upload_fail_count = ThirdEtcApi.etc_deduct_upload( etc_deduct_info_json) db_engine, db_session = create_db_session( sqlite_dir=CommonConf.SQLITE_DIR, sqlite_database='etc_deduct.sqlite') # etc_deduct_info_json入库 DBClient.add(db_session=db_session, orm=ETCFeeDeductInfoOrm( id=CommonUtil.random_str(32).lower(), trans_order_no=body.trans_order_no, etc_info=etc_deduct_info_json, upload_flag=upload_flag, upload_fail_count=upload_fail_count)) db_session.close() db_engine.dispose() # 清除收集到的b2,b3, b4, b5 self.command_recv_set.clear_info_b2345() result['flag'] = True result['data'] = params return result
def fee_deduction(self, obu_body: ETCRequestInfoOrm): """ etc扣费, 正常扣费指令流程, 耗时大约700ms c0->b0->b2->c1->b3->c1->b4->c6->b5,其中 c0: 发送初始化指令 --> init_rsu()中已经初始化 b0: 接收设备状态信息帧 --> nit_rsu()中已经初始化 b2: 接收电子标签信息帧 c1: 发送继续交易指令 b3: 接收设备车辆信息帧 c1: 发送继续交易指令 b4: 接收速通卡信息帧 c6: 发送消费交易指令,出口消费写过站 b5: 接收交易信息帧,表示此次交易成功结束 :return: """ current_time = time.time() logger.info( '==========================================开始扣费==========================================' ) logger.info('订单号:{}, 车牌号:{}, 车牌颜色:{}'.format( obu_body.trans_order_no, obu_body.plate_no, obu_body.plate_color_code)) # self.etc_charge_flag=True表示交易成功,self.etc_charge_flag=False表示交易失败 self.etc_charge_flag = False obuid = None result = dict(flag=False, data=None, error_msg=None) # 统计扣费期间b2的数量,有时会遇到b2->c1-b2->c1->b2->c1这样的无限循环 count_b2 = 0 # 设置超时时间 while True: # 接收数据 try: msg_bytes = self.recv_msg() except: logger.error('搜索obu超时') result['error_msg'] = '没有搜索到obu' return result # 指令转义 msg_str = CommonUtil.transfer_recv_command( msg_bytes.hex()) # 字节转十六进制 logger.info('接收数据: {}'.format(repr(msg_str))) # b2 电子标签信息帧 if msg_str[6:8] == 'b2': if msg_str[8:16] == 'ffffffff': # 'ffffffff' 表示心跳 logger.info('心跳') elif msg_str[68:72] == '2001': info_b2 = self.command_recv_set.parse_b2(msg_str) # 解析b2指令 # 过期日期 data_of_expire = self.command_recv_set.info_b2[ 'DataOfExpire'] # 当前日期 data_of_now = CommonUtil.timestamp_format( int(time.time()), '%Y%m%d') if data_of_now > data_of_expire: result['error_msg'] = '过期日期:{}'.format(data_of_expire) return result # 电子标签mac地址 obuid = info_b2['OBUID'] # 获取c1指令 c1 = CommandSendSet.combine_c1( obuid, obu_div_factor=self.rsu_conf['obu_div_factor']) logger.info('b2后发送c1指令:%s' % (c1)) self.socket_client.send(bytes.fromhex(c1)) count_b2 += 1 if count_b2 > 3: result['error_msg'] = '无法扣费,陷入b2->c1-b2->c1->b2->c1循环' return result else: result['error_msg'] = 'obu卡有问题,obu状态码: {}'.format( msg_str[68:72]) return result # b3 车辆信息帧 elif msg_str[6:8] == 'b3': if msg_str[16:18] == '00': # obu信息帧状态执行码,取值00正常 self.command_recv_set.parse_b3(msg_str) # 解析b3指令 # TODO 车牌号,车颜色 需要校验, 不匹配需要返回 plate_no = self.command_recv_set.info_b3[ 'VehicleLicencePlateNumber'] try: plate_no = CommonUtil.parse_plate_code( plate_no).replace('测A', '鲁L') except: logger.error(traceback.format_exc()) logger.error('车牌号解析错误:{}'.format(repr(plate_no))) # 车牌颜色 obu_plate_color = str( int( self.command_recv_set. info_b3['VehicleLicencePlateColor'], 16)) # obu车颜色 if (obu_body.plate_no != plate_no): error_msg = '车牌号不匹配,监控获取的车牌号:{},obu获取的车牌号:{}'.format( obu_body.plate_no, plate_no) result['error_msg'] = error_msg return result elif int( obu_body.plate_color_code) != int(obu_plate_color): error_msg = "车牌号或车颜色不匹配: 监控获取的车牌号:%s, 车颜色:%s; obu获取的车牌号:%s,车颜色:%s" % ( obu_body.plate_no, obu_body.plate_color_code, plate_no, obu_plate_color) logger.error(error_msg) # todo 颜色不对,继续扣费 if obuid is None: # 如果没有获取到obuid,继续 logger.error( 'obuid is none =====================+++++++++++++++++++' ) continue # 再次获取c1指令并发送 c1 = CommandSendSet.combine_c1( obuid, obu_div_factor=self.rsu_conf['obu_div_factor']) logger.info('b3后发送c1指令:%s' % (c1, )) self.socket_client.send(bytes.fromhex(c1)) else: # 状态执行码不正常,发送c2指令,终止交易 c2 = CommandSendSet.combine_c2(obuid, stop_type='01') # logger.info('发送c2指令,终止交易: %s' % (c2,)) self.socket_client.send(bytes.fromhex(c2)) result['error_msg'] = '终止交易' return result # b4 速通卡信息帧 elif msg_str[6:8] == 'b4': if msg_str[16:18] == '00': # 状态执行码,00说明后续速通卡信息合法有效 logger.info('接收b4指令: {}'.format(msg_str)) self.command_recv_set.parse_b4(msg_str) # 解析b4指令 logger.info('b4解析后:{}'.format( json.dumps(self.command_recv_set.info_b4, ensure_ascii=False))) # 获取并发送c6消费交易指令 if CommonConf.ETC_CONF_DICT['debug'] == 'true': deduct_amount = CommonUtil.etcfee_to_hex(0.01) else: deduct_amount = CommonUtil.etcfee_to_hex( obu_body.deduct_amount) # 扣款额,高字节在前 purchase_time = CommonUtil.timestamp_format( int(time.time())) station = msg_str[132:212] # 过站信息,40个字节 c6 = CommandSendSet.combine_c6( obuid, card_div_factor=self.rsu_conf['obu_div_factor'], reserved='00000000', deduct_amount=deduct_amount, purchase_time=purchase_time, station=station) # 针对红门轮训模式 if CommonUtil.deduct_stop(obu_body.create_time): error_msg = '时间超时,终止etc交易' logger.info(error_msg) result['error_msg'] = error_msg return result logger.info('b4后发送c6指令,消费交易,出口消费写过站: {}, 其中扣除费用{}'.format( c6, obu_body.deduct_amount)) self.socket_client.send(bytes.fromhex(c6)) else: result['error_msg'] = 'b4指令有问题' return result # b5 交易信息帧,表示此次交易成功结束 elif msg_str[6:8] == 'b5': if msg_str[16:18] == '00': # 状态执行码,00说明正常 self.command_recv_set.parse_b5(msg_str) # 解析b5指令 # 获取并发送c1继续交易指令 c1 = CommandSendSet.combine_c1( obuid, obu_div_factor=self.rsu_conf['obu_div_factor']) logger.info('b5后发送c1指令:%s, 电子标签mac地址 obuid = %s' % (c1, obuid)) self.socket_client.send(bytes.fromhex(c1)) self.etc_charge_flag = True result['flag'] = True result['data'] = self.command_recv_set return result else: result['error_msg'] = 'b5指令有问题' return result elif not msg_str: result['error_msg'] = '接收到的指令为空' return result elif not msg_str.startswith('ffff'): logger.info('忽略指令: {}'.format(msg_str)) else: logger.error('未能解析的指令:%s' % (msg_str))