# -*- coding: utf-8 -*- # !/usr/bin/env python import datetime import logging from apilib.monetary import RMB, VirtualCoin from apilib.utils_datetime import to_datetime from apps.web.constant import Const, DeviceCmdCode, DEALER_CONSUMPTION_AGG_KIND from apps.web.core.exceptions import ServiceException from apps.web.core.networking import MessageSender from apps.web.device.models import Group, Device from apps.web.eventer import EventBuilder from apps.web.eventer.base import WorkEvent from apps.web.user.models import ServiceProgress, Card, CardRechargeOrder, MyUser logger = logging.getLogger(__name__) card_is_normal = 1 card_not_in_db = 2 card_is_forzen = 3 card_has_not_order = 4 # IC卡适用 card_less_balance = 5 # ID卡适用 card_type_is_ic = 6 # IC卡不允许当做ID卡使用 class builder(EventBuilder): def __getEvent__(self, device_event): event_data = self.deviceAdapter.analyze_event_data(device_event['data']) if event_data is None: return None if event_data['fun_code'] in [0x35, 0x2C]: return ChargingWorkEvent(self.deviceAdapter, event_data) class ChargingWorkEvent(WorkEvent): def update_balance_for_IC_card(self, card, balance): card.balance = balance try: card.save() except Exception, e: pass def __translate_reason(self, cause, chrmt): if cause == 0: if chrmt == 1: return u'订购时间已经用完。' elif chrmt == 2: return u'订购电量已经用完。' elif cause == 1: return u'用户手工停止了充电' elif cause == 2: return u'充电满了,自动停止' elif cause == 3: return u'超功率自停' elif cause == 4: return u'远程断电' elif cause == 5: return u'刷卡断电' elif cause == 0x0B: return u'设备或是端口出现问题,被迫停止' return u'充电结束。' def response_id_card(self, msgDict): data = msgDict online_card_cost = RMB(self.device['otherConf'].get('online_card_cost', 10) * 0.1) # 元 online_card_val = self.device['otherConf'].get('online_card_val', 240) online_card_chrmt = self.device['otherConf'].get('online_card_chrmt', 1) mqtt_data = { 'fun_code': 0x35, 'port_id': data['port_id'], 'card_no': data['card_no'], 'chrmt': online_card_chrmt, 'result': card_not_in_db, 'balance': 0, 'amount': 0, } cardNo = '{}'.format(int(data['card_no'], 16)) Card.record_dev_card_no(self.device['devNo'], cardNo) card = self.update_card_dealer_and_type(cardNo) # 如果没有卡,直接返回 # cardNo,portId,balance,result if card is None: return self.send_mqtt(mqtt_data) if card.frozen: data.update( {'result': card_is_forzen} ) return self.send_mqtt(mqtt_data) if card.cardType == 'IC': # 如果以前是离线卡,只能用于离线卡,需要提醒用户 data.update( {'result': card_type_is_ic} ) return self.send_mqtt(mqtt_data) #: 首先检查订单,并进行充值 #: 用户首先在手机客户端充值,需要这个时刻刷卡上报事件将充值的订单同步上来 card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id)) if card_recharge_order: result = self.recharge_id_card(card=card, rechargeType='append', order=card_recharge_order) card.reload() if card.balance >= online_card_cost: lineInfo = Device.get_dev_control_cache(self.device.devNo).get(str(data['port_id']), {}) if lineInfo.get('status') == Const.DEV_WORK_STATUS_WORKING: return consumeDict = { 'chargeIndex': data['port_id'] } if online_card_chrmt == 1: consumeDict.update({'needTime': online_card_val}) elif online_card_chrmt == 2: consumeDict.update({'needElec': online_card_val}) online_card_val = online_card_val * 100 mqtt_data = { 'fun_code': 0x35, 'port_id': data['port_id'], 'card_no': data['card_no'], 'chrmt': online_card_chrmt, 'balance': int(card.balance - online_card_cost) * 10, 'result': card_is_normal, 'amount': int(online_card_val), } result = self.send_mqtt(mqtt_data) if result['rst'] == 0: self.consume_money_for_card(card, online_card_cost) orderNo, cardOrderNo = self.record_consume_for_card(card, online_card_cost, servicedInfo=consumeDict) lineInfo = { "isStart": True, "status": Const.DEV_WORK_STATUS_WORKING, "openId": card.openId, "cardNo": card.cardNo, "coins": round(online_card_cost, 2), "startTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "consumeType": "card", "port": data['port_id'], "orderNo": orderNo, "cardOrderNo": cardOrderNo } Device.update_port_control_cache(self.device.devNo, lineInfo) ServiceProgress.register_card_service( self.device, data['port_id'], card, consumeOrder={ "orderNo": orderNo, "cardOrderNo": cardOrderNo, } ) else: data.update( { 'balance': int(card.balance), 'result': card_less_balance, 'amount': int(online_card_cost), } ) return self.send_mqtt(data) def do(self, **args): funCode = self.event_data['fun_code'] if funCode == 0x35: self.response_id_card(self.event_data) if funCode == 0x2C: self._do_finish() def send_mqtt(self, data, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, timeout=10): """ 发送mqtt 指令默认210 返回data """ payload = {'IMEI': self.device.devNo, 'data': data} result = MessageSender.send(self.device, cmd, payload=payload, timeout=timeout) if not result.has_key('rst'): raise ServiceException({'result': 2, 'description': u'报文异常'}) if result['rst'] == -1: raise ServiceException({'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'}) if result['rst'] == 1: raise ServiceException({'result': 2, 'description': u'串口通讯失败,您稍候再试,或者联系客服'}) if result['rst'] == 2: raise ServiceException({'result': 2, 'description': u'串口通讯失败,您稍候再试,或者联系客服'}) else: return result def _do_finish(self): start_type = self.event_data.get('start_type') try: if start_type == 0: # 离线卡 self._offline_card_finish() elif start_type == 1: # 在线卡 cardHex = self.event_data.get('card_no') cardNo = '{}'.format(int(cardHex, 16)) Card.record_dev_card_no(self.device['devNo'], cardNo) card = self.update_card_dealer_and_type(cardNo) # 如果没有卡,直接返回 # cardNo,portId,balance,result if card is None: return self._online_card_finish(card) elif start_type == 2: # 扫码 self._net_pay_finish() except: import traceback logger.info(traceback.format_exc()) Device.clear_port_control_cache(self.device.devNo, str(self.event_data['port_id'])) def _offline_card_finish(self): print(self.event_data) pass def _online_card_finish(self,card): leftTime = self.event_data.get('left_time') leftElec = round(self.event_data.get('left_elec', 0) * 0.001, 3) reason = self.event_data.get('reason') port = self.event_data.get('port_id') lineInfo = Device.get_dev_control_cache(self.device.devNo).get(str(port), {}) if 'coins' not in lineInfo: logger.debug('port cache has no coins. no order in port {}'.format(lineInfo.get('port'))) return orderNo = lineInfo.get('orderNo') coins = VirtualCoin(lineInfo.get('coins')) openId = lineInfo.get('openId') chrmt = self.event_data.get('chrmt') or lineInfo.get('chmrt') startTime = to_datetime(lineInfo['startTime']) nowTime = datetime.datetime.now() if startTime > nowTime: # 如果web服务器时间和事件监控服务器时间不一致,导致开始时间比事件时间还大 usedTime = 0 else: usedTime = int(round(((nowTime - startTime).total_seconds() / 60.0))) extra = [] consumeDict = { 'port': port, 'reason': self.__translate_reason(reason, chrmt), DEALER_CONSUMPTION_AGG_KIND.COIN: coins.mongo_amount, } refundProtectionTime = lineInfo.get('refundProtectionTime', 5) backCoins = VirtualCoin(0) usedFee = coins if usedTime < refundProtectionTime: backCoins = coins usedFee = VirtualCoin(0) else: if self.device.is_auto_refund: if chrmt == 1: needTime = lineInfo.get('needTime') if leftTime == 0xFFFF: actualNeedTime = 0 backCoins = coins usedFee = VirtualCoin(0) else: actualNeedTime = usedTime + leftTime backCoins = self.get_backCoins(coins, leftTime, actualNeedTime) usedFee = coins - backCoins consumeDict.update({ 'needTime': needTime, 'actualNeedTime': actualNeedTime, 'leftTime': leftTime, }) elif chrmt == 2: needElec = round(lineInfo.get('needElec', 0) * 0.001, 3) if leftElec > needElec: leftElec = needElec elec = 0 backCoins = coins usedFee = VirtualCoin(0) else: elec = round((needElec - leftElec) * 0.001, 3) backCoins = self.get_backCoins(coins, leftElec, needElec) usedFee = coins - backCoins consumeDict.update({ 'needElec': needElec, 'elec': elec, 'leftElec': leftElec, }) else: pass ServiceProgress.update_progress_and_consume_rcd( self.device['ownerId'], { 'open_id': openId, 'device_imei': self.device['devNo'], 'port': port, 'isFinished': False }, consumeDict) user = MyUser.objects(openId=openId, groupId=self.device['groupId']).first() group = Group.get_group(self.device['groupId']) # 处理退费 与推送 if backCoins > VirtualCoin(0): extra.append({u'消费明细': u'消费{}金币,退费{}金币'.format(usedFee, backCoins)}) self.refund_money_for_card(backCoins, str(card.id), orderNo) else: extra.append({u'消费明细': u'消费{}(金币)'.format(usedFee)}) self.notify_user_service_complete( service_name='充电', openid=card.managerialOpenId, port=str(port), address=group.address, reason=self.event_data.get('reasonDesc'), finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), extra=extra) def _net_pay_finish(self): leftTime = self.event_data.get('left_time') leftElec = round(self.event_data.get('left_elec', 0) * 0.001, 3) reason = self.event_data.get('reason') port = self.event_data.get('port_id') lineInfo = Device.get_dev_control_cache(self.device.devNo).get(str(port), {}) if 'coins' not in lineInfo: logger.debug('port cache has no coins. no order in port {}'.format(lineInfo.get('port'))) Device.clear_port_control_cache(self.device.devNo, (port)) return coins = VirtualCoin(lineInfo.get('coins')) openId = lineInfo.get('openId') chrmt = self.event_data.get('chrmt') or lineInfo.get('chmrt') startTime = to_datetime(lineInfo['startTime']) nowTime = datetime.datetime.now() if startTime > nowTime: # 如果web服务器时间和事件监控服务器时间不一致,导致开始时间比事件时间还大 usedTime = 0 else: usedTime = int(round(((nowTime - startTime).total_seconds() / 60.0))) extra = [] consumeDict = { 'port': port, 'reason': self.__translate_reason(reason, chrmt), DEALER_CONSUMPTION_AGG_KIND.COIN: coins.mongo_amount, } refundProtectionTime = self.device['otherConf'].get('refundProtectionTime', 5) backCoins = VirtualCoin(0) usedFee = coins if usedTime < refundProtectionTime: backCoins = coins usedFee = VirtualCoin(0) else: if self.device.is_auto_refund: if chrmt == 1: needTime = lineInfo.get('needTime') if leftTime == 0xFFFF: actualNeedTime = 0 backCoins = coins usedFee = VirtualCoin(0) else: actualNeedTime = usedTime + leftTime backCoins = self.get_backCoins(coins, leftTime, actualNeedTime) usedFee = coins - backCoins consumeDict.update({ 'needTime': needTime, 'actualNeedTime': actualNeedTime, 'leftTime': leftTime, }) elif chrmt == 2: needElec = round(lineInfo.get('needElec', 0) * 0.001, 3) if leftElec > needElec: leftElec = needElec elec = 0 backCoins = coins usedFee = VirtualCoin(0) else: elec = round((needElec - leftElec) * 0.001, 3) backCoins = self.get_backCoins(coins, leftElec, needElec) usedFee = coins - backCoins consumeDict.update({ 'needElec': needElec, 'elec': elec, 'leftElec': leftElec, }) else: pass ServiceProgress.update_progress_and_consume_rcd( self.device['ownerId'], { 'open_id': openId, 'device_imei': self.device['devNo'], 'port': port, 'isFinished': False }, consumeDict) user = MyUser.objects(openId=openId, groupId=self.device['groupId']).first() group = Group.get_group(self.device['groupId']) # 处理退费 与推送 if backCoins > VirtualCoin(0): extra.append({u'消费明细': u'消费{}金币,退费{}金币'.format(usedFee, backCoins)}) self.refund_net_pay(user, lineInfo, RMB(0), backCoins, consumeDict, False) else: extra.append({u'消费明细': u'消费{}(金币)'.format(usedFee)}) self.notify_user_service_complete( service_name='充电', openid=user.managerialOpenId, port=str(port), address=group.address, reason=self.event_data.get('reasonDesc'), finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), extra=extra)