# -*- coding: utf-8 -*- # !/usr/bin/env python import datetime import logging import random import time from typing import TYPE_CHECKING from apilib.monetary import sum_rmb, RMB, VirtualCoin, Ratio from apilib.utils_datetime import to_datetime from apps.web.common.models import District from apps.web.constant import Const, FAULT_CODE, FAULT_LEVEL, DEALER_CONSUMPTION_AGG_KIND from apps.web.core.accounting import Accounting from apps.web.core.helpers import ActionDeviceBuilder from apps.web.dealer.models import Dealer from apps.web.device.models import PortReport, Part, Group, Device from apps.web.device.timescale import FluentedEngine from apps.web.eventer import EventBuilder from apps.web.eventer.base import FaultEvent, WorkEvent from apps.web.south_intf.delixi import DelixiNorther from apps.web.south_intf.liangxi_fire import LiangXiXiaoFang from apps.web.south_intf.platform import notify_event_to_north, notify_event_to_north_v2, \ handle_and_notify_event_to_north_Dc from apps.web.south_intf.yuhuan_fire import YuhuanNorther from apps.web.south_intf.zhejiang_fire import send_event_to_zhejiang from apps.web.user.models import VCardConsumeRecord, ServiceProgress, UserVirtualCard, CardRechargeOrder, MyUser, \ Redpack, RechargeRecord from apps.web.user.models import Card from apps.web.core.adapter.base import fill_2_hexByte if TYPE_CHECKING: from apps.web.device.models import GroupDict logger = logging.getLogger(__name__) class builder(EventBuilder): def __getEvent__(self, device_event): if 'data' not in device_event: return None event_data = self.deviceAdapter.analyze_event_data(device_event['data']) if event_data is None or 'cmdCode' not in event_data: return None if event_data['cmdCode'] == 'C0': if event_data['FaultCode'] in [3,4]: return ZHIXIA2FaultEvent(self.deviceAdapter, event_data) else: return ZHIXIA2InductorEvent(self.deviceAdapter, event_data) return ChargingZHIXIA2WorkEvent(self.deviceAdapter, event_data) class ChargingZHIXIA2WorkEvent(WorkEvent): def do(self, **args): logger.info('dianchuan delixi charging event detected, devNo=%s,info=%s' % (self.device.devNo, self.event_data)) cmdCode = self.event_data['cmdCode'] devNo = self.device.devNo data = self.event_data if cmdCode == 'A0': # 向平台上传主板模块版本 try: Device.get_collection().update({'devNo':devNo},{'$set':{'boardNo':data['boardNo'], 'hardVersion':data['hardVersion'], 'softId':data['softId'], 'softVersion':data['softVersion']}}) devObj = Device.objects.get(devNo = devNo) otherConf = devObj.otherConf if 'cardFeeMode' not in otherConf: otherConf['cardFeeMode'] = 'time' if 'cardFeeValue' not in otherConf: otherConf['cardFeeValue'] = 180 if 'runMode' not in otherConf: otherConf['runMode'] = 1 if 'voice' not in otherConf: otherConf['voice'] = 4 if 'coinTime' not in otherConf: otherConf['coinTime'] = 180 if 'cardTime' not in otherConf: otherConf['cardTime'] = 180 if 'icMoney' not in otherConf: otherConf['icMoney'] = 10 if 'cardRefund' not in otherConf: otherConf['cardRefund'] = 1 if 'power1' not in otherConf: otherConf['power1'] = 3000 if 'power2' not in otherConf: otherConf['power2'] = 4000 if 'power3' not in otherConf: otherConf['power3'] = 5000 if 'power4' not in otherConf: otherConf['power4'] = 6000 if 'power5' not in otherConf: otherConf['power5'] = 7000 if 'power1ratio' not in otherConf: otherConf['power1ratio'] = 100 if 'power2ratio' not in otherConf: otherConf['power2ratio'] = 90 if 'power3ratio' not in otherConf: otherConf['power3ratio'] = 80 if 'power4ratio' not in otherConf: otherConf['power4ratio'] = 70 if 'power5ratio' not in otherConf: otherConf['power5ratio'] = 60 if 'autoStop' not in otherConf: otherConf['autoStop'] = 1 if 'fuchongPower' not in otherConf: otherConf['fuchongPower'] = 300 if 'fuchongTime' not in otherConf: otherConf['fuchongTime'] = 7200 if 'noPowerTime' not in otherConf: otherConf['noPowerTime'] = 60 if 'password' not in otherConf: otherConf['password'] = 8888 if 'temperature' not in otherConf: otherConf['temperature'] = 256 #FF 默认关闭 devObj.otherConf = otherConf devObj.save() self.deviceAdapter.reply_A0() except Exception,e: logger.error('update device info error,e=%s' % e) elif cmdCode == 'A8': try: self.deviceAdapter.reply_A8() except Exception,e: logger.error('reply A8 error=%s' % e) elif cmdCode == 'B1': if not data['portDict']: return for port,power in data['portDict'].items(): logger.info("insert power data into database") FluentedEngine().in_power_udp(devNo=devNo,port=str(port),ts=int(time.time()),power=power,voltage=None,current=None) elif cmdCode == 'B4': # 本地离线卡、投币启动充电 # if data['consumeType'] == 0: #投币 # Accounting.recordOfflineCoin(device=self.device, # report_ts=int(time.time()), # coins=int(data['chargedMoney']), # mode='uart', # port=data['port']) # elif data['consumeType'] == 1: #离线卡,现在不处理。离线卡不支持充值,也不支持离线卡的余额获取 # pass try: self.deviceAdapter.reply_B4(data['port']) except Exception,e: logger.error('reply B4 error=%s' % e) elif cmdCode == 'B6': # 本地刷在线卡 cardNo = data['cardNo'] consumeMoneyValue = data['money']/10.0 card = self.update_card_dealer_and_type(cardNo) # 首先检查订单,并进行充值 # 不存在卡的情况下 直接不回复 if not card or not card.openId or card.frozen: return # 有绑定的卡 并且卡没被冻结的情况下 # 刷新卡的金额 card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id)) result = self.recharge_id_card(card=card, rechargeType='append', order=card_recharge_order) card.reload() logger.info('cardNo=%s,openId=%s,result=%s,consumeMoneyValue=%s,info=%s' % ( cardNo, card.openId, result, consumeMoneyValue, self.event_data)) #回复,并启动充电 orderNo = self.deviceAdapter.make_dianchuan_order_no(devNo,data['port']) data = fill_2_hexByte(hex(data['port'])) data += orderNo[16::] otherConf = Device.objects.get(devNo = devNo).otherConf consumeType = otherConf.get('cardFeeMode','time') value = otherConf.get('cardFeeValue',180) if consumeType == 'time': data += '0003' needValue = value*consumeMoneyValue data += fill_2_hexByte(hex(int(needValue)),2) else: # 电量的单位需要乘1000 data += '0103' needValue = value*consumeMoneyValue*1000 data += fill_2_hexByte(hex(int(needValue)),2) data += data['cardNoStr'] balanceJiao = 0 if not card else int(card.balance.mongo_amount * 10) data += fill_2_hexByte(hex(balanceJiao),4) try: logger.info("card is <{}> balance is <{}> response res is <{}>".format(cardNo, card.balance.mongo_amount, data)) devInfo = self.deviceAdapter.remote_start_charge(data['port'], data) except Exception, e: logger.info('resp back error=%s' % e) return # 服务器就需要正式扣掉钱 fee = RMB(consumeMoneyValue) virtual_card = card.bound_virtual_card if virtual_card is not None: group = Group.get_group(self.device['groupId']) VCardConsumeRecord( orderNo=orderNo, openId=card.openId, cardId=str(virtual_card.id), dealerId=card.dealerId, devNo=self.device.devNo, devTypeCode = self.device.devTypeCode, devTypeName = self.device.devTypeName, logicalCode=self.device['logicalCode'], groupId=group['groupId'], address=group['address'], groupNumber=self.device['groupNumber'], groupName=group['groupName'], ).save() else: self.consume_money_for_card(card, fee) # 记录卡消费记录以及消费记录 servicedInfo = { 'orderNo': orderNo, 'money': self.event_data['fee'], 'coin': self.event_data['fee'], 'needValue': needValue, 'consumeTypeFromDev':consumeType, 'chargeIndex':data['port'] } orderNo, cardOrderNo = self.record_consume_for_card(card, fee,'',servicedInfo) # 记录当前服务的progress.没有上报端口号,所以先用-1标记,表示端口未知。端口等消费的时候会报上来 servicedInfo.update({'cardOrderNo': cardOrderNo}) ServiceProgress.register_card_service( self.device, data['port'], card, servicedInfo ) # 通知微信,已经扣费 self.notify_balance_has_consume_for_card(card, fee) # 更新端口状态 queryDict = {'device_imei': devNo,'port': data['port'], 'isFinished': False,'cardId': str(card.id), 'start_time': {'$gte': int(time.time()) - 3600}} rcds = ServiceProgress.get_collection().find(queryDict, {'consumeOrder': 1},sort=[('start_time', -1)]) allCoins = sum_rmb([rcd['consumeOrder']['coin'] for rcd in rcds]) newValue = {'cardId': str(card.id),'openId': card.openId,'coins': str(allCoins)} Device.update_port_control_cache(self.device.devNo, newValue) elif cmdCode == 'BB': # 上传结算订单 port = str(data['port']) # 收到报文后,先回复报文给设备 self.deviceAdapter.reply_BB(data['port']) try: # 这个lineInfo 就是缓存全部的信息 lineInfo = Device.clear_port_control_cache(devNo, port) if not lineInfo: logger.debug('get null control cache from {}'.format(repr(self.device))) return logger.debug('port<{}> cache is: {}'.format(port, str(lineInfo))) # 投币只记录下充了多少钱、IC卡的不处理 if data['consumeType'] == 0: Accounting.recordOfflineCoin(device=self.device, report_ts=int(time.time()), coins=int(data['chargedMoney']), mode='uart', port=data['port']) return elif data['consumeType'] in [1,4]:# 离线IC卡,按键启动 return if lineInfo.get('isApi', False) is True: self.event_data.update({'deviceCode': self.device['logicalCode']}) return handle_and_notify_event_to_north_Dc(self.device["devNo"], self.event_data) # 红包退费的判断处理 if 'redpackInfo' in lineInfo: for _info in lineInfo['redpackInfo']: redpack = Redpack.get_one(_info['redpackId']) redpackCoins = VirtualCoin(redpack['redpackCoins']) lineInfo['coins'] = float(VirtualCoin(lineInfo['coins']) - redpackCoins) redpackMoney = RMB(redpack['redpackMoney']) lineInfo['price'] = float(RMB(lineInfo['price']) - redpackMoney) logger.debug( 'redpack is <{}> redpack money is: {}; redpack coins is: {}'.format(redpack.get('id'), str(redpackMoney.amount), str(redpackCoins.amount))) if data['chargeType'] == 1: # 电量 return self.do_elec_finish(devNo, port, lineInfo, data) else: return self.do_time_finish(devNo, port, lineInfo, data) finally: notify_event_to_north_v2(self.device["devNo"], data) notify_event_to_north(self.dealer, self.device, level=Const.EVENT_NORMAL,desc=data['reason']) send_event_to_zhejiang(self.dealer, self.device, data) if self.event_data['reasonCode'] == '04': # 功率过载导致的断电,一条告警,一条恢复告警 YuhuanNorther.send_dev_event(self.device, '97', port) YuhuanNorther.send_dev_event(self.device, '98', port) elif cmdCode == 'C2': pass elif cmdCode == 'C7': self.deviceAdapter.reply_C7() def do_elec_finish(self, devNo, port, lineInfo, msgDict): """ 电川的板子 按电量退费 :return: """ logger.info("[{} do_elec_finish] devNo = {}, port = {}, lineInfo = {}, msgDict = {}, event = {}".format( self.__class__.__name__, devNo, port, lineInfo, msgDict, self.event_data)) if not self.dealer: logger.error( "[{} do_elec_finish] dealer {} is not found!".format(self.__class__.__name__, self.device.ownerId)) return # 提取事件信息 leftElec = msgDict.get("leftElec", 0) / 100.0 reasonStr = msgDict.get("reason",'') cardNo = msgDict.get("cardNo", None) usedTime = msgDict.get('duration') / 60.0 # 转成分钟 price = RMB(lineInfo.get('price', 0)) recvTime = to_datetime(self.recvTime) try: consumeDict = {"reason": reasonStr,"chargeIndex": port,'duration':usedTime,"uartData": msgDict.get('uartData', ''), 'port':msgDict['port'],'elec':msgDict['elec'],'orderNo':msgDict['orderNo'],'startTime':msgDict['startTime'],'endTime':msgDict['endTime'], 'chargeType':'elec','consumeTypeFromDev':msgDict['consumeType'],'leftElec':msgDict['leftElec'],'elec':msgDict['elec'],'reasonCode':msgDict['reasonCode'], } # 计算充电的时间 refundProtectionTime = self.device.get_other_conf_item('refundProtectionTime', 3) # 获取组信息 group = Group.get_group(self.device["groupId"]) coins = VirtualCoin(lineInfo.get("coins")) needElec = lineInfo.get("needElec") refundCoins = VirtualCoin(0) refundedMoney = RMB(0) if leftElec == int("FFFF", 16): refundCoins = VirtualCoin(coins) refundedMoney = RMB(price) leftElec = needElec spendElec = 0 else: if usedTime < refundProtectionTime: refundCoins = VirtualCoin(coins) refundedMoney = RMB(price) spendElec = 0 else: if leftElec > needElec: logger.error('left elec is bigger than need elec. something is wrong') leftElec = 0 spendElec = needElec - leftElec refundSwitch = self.device.is_auto_refund if refundSwitch: refundCoins = VirtualCoin(coins) * Ratio(leftElec / needElec) refundedMoney = RMB(price) * Ratio(leftElec / needElec) logger.debug('left elec = {}, duration = {}, need elec = {}, back coins = {}, refund cash = {}'.format( leftElec, usedTime, needElec, refundCoins, refundedMoney)) # 扫码的结束 if msgDict['consumeType'] == 2: if 'consumeRcdId' in lineInfo and lineInfo['consumeRcdId']: # 虚拟卡的结束 consumeRcdId = lineInfo['consumeRcdId'] vCardId = lineInfo.get("vCardId", "") vCard = UserVirtualCard.objects(id=vCardId).first() if not vCard: logger.info('can not find the vCard id = %s' % vCardId) return try: ServiceProgress.update_progress_and_consume_rcd( self.device.ownerId, { 'open_id': lineInfo['openId'], 'port': int(port), 'device_imei': self.device.devNo, 'isFinished':False }, consumeDict ) if refundCoins > VirtualCoin(0): vCardConsumeRcd = VCardConsumeRecord.objects.get(id=consumeRcdId) vCard.refund_quota(vCardConsumeRcd, needElec - spendElec, spendElec, refundCoins.mongo_amount) finally: user = MyUser.objects(openId=lineInfo['openId'], groupId=self.device.groupId).first() self.notify_user_service_complete( service_name=u'充电', openid=user.managerialOpenId if user else '', port=port, address=group['address'], reason=self.event_data['reason'], finished_time=recvTime.strftime('%Y-%m-%d %H:%M:%S'), extra=[ {u'虚拟卡号': vCard.cardNo} ] ) elif 'openId' in lineInfo and lineInfo['openId']: # 扫码使用金币启动 user = MyUser.objects(openId=lineInfo['openId'], groupId=self.device.groupId).first() try: is_cash = False if 'refundRMB_device_event' in self.device.owner.features and refundedMoney > RMB(0): is_cash = True consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.COIN: VirtualCoin(coins).mongo_amount}) if refundedMoney > RMB(0) or refundCoins > VirtualCoin(0): consumeDict.update( {DEALER_CONSUMPTION_AGG_KIND.COIN: (VirtualCoin(coins) - refundCoins).mongo_amount}) self.refund_net_pay(user, lineInfo, refundedMoney, refundCoins, consumeDict, is_cash) if self.device.bill_as_service_feature.on: serviceFee = round(spendElec * float(self.device.bill_as_service_feature.service_charge),2) elecFee = round(spendElec * float(self.device.bill_as_service_feature.elec_charge),2) consumeDict.update({"elec": spendElec, "elecFee": elecFee,"serviceFee":serviceFee}) ServiceProgress.update_progress_and_consume_rcd( self.device.ownerId, { 'open_id': lineInfo['openId'], 'port': int(port), 'device_imei': self.device.devNo, 'isFinished':False }, consumeDict) finally: extra = [] if DEALER_CONSUMPTION_AGG_KIND.REFUNDED_CASH in consumeDict: real_refund = RMB(consumeDict[DEALER_CONSUMPTION_AGG_KIND.REFUNDED_CASH]) if real_refund > RMB(0): extra.append({u'消费金额': '{}(元)'.format(RMB(price) - real_refund)}) extra.append({u'退款金额': '{}(元)'.format(real_refund)}) else: extra.append({u'消费金额': '{}(元)'.format(price)}) if self.device.bill_as_service_feature.on: elecCharge = round(spendElec * float(self.device.bill_as_service_feature.elec_charge),2) serviceCharge = round(spendElec * float(self.device.bill_as_service_feature.service_charge),2) extra.append({u'服务费金额': '{}(元)'.format(serviceCharge)}) extra.append({u'电费金额': '{}(元)'.format(elecCharge)}) elif DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS in consumeDict: real_refund = VirtualCoin(consumeDict[DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS]) if real_refund > VirtualCoin(0): extra.append({u'消费金额': '{}(金币)'.format(VirtualCoin(coins) - real_refund)}) extra.append({u'退款金额': '{}(金币)'.format(real_refund)}) else: extra.append({u'消费金额': '{}(金币)'.format(VirtualCoin(coins))}) if self.device.bill_as_service_feature.on: elecCharge = round(spendElec * float(self.device.bill_as_service_feature.elec_charge),2) serviceCharge = round(spendElec * float(self.device.bill_as_service_feature.service_charge),2) extra.append({u'服务费金额': '{}(金币)'.format(serviceCharge)}) extra.append({u'电费金额': '{}(金币)'.format(elecCharge)}) else: extra.append({u'消费金额': '{}(金币)'.format(VirtualCoin(coins))}) if self.device.bill_as_service_feature.on: elecCharge = spendElec * float(self.device.bill_as_service_feature.elec_charge) serviceCharge = spendElec * float(self.device.bill_as_service_feature.service_charge) extra.append({u'服务费金额': '{}(金币)'.format(serviceCharge)}) extra.append({u'电费金额': '{}(金币)'.format(elecCharge)}) self.notify_user_service_complete( service_name=u'充电', openid=user.managerialOpenId if user else '', port=port, address=group['address'], reason=self.event_data['reason'], finished_time=recvTime.strftime('%Y-%m-%d %H:%M:%S'), extra=extra) else: logger.error('not net pay rather user virtual card pay. something is wrong.') else: backMoney = refundedMoney # 刷卡的结束 如果没有开启刷卡退费 则结束上报过来的backMoney = 0 # ID卡结束 直接退费就行了 card = self.update_card_dealer_and_type(cardNo, "ID") if self.device.bill_as_service_feature.on: total = self.device.otherConf.get('totalCharge') backMoney = round(leftElec * total,2) needCoins = float(lineInfo.get('coins')) usedMoney = needCoins - backMoney elec = round(usedMoney / total,2) elecFee = round(elec * float(self.device.bill_as_service_feature.elec_charge),2) serviceFee = round(elec * float(self.device.bill_as_service_feature.service_charge),2) consumeDict.update( {"elec": elec, "elecFee": elecFee, "serviceFee": serviceFee}) if backMoney > 0: self.refund_money_for_card(RMB(backMoney), str(card.id)) consumeDict.update({ DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: VirtualCoin(backMoney).mongo_amount }) consumeDict.update({'balance': str(card.balance + VirtualCoin(backMoney))}) ServiceProgress.update_progress_and_consume_rcd( self.device.ownerId, { 'open_id': lineInfo['openId'], 'port': int(port), 'device_imei': self.device.devNo, 'isFinished':False }, consumeDict ) extra = [{u'实体卡号': cardNo}] if backMoney > 0: extra.append({ u'退款金额': u'{}(金币)'.format(backMoney) }) if self.device.bill_as_service_feature.on: extra.append({ u'消费电量':u'{}(度)'.format(elec), u'电费金额': u'{}(金币)'.format(elecFee), u'服务费金额': u'{}(金币)'.format(serviceFee), }) self.notify_user_service_complete( service_name=u'充电', openid=self.get_managerialOpenId_by_openId(lineInfo['openId']), port=port, address=group['address'], reason=self.event_data['reason'], finished_time=recvTime.strftime('%Y-%m-%d %H:%M:%S'), extra=extra ) except Exception as e: logger.exception(e) def do_time_finish(self, devNo, port, lineInfo, msgDict): logger.info("[{} do_time_finish] devNo = {}, port = {}, lineInfo = {}, msgDict = {}, event = {}".format( self.__class__.__name__, devNo, port, lineInfo, msgDict, self.event_data)) try: if not self.dealer: logger.error( "[{} do_time_finish] dealer {} is not found!".format(self.__class__.__name__, self.device.ownerId)) return # 提取事件信息 leftTime = msgDict.get("leftTime", 0) reasonStr = msgDict.get("reason",'') cardNo = msgDict.get("cardNo", None) usedTime = round(msgDict.get('duration') / 60.0, 2) # 转成分钟 price = RMB(lineInfo.get('price', 0)) recvTime = to_datetime(self.recvTime) refundProtectionTime = self.device.get_other_conf_item('refundProtectionTime', 5) if leftTime == 65535: # 设备返回65535说明端口没有启动 actualNeedTime = 0 else: actualNeedTime = usedTime + leftTime if usedTime < refundProtectionTime: # 通过使用的时间来判断是否可以进行保险的退款 rechargeIds = list() payInfo = lineInfo.get('payInfo', list()) for item in payInfo: if 'rechargeRcdId' not in item: continue rechargeIds.append(item['rechargeRcdId']) group = self.device.group # type: GroupDict consumeDict = {"reason": reasonStr,"chargeIndex": port,'duration':usedTime,"uartData": msgDict.get('uartData', ''), 'port':msgDict['port'],'elec':msgDict['elec'],'orderNo':msgDict['orderNo'],'startTime':msgDict['startTime'],'endTime':msgDict['endTime'], 'chargeType':'elec','consumeTypeFromDev':msgDict['consumeType'],'leftElec':msgDict['leftElec'],'elec':msgDict['elec'],'reasonCode':msgDict['reasonCode'], 'leftTime':round(msgDict['leftTime']/60.0,2) } coins = VirtualCoin(lineInfo['coins']) refundCoins = VirtualCoin(0) refundedMoney = RMB(0) if leftTime == 65535 or (usedTime < refundProtectionTime): refundCoins = coins refundedMoney = RMB(price) elif self.device.is_auto_refund: refundCoins = coins * (float(leftTime) / float(actualNeedTime)) refundedMoney = RMB(price * (float(leftTime) / float(actualNeedTime))) if refundCoins > coins: refundCoins = coins if refundedMoney > RMB(price): refundedMoney = RMB(price) logger.debug('left time = {}, duration = {}, need time = {}, back coins = {}'.format( leftTime, usedTime, actualNeedTime, refundCoins)) # 涉及到卡的退费 不要以缓存为准 以设备上报的为准 if msgDict['consumeType'] == 2: if 'coins' not in lineInfo: logger.warning('has no coins fields in lineInfo. may be coins start.') return if 'consumeRcdId' in lineInfo and lineInfo['consumeRcdId']: # 虚拟卡 vCardId = lineInfo['vCardId'] vCard = UserVirtualCard.objects(id=vCardId).first() # type: UserVirtualCard if not vCard: logger.info('can not find the vCard id = %s' % vCardId) return try: ServiceProgress.update_progress_and_consume_rcd(self.device.ownerId, { 'open_id': lineInfo['openId'], 'port': int(port), 'device_imei': self.device.devNo, 'isFinished':False }, consumeDict) if refundCoins > VirtualCoin(0): vCardConsumeRcd = VCardConsumeRecord.objects.get(id=lineInfo['consumeRcdId']) vCard.refund_quota(vCardConsumeRcd, usedTime, 0.0, refundCoins.mongo_amount) finally: user = MyUser.objects(openId=lineInfo['openId'], groupId=self.device.groupId).first() self.notify_user_service_complete( service_name=u'充电', openid=user.managerialOpenId if user else '', port=port, address=group['address'], reason=self.event_data['reason'], finished_time=recvTime.strftime('%Y-%m-%d %H:%M:%S'), extra=[ {u'虚拟卡号': vCard.cardNo} ] ) elif 'openId' in lineInfo and lineInfo['openId']: user = MyUser.objects(openId=lineInfo['openId'], groupId=self.device.groupId).first() try: is_cash = False if 'refundRMB_device_event' in self.device.owner.features and refundedMoney > RMB(0): is_cash = True consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.COIN: VirtualCoin(coins).mongo_amount}) if refundedMoney > RMB(0) or refundCoins > VirtualCoin(0): consumeDict.update( {DEALER_CONSUMPTION_AGG_KIND.COIN: (VirtualCoin(coins) - refundCoins).mongo_amount}) self.refund_net_pay(user, lineInfo, refundedMoney, refundCoins, consumeDict, is_cash) ServiceProgress.update_progress_and_consume_rcd(self.device.ownerId, { 'open_id': lineInfo['openId'], 'port': int(port), 'device_imei': self.device.devNo, 'isFinished':False }, consumeDict) finally: extra = [] if DEALER_CONSUMPTION_AGG_KIND.REFUNDED_CASH in consumeDict: real_refund = RMB(consumeDict[DEALER_CONSUMPTION_AGG_KIND.REFUNDED_CASH]) if real_refund > RMB(0): extra.append({u'消费金额': '{}(元)'.format(RMB(price) - real_refund)}) extra.append({u'退款金额': '{}(元)'.format(real_refund)}) else: extra.append({u'消费金额': '{}(元)'.format(price)}) elif DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS in consumeDict: real_refund = VirtualCoin(consumeDict[DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS]) if real_refund > VirtualCoin(0): extra.append({u'消费金额': '{}(金币)'.format(VirtualCoin(coins) - real_refund)}) extra.append({u'退款金额': '{}(金币)'.format(real_refund)}) else: extra.append({u'消费金额': '{}(金币)'.format(VirtualCoin(coins))}) else: extra.append({u'消费金额': '{}(金币)'.format(VirtualCoin(coins))}) self.notify_user_service_complete( service_name=u'充电', openid=user.managerialOpenId if user else '', port=port, address=group['address'], reason=self.event_data['reason'], finished_time=recvTime.strftime('%Y-%m-%d %H:%M:%S'), extra=extra) elif msgDict['consumeType'] == 3: # 如果是刷卡的,直接更新消费记录,发通知消息,支持ID卡的退费。 cardNo = self.event_data.get("cardNo", None) backMoney = refundedMoney card = self.update_card_dealer_and_type(cardNo=cardNo, cardType="ID") consumeDict.update({'balance': str(card.balance + VirtualCoin(backMoney))}) if backMoney > 0: dealer = Dealer.objects(id=self.device.ownerId).first() if dealer is not None and 'doNotRefundIDcardForDC' in dealer.features: pass else: self.refund_money_for_card(VirtualCoin(backMoney), card.id) # 通知微信,已经退费 self.notify_user(card.managerialOpenId, 'refund_coins', **{ 'title': u'退币完成!您的卡号是%s,卡别名:%s' % (card.cardNo, card.nickName), 'backCount': u'金币:%s' % VirtualCoin(backMoney), 'finishTime': recvTime.strftime('%Y-%m-%d %H:%M:%S') }) ServiceProgress.update_progress_and_consume_rcd(self.device.ownerId, { 'open_id': lineInfo['openId'], 'port': int(port), 'device_imei': self.device.devNo, 'isFinished':False }, consumeDict) extra = [{ u'实体卡号': lineInfo['cardNo'] }] if VirtualCoin(backMoney) > VirtualCoin(0): extra.append({ u'退费金额': u'{}(金币)'.format(backMoney) }) templateMap = { 'cardCoins': lineInfo.get('coins', 0), 'cardOrderTime': lineInfo.get('cardOrderTime', 0), 'actualNeedTime': actualNeedTime, 'usedTime': usedTime, 'backCoins': backMoney, 'reason': self.event_data['reason'] } title = self.generate_service_complete_title_by_devType(self.device['devType']['id'], templateMap) if title != '': # u"\\n\\n刷卡扣费金额:\\t\\t{cardCoins}\\n\\n刷卡订购时间:\\t\\t{cardOrderTime}\\n\\n动态功率计算时间:\\t\\t{actualNeedTime}\\n\\n使用时间:\\t\\t{usedTime}\\n\\n退款金额:\\t\\t{backCoins}\\n\\n结束原因:\\t\\t{reason}", self.notify_user( card.managerialOpenId if card else '', 'service_complete', **{ 'title': title, 'service': u'充电服务', 'finishTime': recvTime.strftime('%Y-%m-%d %H:%M:%S'), 'remark': u'动态功率计算时间是指按照你的电动车功率大小进行折算出来的实际充电时间。' }) else: self.notify_user_service_complete( service_name=u'充电', openid=card.managerialOpenId if card else '', port=port, address=group['address'], reason=self.event_data['reason'], finished_time=recvTime.strftime('%Y-%m-%d %H:%M:%S'), extra=extra) else: logger.error('not net pay rather user virtual card pay. something is wrong.') except Exception as e: logger.exception(e) class ZHIXIA2FaultEvent(FaultEvent): LX_FAULE_CODE_MAP = { "01": "07", "02": "03", "03": "05" } def do_norther(self): """ 上报其他平台的,都在这个地方处理 可迁移至异步任务 :return: """ # 玉环的消防对接 YuhuanNorther.send_dev_event(self.device, self.event_data['FaultCode'], self.event_data['port']) DelixiNorther.send_dev_event(self.device, self.event_data) # # 梁溪消防局的对接 # faultContent = self.event_data["statusInfo"] # faultCode = self.LX_FAULE_CODE_MAP.get(self.event_data["FaultCode"], "") # districtInfo = District.get_district(self.device.group["districtId"]) # self.device.update({"districtInfo": districtInfo, "groupAddr": self.device.group["address"]}) # # LiangXiXiaoFang.send_to_xf_fault(self.device, faultCode, faultContent, self.event_data["port"]) def do(self, **args): # 将告警的消息打入相应的缓存 port = self.event_data["port"] # 0 表示整机 if port == 0xFF: part = str(0) else: part = str(port) warningData = { "warningStatus": 2, "warningDesc": self.event_data["statusInfo"], "warningTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "warningUart": self.event_data["uart"] } Device.update_dev_warning_cache(self.device.devNo, {part: warningData}) # self.do_norther() super(ZHIXIA2FaultEvent, self).do() class ZHIXIA2InductorEvent(FaultEvent): def do(self, **args): # 处理数据 voltage = self.event_data.get('voltage', -1) temperature = self.event_data.get('temperature', -1) try: otherConf = Device.objects.get(devNo=self.device.devNo).otherConf maxVoltage = otherConf.get('voltageThre', 230) if voltage >= maxVoltage: desc = u'当前电压%s伏超过了门限值:%s伏' % (voltage, maxVoltage) self.record(faultCode=FAULT_CODE.OVER_VOLTAGE, description=desc, title=u'主机电压过高', detail=None, level=FAULT_LEVEL.CRITICAL) maxTemperature = otherConf.get('temThre', 60) if temperature > maxTemperature: desc = u'当前主机温度%s度超过了门限值:%s度' % (temperature, maxTemperature) self.record(faultCode=FAULT_CODE.OVER_TEMPERATURE, description=desc, title=u'主机温度过高', detail=None, level=FAULT_LEVEL.CRITICAL) self.send_fault_to_xf() if self.event_data['alarmType'] == 0xBB: desc = u'当前主机出现冒烟,请第一时间确定是否有起火。此告警十万火急,请迅速联系物业、消防相关部门!' self.record(faultCode=FAULT_CODE.SMOKE, description=desc, title=u'主机出现冒烟', detail=None, level=FAULT_LEVEL.FATAL) self.send_fault_to_xf() self.deviceAdapter.reply_C0(self.event_data['port']) except Exception, e: logger.error('some error=%s' % e) return def send_fault_to_xf(self): try: code = self.event_data.get('FaultCode') # 无锡 梁溪区 粤万通 消防对接 推送 if code == 0xBB: faultCode = '02' faultContent = u'烟雾报警' elif code == 0xAA: faultCode = '01' faultContent = u'温度报警' else: return from taskmanager.mediator import task_caller task_caller('send_to_xf_falut', dealerId=self.device.ownerId, devNo=self.device.devNo, faultCode=faultCode, faultContent=faultContent) except Exception: pass class InteroperatorAlertEvent(FaultEvent): def __Analyze_alert_data(self, data): alertInfo = {'cmdCode': data['FaultCode'], 'logicalCode': self.device['logicalCode']} address = Group.get_group(self.device['groupId'])['address'] # 这里做高温告警处理 if '6' in data['status']: temperatureAccess = [index for index, acces in enumerate(data['status'], 1) if acces == '6'] temperatureAlertList = [] for i in temperatureAccess: temperatureValue = str(int(data['values'][(i - 1) * 4:(i - 1) * 4 + 4], 16)) temperatureAlertList.append( {'temperatureValue': temperatureValue, 'address': address, 'reasonCode': '11', 'reason': u'在{}编号为{}的设备有高温预警,当前温度为{}摄氏度' .format(address, self.device['logicalCode'], temperatureValue)}) alertInfo['temperature'] = temperatureAlertList return alertInfo def do(self, **args): # 判断不存在的设备网上报 if not self.device.ownerId: logger.error('This device cant find a dealer') return # 是否存在温感和电感 temperaturePart = Part.objects(logicalCode=self.device['logicalCode'], partType='3001') electricityPart = Part.objects(logicalCode=self.device['logicalCode'], partType='3002') if not temperaturePart.count() or not electricityPart.count(): logger.error( 'There are no transformers in the locigalcode {} equipment'.format(self.device['logicalCode'])) return # 处理数据 eventInfo = self.__Analyze_alert_data(self.event_data['data']) try: # 先处理高温情况 if 'temperature' in eventInfo: for InfoDetail in eventInfo['temperature']: send_event_to_zhejiang(self.dealer, self.device, InfoDetail, partId=temperaturePart[0].id) # 提示用户 group = Group.get_group(self.device['groupId']) self.notify_dealer('device_fault', **{ 'title': u'注意!注意!您的设备发生故障', 'device': u'组号::%s, 二维码编号:%s' % (self.device['groupNumber'], self.device['logicalCode']), 'location': u'组名称:%s, 地址:%s' % (group['groupName'], group['address']), 'fault': InfoDetail['reason'], 'notifyTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') }) # 上报高温至消防 # if self.device["ownerId"] in ("5b4ed32e8732d67bd0626528", "5b6c29388732d669f3ae6f94"): group = Group.get_group(self.device['groupId']) districtInfo = District.get_district(group["districtId"]) self.device.update({"districtInfo": districtInfo, "groupAddr": group["address"]}) LiangXiXiaoFang.send_to_xf_fault(self.device, "01", u"设备温度过高") # 处理漏电情况 elif 'electricity' in eventInfo: # 获取漏电告警插件 send_event_to_zhejiang(self.dealer, self.device, eventInfo['electricity'], partId=electricityPart[0].id) # 提示用户 group = Group.get_group(self.device['groupId']) self.notify_dealer('device_fault', **{ 'title': u'注意!注意!您的设备发生故障', 'device': u'组号::%s, 二维码编号:%s' % (self.device['groupNumber'], self.device['logicalCode']), 'location': u'组名称:%s, 地址:%s' % (group['groupName'], group['address']), 'fault': eventInfo['electricity']['reason'], 'notifyTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') }) # 上报漏电至消防 # if self.device["ownerId"] in ("5b4ed32e8732d67bd0626528", "5b6c29388732d669f3ae6f94"): group = Group.get_group(self.device['groupId']) districtInfo = District.get_district(group["districtId"]) self.device.update({"districtInfo": districtInfo, "groupAddr": group["address"]}) LiangXiXiaoFang.send_to_xf_fault(self.device, "04", u"设备发生漏电") except: logger.error('Array {} nonspecification'.format(eventInfo)) return self.record(detail=eventInfo) class InteroperatorReport(WorkEvent): def do(self, **args): if 'type' not in self.event_data: logger.error('Array {} is not format,lose a key named "type"'.format(self.event_data)) if self.event_data.get('type') == 'report': devReportDict = {'logicalCode': 'logicalCode', 'time': self.event_data['time_stamp'], 'portInfo': {}} temperature = '' voltage = 220 try: # 拿到个数判断是不是第一次 reportNum = PortReport.get_collection().find({ 'logicalCode': self.device['logicalCode'] }).sort('time', -1).count() if reportNum: # 获取上一次存储的信息 reportLast = PortReport.get_collection().find({ 'logicalCode': self.device['logicalCode'] }).sort('time', -1)[0] for ii in range(10): power = self.__saveDate(1, msgDict=self.event_data, ii=ii) if power: electricity = float(power) / voltage / 10 else: electricity = reportLast['portInfo'][str(ii + 1)]['electricity'] temperatureR = self.__saveDate(2, msgDict=self.event_data, ii=ii, electricity=electricity, devReportDict=devReportDict) if temperatureR: temperature = temperatureR devReportDict.update({'temperature': temperature}) # 查看现在的跟以前差距多少 timeInterval = devReportDict['time'] - reportLast['time'] if timeInterval > 2: PortReportNewList = [ {"logicalCode": self.device['logicalCode'], "temperature": reportLast['temperature'], 'portInfo': reportLast['portInfo'], 'time': reportLast['time'] + (v + 1) * 2} for v in range(int(timeInterval / 2) - 1)] PortReport.get_collection().insert_many(PortReportNewList) # 首存的情况 else: for ii in range(10): power = self.__saveDate(1, msgDict=self.event_data, ii=ii) electricity = float(power) / voltage / 10 temperatureR = self.__saveDate(2, msgDict=self.event_data, ii=ii, electricity=electricity, devReportDict=devReportDict) if temperatureR: temperature = temperatureR devReportDict.update({'temperature': temperature}) except Exception, e: logger.error('solve dev=%s device report has an error e=%s' % (self.device.devNo, e)) finally: newInfo = PortReport( logicalCode=self.device['logicalCode'], temperature=devReportDict['temperature'], time=devReportDict['time'], portInfo=devReportDict['portInfo'] ) newInfo.save() def __saveDate(self, data, msgDict, ii, electricity=None, devReportDict=None): # 存储数据库 if data == 1: powerData = msgDict['data']['power_data'][0 + 4 * ii:4 + 4 * ii] power = int(powerData, 16) return power if data == 2: temperature = '' status = 'idle' if electricity == 0 else 'busy' devReportDict['portInfo'].update( {str(ii + 1): {'electricity': round(electricity, 3), 'status': status}}) if ii < 4 and msgDict['data']['temp_data'][0 + 4 * ii:4 + 4 * ii] != '0000': temperatureNum = msgDict['data']['temp_data'][0 + 4 * ii:4 + 4 * ii] temperature = int(temperatureNum, 16) return temperature