123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- import datetime
- import logging
- from typing import TYPE_CHECKING
- from apilib.monetary import RMB
- from apps.web.constant import DeviceCmdCode, CONSUMETYPE, FAULT_CODE, FAULT_LEVEL
- from apps.web.core.exceptions import ServiceException
- from apps.web.core.networking import MessageSender
- from apps.web.device.models import Device
- from apps.web.eventer.base import FaultEvent, WorkEvent, AckEventProcessorIntf
- from apps.web.eventer import EventBuilder
- from apps.web.eventer.policy_common import PolicyComNetPayAckEvent, PolicyOnlineCardStartAckEvent, \
- StartAckEventPreProcessor
- from apps.web.user.models import CardRechargeOrder, Card
- if TYPE_CHECKING:
- from apps.web.device.models import DeviceDict
- logger = logging.getLogger(__name__)
- class AckEventPreProcessor(StartAckEventPreProcessor):
- def analysis_reason(self, reason, fault_code=None):
- FINISHED_CHARGE_REASON_MAP = {
- 0: u'购买的充电时间或电量用完了。',
- 1: u'插头被拔或者松动,或者电瓶已经充满(系统判断为异常断电,电瓶车充电器种类繁多,可能存在误差)',
- 2: u'电池已经充满',
- 3: u'设备或者端口故障。建议您根据已经充电的时间评估是否需要到现场换到其他端口充电',
- 4: u'警告!您的电池功率超过本机最大限制。为了公共安全,不建议您在该充电桩充电',
- 5: u'刷卡退费结束',
- 6: u'可能是插头被拔掉或者未连接充电器。如果不是自己操作,建议您到现场检查是否有人误操作',
- # 7: u'远程方式停止充电。如果不是自己操作,建议到现场尽快恢复充电',
- # 服务器定义的停止事件
- 0xC0: u'计费方式无效',
- 0xC1: u'订购套餐金额已用完',
- 0xC2: u'订购时间已用完',
- 0xC3: u'订购电量已用完',
- 0xC4: u'动态计算功率后, 时间已用完',
- 0xC5: u'订单异常,设备可能离线超过1小时, 平台结单',
- 0xC6: u'系统检测到充电已结束, 平台结单(0x21)',
- 0xC7: u'系统检测到充电已结束, 平台结单(0x15)',
- 0xC8: u'用户远程停止订单',
- 0xC9: u'经销商远程停止订单',
- 0xCA: u'系统检测到充电已结束, 平台结单(0xCA)',
- 0xCD: u'设备升级中... 关闭当前订单',
- }
- return FINISHED_CHARGE_REASON_MAP.get(reason, '充电结束')
- class builder(EventBuilder):
- def __getEvent__(self, device_event):
- if 'order_id' in device_event:
- if device_event['order_type'] == 'com_start':
- return MyComNetPayAckEvent(self.deviceAdapter, device_event)
- if device_event['order_type'] == 'ic_recharge':
- pass
- if device_event['order_type'] == 'id_start':
- return OnlineCardStartAckEvent(self.deviceAdapter, device_event)
- if device_event['order_type'] == 'card_refund':
- pass
- else:
- if 'event_type' in device_event:
- if device_event['event_type'] == 'card':
- return CardEvent(self.deviceAdapter, 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 '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') == '35' or event_data.get('cmdCode') == '41':
- return KEHANGInductorEvent(self.deviceAdapter, event_data)
- return None
- class MyComNetPayAckEvent(PolicyComNetPayAckEvent):
- def __init__(self, smartBox, event_data):
- super(PolicyComNetPayAckEvent, self).__init__(smartBox, event_data, AckEventPreProcessor())
- class OnlineCardStartAckEvent(PolicyOnlineCardStartAckEvent):
- def __init__(self, smartBox, event_data):
- super(PolicyOnlineCardStartAckEvent, self).__init__(smartBox, event_data, AckEventPreProcessor())
- class AckEventPreProcessor(StartAckEventPreProcessor):
- def analysis_reason(self, reason, fault_code=None):
- FINISHED_CHARGE_REASON_MAP = {
- 0: u'购买的充电时间或电量用完了。',
- 1: u'插头被拔或者松动,或者电瓶已经充满(系统判断为异常断电,电瓶车充电器种类繁多,可能存在误差)',
- 2: u'电池已经充满',
- 3: u'设备或者端口故障。建议您根据已经充电的时间评估是否需要到现场换到其他端口充电',
- 4: u'警告!您的电池功率超过本机最大限制。为了公共安全,不建议您在该充电桩充电',
- 5: u'刷卡退费结束',
- 6: u'可能是插头被拔掉或者未连接充电器。如果不是自己操作,建议您到现场检查是否有人误操作',
- # 7: u'远程方式停止充电。如果不是自己操作,建议到现场尽快恢复充电',
- # 服务器定义的停止事件
- 0xC1: u'订购套餐金额已用完',
- 0xC2: u'订购时间已用完',
- 0xC3: u'订购电量已用完',
- 0xC4: u'动态计算功率后, 时间已用完',
- 0xC5: u'订单异常,设备可能离线超过1小时, 平台结单',
- 0xC6: u'系统检测到充电已结束, 平台结单(0x21)',
- 0xC7: u'系统检测到充电已结束, 平台结单(0x15)',
- 0xC8: u'用户远程停止订单',
- 0xC9: u'经销商远程停止订单',
- 0xCA: u'系统检测到充电已结束, 平台结单(0xCA)',
- }
- return FINISHED_CHARGE_REASON_MAP.get(reason, '充电结束')
- class CardEvent(WorkEvent):
- def do(self):
- if self.event_data['funCode'] == '22':
- self._do_get_balance()
- def _do_get_balance(self):
- cardNo = str(int(self.event_data['card_id'], 16))
- money = self.event_data['money'] * 0.1
- logger.info('[_do_get_balance] receive cardNo = {}'.format(cardNo))
- card = self.update_card_dealer_and_type(cardNo) # type: Card
- data = {'funCode': '23', 'card_id': self.event_data['card_id'], 'balance': 0, 'card_type': self.event_data['card_type']} # 十六进制卡号
- dealer = self.device.owner
- # 无主卡或者是卡被冻结
- if not card or not card.openId or card.frozen or not dealer:
- logger.info('[_do_get_balance] receive cardNo = {}, card invalid!'.format(cardNo))
- data.update({'result': 1})
- return self.send_mqtt(data=data)
- # 是否存在没有到账的余额 进行充值
- card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id))
- self.recharge_id_card(
- card=card,
- rechargeType='append',
- order=card_recharge_order
- )
- card.reload()
- # # 先不给做一张卡只能开启一单的限制
- if card.balance <= RMB(money):
- logger.info(
- '[_do_get_balance] receive cardNo = {}, card balance = {} not enough!'.format(cardNo, card.balance))
- data.update({'result': 1})
- return self.send_mqtt(data=data)
- data.update({
- 'balance': int(RMB(card.balance) * 10), # 单位: 角
- 'attach_paras': {'openId': card.openId},
- })
- data.update(self.generate_rule())
- if RMB(card.balance) > RMB(money):
- data.update({'result': 1})
- else:
- data.update({'result': 2})
- self.send_mqtt(data=data)
- def send_mqtt(self, data=None, cmd=DeviceCmdCode.OPERATE_DEV_SYNC):
- """
- 发送mqtt 指令默认210 返回data
- """
- result = MessageSender.send(self.device, cmd,
- data)
- if 'rst' in result and result['rst'] != 0:
- if result['rst'] == -1:
- raise ServiceException(
- {'result': 2, 'description': u'该设备正在玩命找网络,请您稍候再试', 'rst': -1})
- elif result['rst'] == 1:
- raise ServiceException(
- {'result': 2, 'description': u'该设备忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能', 'rst': 1})
- else:
- if cmd in [DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE]:
- return
- return result.get('data', 'ok')
- def generate_rule(self):
- forIdcard = self.device.policyTemp.get('forIdcard', {})
- policyType = forIdcard.get('policyType', 'time')
- billingMethod = forIdcard.get('billingMethod', 'prepaid')
- account_type = 'real' # account_type(max, real, min) 计费的规则是最大功率计费,还是实时功率计费
- if 'card_accumulate_max_power' in self.device.owner.features:
- account_type = 'max'
- policy = {'billing_method': billingMethod}
- if billingMethod == CONSUMETYPE.POSTPAID: # 后付费
- if policyType == 'time':
- policy.update({
- 'rule_type': 'TIME_ELEC_MONEY',
- 'rule': {
- 'account_type': account_type,
- 'step': self.deviceAdapter.get_uart_step(is_card=True)
- }})
- elif policyType == 'elec':
- elecFee = float(forIdcard.get('rule', {}).get('price', 1)) # 电费单价, 用于显示金额
- policy.update({
- 'rule_type': 'ELEC',
- 'rule': {
- 'account_type': account_type,
- 'elec_fee': int(elecFee * 100)
- }
- })
- else:
- raise ServiceException({'result': 2, 'description': u'套餐单位错误,请联系经销商'})
- else: # 预付费
- money = self.event_data['money'] * 0.1
- if policyType == 'time':
- policy.update({
- 'rule_type': 'MONEY',
- 'rule': {
- 'account_type': account_type,
- 'amount': int(money * 100),
- 'step': self.deviceAdapter.get_uart_step(is_card=True)
- },
- })
- elif policyType == 'elec':
- elecFee = float(forIdcard.get('rule', {}).get('price', 1)) # 电费单价, 用于显示金额
- policy.update({
- 'rule_type': 'MONEY_BY_ELEC',
- 'rule': {
- 'account_type': account_type,
- 'amount': int(money * 100),
- 'elec_fee': int(elecFee * 100)
- },
- })
- else:
- raise ServiceException({'result': 2, 'description': u'套餐单位错误,请联系经销商'})
- return policy
- class CardStartAckEventPreProcessor(AckEventProcessorIntf):
- def analysis_reason(self, reason, fault_code=None):
- pass
- def pre_processing(self, device, event_data):
- # type:(DeviceDict, dict)->dict
- pass
- class KEHANGInductorEvent(FaultEvent):
- def do(self, **args):
- # 处理数据
- voltage = self.event_data.get('voltage', -1)
- temperature = self.event_data.get('temperature', -1)
- smokeWarning = self.event_data.get('smokeWarning', False)
- 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)
- if smokeWarning:
- desc = u'当前主机出现冒烟,请第一时间确定是否有起火。此告警十万火急,请迅速联系物业、消防相关部门!'
- self.record(faultCode=FAULT_CODE.SMOKE, description=desc, title=u'主机出现冒烟', detail=None,
- level=FAULT_LEVEL.FATAL)
- except Exception as e:
- logger.error('some error=%s' % e)
- return
|