1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- import datetime
- import logging
- import random
- import time
- from arrow import Arrow
- from django.conf import settings
- from typing import TYPE_CHECKING
- from apilib.monetary import sum_rmb, RMB, VirtualCoin, Ratio
- from apilib.utils_datetime import to_datetime
- from apps.web.device.timescale import FluentedEngine
- from apps.web.south_intf.liangxi_fire import LiangXiXiaoFang
- 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.device_define.dianchuan import FINISHED_CHARGE_REASON_MAP
- from apps.web.dealer.models import Dealer
- from apps.web.device.models import PortReport, OfflineCoinStatistics, Part, Group, Device
- from apps.web.eventer.base import FaultEvent, WorkEvent, ComNetPayAckEvent, IcStartAckEvent, \
- AckEventProcessorIntf, IcRechargeAckEvent, CardRefundAckEvent
- from apps.web.eventer import EventBuilder
- from apps.web.south_intf.platform import notify_event_to_north
- from apps.web.south_intf.zhejiang_fire import send_event_to_zhejiang
- from apps.web.user.models import VCardConsumeRecord, ServiceProgress, UserVirtualCard, CardRechargeOrder, MyUser, \
- ConsumeRecord, RechargeRecord, Card
- if TYPE_CHECKING:
- from apps.web.user.models import Card
- from apps.web.device.models import GroupDict
- from apps.web.device.models import DeviceDict
- logger = logging.getLogger(__name__)
- class builder(EventBuilder):
- def __getEvent__(self, device_event):
- if 'data' not in device_event:
- return None
- if 'type' in device_event:
- if device_event['type'] == 'alert':
- return DIANCHUANAlertEvent(self.deviceAdapter, device_event)
- if device_event['type'] == 'report':
- return DIANCHUANReport(self.deviceAdapter, device_event)
- else:
- event_data = self.deviceAdapter.analyze_event_data(device_event['data'])
- if event_data is None or 'cmdCode' not in event_data:
- return None
- if 'duration' in device_event:
- event_data.update({'duration': device_event['duration']})
- if 'elec' in device_event:
- event_data.update({'elec': device_event['elec']})
- if 'v' in device_event:
- event_data.update({'v': device_event['v']})
- if 'today_coins' in device_event and 'ts' in device_event:
- event_data.update({'today_coins': device_event['today_coins']})
- event_data.update({'ts': device_event['ts']})
- if event_data.get('cmdCode') in ['03', '05', '11', '12', '17', '22', '48']:
- return ChargingDIANCHUANWorkEvent(self.deviceAdapter, event_data)
- if event_data.get('cmdCode') == '0D':
- return DIANCHUANFaultEvent(self.deviceAdapter, event_data)
- if event_data.get('cmdCode') in ['41', '47']:
- return DIANCHUANInductorEvent(self.deviceAdapter, event_data)
- class ChargingDIANCHUANWorkEvent(WorkEvent):
- @property
- def support_playback(self):
- if self.event_data['cmdCode'] in ['05']:
- return True
- else:
- return False
- def __parse_device_finished_data(self, event_data):
- duration = event_data.get('duration', -1)
- if duration != -1:
- if 'v' in event_data:
- duration = (duration / 60)
- elec = event_data.get('elec', -1)
- if elec != -1:
- if 'v' in event_data:
- elec = round(elec / (10000.0 * 3600.0), 3)
- else:
- elec = round(elec / 3600.0, 3)
- logger.debug('device duration is {}, device elec is {}'.format(duration, elec))
- return duration, elec
- def do(self, **args):
- logger.info('[dianchuan] charging event detected, devNo=%s,info=%s' % (self.device.devNo, self.event_data))
- if self.event_data['cmdCode'] == '03':
- if 'today_coins' in self.event_data and 'ts' in self.event_data:
- Accounting.syncOfflineCoin(
- self.device,
- datetime.datetime.fromtimestamp(self.event_data['ts']).strftime('%Y-%m-%d'),
- self.event_data['today_coins'])
- if self.event_data['coins'] > 0:
- FluentedEngine().in_put_coins_udp(devNo=self.device.devNo,
- ts=int(time.time()),
- coins=self.event_data['coins'],
- mode='uart')
- else:
- # 老的流程会单条记录上报记录
- Accounting.recordOfflineCoin(device=self.device,
- report_ts=int(time.time()),
- coins=int(self.event_data['coins']),
- mode='uart',
- port=self.event_data.get('port', None))
- try:
- # 如果是投币,直接把端口状态刷新下
- self.deviceAdapter.get_port_status_from_dev()
- except Exception, e:
- logger.info('some err=%s' % e)
- # YuhuanNorther.send_dev_status(self.device, self.event_data['port'], 1)
- elif self.event_data['cmdCode'] == '22': # ID在线卡刷卡查询余额
- cardNo = self.event_data['cardNo']
- fee = RMB(self.event_data['fee'])
- cardType = self.event_data['cardType']
- card = self.update_card_dealer_and_type(cardNo)
- if not card:
- res = "03"
- elif not card.openId:
- res = '03'
- elif card.frozen:
- res = '04'
- else:
- 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,fee=%s,info=%s' % (
- cardNo, card.openId, result, fee, self.event_data))
- res = "01" if result["balance"] >= fee else "02"
- balance = RMB(0) if not card else card.balance
- try:
- logger.info("card is <{}> fee is <{}> response res is <{}>".format(cardNo, fee, res))
- result = self.deviceAdapter.response_card_status(cardNo, balance, res)
- except Exception, e: # 启动失败需要把金币还回去
- logger.info('resp back error=%s' % e)
- return
- if fee <= RMB(0):
- logger.debug(
- 'query the balance of card<cardNo={}> in device<devNo={}>'.format(card.cardNo, self.device.devNo))
- return
- # 设备回复扣款成功,服务器就需要正式扣掉钱
- if result['status'] == '01' and res == '01':
- virtual_card = card.bound_virtual_card
- if virtual_card is not None:
- group = Group.get_group(self.device['groupId'])
- VCardConsumeRecord(
- orderNo=VCardConsumeRecord.make_no(self.device.logicalCode),
- 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)
- # 记录卡消费记录以及消费记录
- orderNo, cardOrderNo = self.record_consume_for_card(card, fee)
- # 记录当前服务的progress.没有上报端口号,所以先用-1标记,表示端口未知。端口等消费的时候会报上来
- ServiceProgress.register_card_service(
- self.device, -1, card,
- {
- 'orderNo': orderNo,
- 'money': self.event_data['fee'],
- 'coin': self.event_data['fee'],
- 'needTime': 0,
- 'cardOrderNo': cardOrderNo
- }
- )
- # 通知微信,已经扣费
- self.notify_balance_has_consume_for_card(card, fee)
- elif self.event_data['cmdCode'] == '11' and self.event_data['status'] == '01': # 开始启动某个端口
- # 按下端口,开始使用。IC卡和ID卡都会上报这条信息。IC卡只记录消费记录信息,ID卡主要是上报了具体的某一个端口.ID卡和劲能的类似,主要是表示开始某一个端口以及回收余额
- cardType = self.event_data['cardType']
- card = self.update_card_dealer_and_type(self.event_data['cardNo'], cardType)
- # 这个地方将计费方式更新到 portDict 刷卡退费的时候会将刷卡的退费金额上报过来 结束事件需要这个字段 主要的是要更新consumeDict的字段
- # 0 按时间收费
- # 1 按度计费
- consumeModule = self.device.get("otherConf", dict()).get("consumeModule", 0)
- if consumeModule == 0:
- self.event_data["billingType"] = "time"
- else:
- self.event_data["billingType"] = "elec"
- if cardType == 'ID':
- consumeDict = {'chargeIndex': self.event_data['port']}
- queryDict = {'device_imei': self.device.devNo,
- 'port': -1, 'isFinished': False,
- 'cardId': str(card.id), 'start_time': {'$gte': int(time.time()) - 3600}}
- progressDict = {'port': self.event_data['port']}
- ServiceProgress.update_progress_and_consume_rcd(ownerId=self.device.ownerId,
- queryDict=queryDict, consumeDict=consumeDict,
- updateConsume=True, progressDict=progressDict)
- # 找出对应的卡的ID记录到端口内存数据
- queryDict.update(progressDict)
- rcds = ServiceProgress.get_collection().find(queryDict, {'consumeOrder': 1},
- sort=[('start_time', -1)])
- allCoins = sum_rmb([rcd['consumeOrder']['coin'] for rcd in rcds])
- self.event_data.update({'cardId': str(card.id)})
- self.event_data.update({'openId': card.openId})
- self.event_data.update({'coins': str(allCoins)})
- self.event_data.update({'startTime': datetime.datetime.now().strftime(Const.DATETIME_FMT)})
- self.event_data.update({'isStart': True, 'status': Const.DEV_WORK_STATUS_WORKING})
- Device.update_port_control_cache(self.device.devNo, self.event_data) # 记录该端口累计需要的时间和钱,cardId
- else: # IC卡,需要记录消费记录
- if card is not None:
- fee = RMB(self.event_data['fee'])
- card.balance = RMB(self.event_data['balance'])
- try:
- card.save()
- except Exception, e:
- logger.exception(e)
- orderNo, cardOrderNo = self.record_consume_for_card(card, fee)
- ServiceProgress.register_card_service(self.device, self.event_data['port'], card,
- {'orderNo': orderNo, 'money': self.event_data['fee'],
- 'coin': self.event_data['fee'], 'cardOrderNo': cardOrderNo})
- # 通知微信,已经扣费
- self.notify_balance_has_consume_for_card(card, fee)
- self.event_data.update({'cardId': str(card.id)})
- self.event_data.update({'openId': card.openId})
- self.event_data.update({'coins': self.event_data['fee']})
- self.event_data.update({'startTime': datetime.datetime.now().strftime(Const.DATETIME_FMT)})
- self.event_data.update({'isStart': True, 'status': Const.DEV_WORK_STATUS_WORKING})
- Device.update_port_control_cache(self.device.devNo, self.event_data) # 记录该端口累计需要的时间和钱,cardId
- # YuhuanNorther.send_dev_status(self.device, self.event_data['port'], 1)
- elif self.event_data['cmdCode'] == '17': # 余额回收
- card = self.update_card_dealer_and_type(self.event_data['cardNo'], 'IC') # type: Card
- if not card:
- logger.warning('Card<dealerId={},cardNo={},type=IC> is not registered.'.format(
- str(self.dealer.id), self.event_data['cardNo']))
- return
- self.refund_money_for_card(RMB(self.event_data['backMoney']), card.id)
- elif self.event_data['cmdCode'] == '12':
- cardNo = self.event_data['cardNo']
- preBalance = RMB(self.event_data['balance'])
- card = self.update_card_dealer_and_type(cardNo, 'IC', balance=preBalance) # type: Card
- if not card:
- return
- if card.frozen:
- logger.debug('{} has been frozen.'.format(repr(card)))
- return
- card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id)) # type: CardRechargeOrder
- if not card_recharge_order:
- logger.debug('{} has no recharge order.'.format(repr(card)))
- return
- self.recharge_ic_card(card=card,
- preBalance=preBalance,
- rechargeType='overwrite',
- order=card_recharge_order)
- elif self.event_data['cmdCode'] == '05':
- # 新版本的结束事件,比就版本多刷卡ID卡的在线退费
- devNo = self.device.devNo
- port = str(self.event_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)))
- billingType = lineInfo.get("billingType", "time")
- if billingType == "elec":
- return self.do_elec_finish(devNo, port, lineInfo, self.event_data)
- else:
- return self.do_time_finish(devNo, port, lineInfo, self.event_data)
- finally:
- notify_event_to_north(self.dealer, self.device, level=Const.EVENT_NORMAL,
- desc=self.event_data.get('reason'))
- send_event_to_zhejiang(self.dealer, self.device, self.event_data)
- if self.event_data['reasonCode'] == '04': # 功率过载导致的断电,一条告警,一条恢复告警
- pass
- elif self.event_data['cmdCode'] == '48':
- # 没有报端口
- leftTime = self.event_data.get('leftTime', 0)
- nowPower = self.event_data.get('nowPower', 0)
- nowPowerRatio = self.event_data.get('nowPowerRatio', 0)
- ctlDict = Device.get_dev_control_cache(self.device.devNo)
- for port, info in ctlDict.items():
- if isinstance(info, dict):
- doPowerLevel = info.get('doPowerLevel', '')
- if doPowerLevel == True or doPowerLevel == '':
- continue
- realTime = info['needTime'] * nowPowerRatio / 100
- if abs(realTime - leftTime) <= 3:
- info['doPowerLevel'] = True
- info['powerLevelTime'] = leftTime
- info['power'] = nowPower
- info['port'] = port
- Device.update_port_control_cache(self.device.devNo, info)
- else:
- pass
- 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 = self.event_data.get("leftTime", 0) / 100.0
- reasonCode = self.event_data.get("endType")
- reasonStr = self.event_data.get("reason")
- cardNo = self.event_data.get("cardNo", None)
- cardType = self.event_data.get("cardType", None)
- price = RMB(lineInfo.get('price', 0))
- startTime = lineInfo.get("startTime")
- # 投币事件
- if not startTime:
- return
- startTime = to_datetime(startTime)
- recvTime = to_datetime(self.recvTime)
- deviceDuration, deviceElec = self.__parse_device_finished_data(self.event_data)
- try:
- consumeDict = {
- "reason": reasonStr,
- "chargeIndex": port,
- "uartData": self.event_data.get('uartData', '')
- }
- # 计算充电的时间
- if deviceDuration and deviceDuration > 0:
- usedTime = deviceDuration
- else:
- if startTime > recvTime:
- usedTime = 0
- else:
- usedTime = int(round(((recvTime - startTime).total_seconds() / 60.0)))
- refundProtectionTime = self.device.get_other_conf_item('refundProtectionTime', 3)
- consumeDict.update({"duration": usedTime})
- # 获取组信息
- group = Group.get_group(self.device["groupId"])
- coins = VirtualCoin(lineInfo.get("coins"))
- # 扫码的结束
- if not cardNo:
- consumeDict.update({
- "reason": reasonStr,
- "chargeIndex": port
- })
- 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 '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
- if refundedMoney > RMB(0) or refundCoins > VirtualCoin(0):
- 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))})
- consumeDict.update({
- DEALER_CONSUMPTION_AGG_KIND.COIN: (VirtualCoin(coins) - real_refund).mongo_amount
- })
- else:
- extra.append({u'消费金额': '{}(金币)'.format(VirtualCoin(coins))})
- consumeDict.update({
- DEALER_CONSUMPTION_AGG_KIND.COIN: VirtualCoin(coins).mongo_amount
- })
- 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 = self.event_data.get("backMoney")
- # 刷卡的结束 如果没有开启刷卡退费 则结束上报过来的backMoney = 0
- if cardType == "IC":
- # IC卡结束 只有刷卡结束的IC卡才能够刷新IC卡余额
- card = self.update_card_dealer_and_type(cardNo, "IC")
- if reasonCode == "05" and backMoney > 0:
- # 原因为05表示刷卡结束充电,会把退费金额报上来. 否则只能通过11指令报上来
- self.refund_money_for_card(VirtualCoin(backMoney), card.id)
- consumeDict.update({'balance': str(card.balance + VirtualCoin(backMoney))})
- else:
- consumeDict.update({'balance': str(card.balance)}) # IC卡必须下次刷卡的时候回收上来
- elif cardType == "ID":
- # ID卡结束 直接退费就行了
- card = self.update_card_dealer_and_type(cardNo, "ID")
- 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))})
- else:
- logger.error("invalid card type event data is <{}>".format(self.event_data))
- 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)
- })
- 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
- price = RMB(lineInfo.get('price', 0))
- refundProtectionTime = self.device.get_other_conf_item('refundProtectionTime', 5)
- deviceDuration, deviceElec = self.__parse_device_finished_data(self.event_data)
- recvTime = to_datetime(self.recvTime)
- if lineInfo is not None and 'startTime' in lineInfo:
- startTime = to_datetime(lineInfo['startTime'])
- if startTime > recvTime:
- logger.error('start time is bigger than now time,devNo={}'.format(devNo))
- serverDuration = -1
- else:
- serverDuration = int(round((((recvTime - startTime).total_seconds() + 59) / 60.0)))
- else:
- logger.info('lineinfo has not startTime,devNo=%s' % devNo)
- serverDuration = -1
- leftTime = self.event_data['leftTime']
- logger.debug('serverDuration = {}; deviceDuration = {}; leftTime = {}; lineInfo = {}'.format(
- serverDuration, deviceDuration, leftTime, lineInfo))
- if serverDuration < 0 and deviceDuration < 0:
- logger.warning('serverDuration and deviceDuration is valid. ignore this event.')
- return
- usedTime = 0
- if leftTime == 65535: # 设备返回65535说明端口没有启动
- leftTimeStr = u'端口未使用'
- actualNeedTime = 0
- else:
- usedTime = max(serverDuration, deviceDuration)
- leftTimeStr = leftTime
- 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': self.event_data['reason'],
- 'leftTime': leftTimeStr,
- 'chargeIndex': port,
- 'actualNeedTime': u'动态功率计算为%s分钟' % actualNeedTime,
- 'duration': usedTime,
- 'deviceDuration': deviceDuration,
- 'serverDuration': serverDuration,
- 'uartData': self.event_data.get('uartData', '')
- }
- try:
- groupObj = Group.objects(id=self.device.groupId).first()
- if groupObj.otherConf.get('zhuxing', None) is not None:
- spendElec = round((float(random.randint(15, 19)) / 100) * (float(usedTime) / 60), 3)
- consumeDict.update({'elec': spendElec})
- consumeDict.update({'elecFee': self.deviceAdapter.calc_elec_fee(spendElec)})
- else:
- if deviceElec != -1:
- consumeDict.update({'elec': deviceElec})
- if deviceElec == 0 and usedTime > 0:
- consumeDict.update(
- {'elec': round((float(random.randint(15, 19)) / 100) * (float(usedTime) / 60), 3)})
- else:
- consumeDict.update({'elec': 0.0})
- consumeDict.update({'elecFee': RMB(0.0).mongo_amount})
- except Exception as e:
- logger.error("device <{}> elec add error! <{}>".format(self.device.devNo, e))
- # 涉及到卡的退费 不要以缓存为准 以设备上报的为准
- cardNo = self.event_data.get("cardNo", None)
- if cardNo:
- # 如果是刷卡的,直接更新消费记录,发通知消息,支持ID卡的退费。
- # IC卡的退费, 如果结束原因是刷卡退费(05), 则会把退费信息返回;
- # 否则不会把退费信息传过来,会通过11号命令返回
- cardType = self.event_data.get("cardType")
- if cardType == 'ID':
- card = self.update_card_dealer_and_type(cardNo=cardNo, cardType="ID")
- consumeDict.update({'balance': str(card.balance + VirtualCoin(self.event_data['backMoney']))})
- if self.event_data.has_key('backMoney') and self.event_data['backMoney'] > 0:
- self.refund_money_for_card(VirtualCoin(self.event_data['backMoney']), card.id)
- # 通知微信,已经退费
- self.notify_user(card.managerialOpenId, 'refund_coins', **{
- 'title': u'退币完成!您的卡号是%s,卡别名:%s' % (card.cardNo, card.nickName),
- 'backCount': u'金币:%s' % VirtualCoin(self.event_data['backMoney']),
- 'finishTime': recvTime.strftime('%Y-%m-%d %H:%M:%S')
- })
- elif cardType == "IC":
- card = self.update_card_dealer_and_type(cardNo=cardNo, cardType="IC")
- if not card:
- logger.warning('Card<dealerId={},cardNo={},type=IC> is not registered.'.format(
- str(self.dealer.id), cardNo))
- return
- # IC 并且结束code是05 表示卡贴上去了
- if self.event_data['endType'] == '05': # 刷卡退费结束的时候,才能够刷新IC卡的余额
- self.refund_money_for_card(VirtualCoin(self.event_data['backMoney']), card.id)
- consumeDict.update({'balance': str(card.balance + VirtualCoin(self.event_data['backMoney']))})
- else:
- consumeDict.update({'balance': str(card.balance)}) # IC卡必须下次刷卡的时候回收上来
- else:
- logger.error("invalid card type event data is <{}>".format(self.event_data))
- 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(self.event_data['backMoney']) > VirtualCoin(0):
- extra.append({
- u'退费金额': u'{}(金币)'.format(self.event_data['backMoney'])
- })
- 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:
- if 'coins' not in lineInfo:
- logger.warning('has no coins fields in lineInfo. may be coins start.')
- return
- 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 = {}, refund money = {}'.format(
- leftTime, usedTime, actualNeedTime, refundCoins, refundedMoney))
- 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
- if refundedMoney > RMB(0) or refundCoins > VirtualCoin(0):
- 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)
- else:
- logger.error('not net pay rather user virtual card pay. something is wrong.')
- except Exception as e:
- logger.exception(e)
- class DIANCHUANFaultEvent(FaultEvent):
- def do(self, **args):
- detail = {"errorCode": self.event_data["faultCode"]}
- self.event_data["detail"] = detail
- super(DIANCHUANFaultEvent, self).do()
- class DIANCHUANInductorEvent(FaultEvent):
- def do(self, **args):
- # 处理数据
- temperatureLast = self.event_data.get('temperatureLast', None)
- smokeWarning = self.event_data.get('smokeWarning', False)
- try:
- faultName = "告警"
- faultCode = ""
- desc = ""
- if temperatureLast:
- logger.info('Warning!!! Temperature exceeds upper limit')
- temperatureMax = self.device.get('otherconf', {}).get('temperatureMax', 60)
- desc = u'当前主机温度%s度超过了门限值:%s度' % (temperatureLast, temperatureMax)
- faultName = u"主机温度告警"
- faultCode = FAULT_CODE.OVER_TEMPERATURE
- if smokeWarning:
- logger.info('Warning!!! Smoke sensor alarm')
- desc = u'当前主机出现冒烟,请第一时间确定是否有起火。此告警十万火急,请迅速联系物业、消防相关部门!'
- faultName = u"主机烟雾告警"
- faultCode = FAULT_CODE.SMOKE
- except Exception, e:
- logger.error('some error=%s' % e)
- return
- detail = {"errorCode": self.event_data["faultCode"]}
- self.event_data.update({
- "faultName": faultName,
- "faultCode": faultCode,
- "level": FAULT_LEVEL.CRITICAL,
- "desc": desc,
- "detail": detail
- })
- super(DIANCHUANInductorEvent, self).do(**args)
- class DIANCHUANAlertEvent(FaultEvent):
- def __Analyze_alert_data(self, data):
- alertInfo = {'cmdCode': data['cmd'], 'logicalCode': self.device['logicalCode']}
- address = Group.get_group(self.device['groupId'])['address']
- # 这里判断数据格式
- if 'status' not in data:
- logger.error('Data arrays have no keywords status')
- return
- # 这里做漏电告警处理
- if '5' in data['status']:
- electricityNum = str(int(data['values'][0:4], 16)) + 'mA'
- alertInfo['electricity'] = {'electricityNum': electricityNum,
- 'address': address,
- 'reasonCode': '12',
- 'reason': u'在{}编号为{}发生漏电,漏电量为{}'
- .format(address, self.device['logicalCode'], electricityNum)}
- # 这里做高温告警处理
- 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 DIANCHUANReport(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
|