123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- import datetime
- import logging
- from typing import TYPE_CHECKING
- from apilib.monetary import RMB
- from apilib.utils_string import make_title_from_dict
- from apps.web.constant import DeviceCmdCode, CONSUMETYPE
- from apps.web.core.device_define.jndz import CMD_CODE
- 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
- 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:
- pass
- logger = logging.getLogger(__name__)
- 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)
- event_data = self.deviceAdapter.analyze_event_data(device_event)
- if event_data is None or 'cmdCode' not in event_data:
- return None
- if event_data['cmdCode'] in [
- # todo 0A的告警不要了,先注释掉,后续删除
- # CMD_CODE.DEVICE_SUBMIT_DEVICE_FAULT_0A,
- CMD_CODE.DEVICE_FAULT_TEMPERATURE,
- CMD_CODE.DEVICE_FAULT_POWER,
- CMD_CODE.DEVICE_FAULT_SMOKE,
- CMD_CODE.DEVICE_ELEC,
- CMD_CODE.DEVICE_FAULT_ALTER
- ]:
- return JNDZEventerFailure(self.deviceAdapter, event_data)
- class CardEvent(WorkEvent):
- def do(self):
- if self.event_data['funCode'] == '10':
- self._do_get_balance()
- def _do_get_balance(self):
- if self.device.devType.get('features', {}).get('cardNoReverse'):
- card_id = self.deviceAdapter.reverse_hex(self.event_data['card_id'])
- else:
- card_id = self.event_data['card_id']
- cardNo = str(int(card_id, 16))
- money = self.event_data['money'] * 0.1
- oper = self.event_data['oper']
- if oper == 1:
- logger.info('recharge operation pass!!')
- return
- logger.info('[_do_get_balance] receive cardNo = {}'.format(cardNo))
- card = self.update_card_dealer_and_type(cardNo) # type: Card
- data = {'funCode': '10', 'card_id': self.event_data['card_id'], 'balance': 0} # 十六进制卡号
- 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()
- # ongoingList = getattr(card, 'ongoingList', []) # 有冻结未结束的订单
- #
- # # 先不给做一张卡只能开启一单的限制
- 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': 0})
- else:
- data.update({'result': 1})
- 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 JNDZEventerFailure(FaultEvent):
- def do(self, **args):
- cmdCode = self.event_data.get("cmdCode")
- faultType = self.event_data.get(u"fault")
- desc = self.event_data.get("desc", "")
- # todo 0A的告警不要了,先注释掉,后续删除
- # 保证原有的故障处理逻辑不变
- # if cmdCode == CMD_CODE.DEVICE_SUBMIT_DEVICE_FAULT_0A:
- # return self.handler_0A_fault()
- # 这些都是整机告警
- part = "0"
- warningData = {
- "warningStatus": 2,
- "warningDesc": desc,
- "warningTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
- }
- Device.update_dev_warning_cache(self.device.devNo, {part: warningData})
- group = self.device.group
- titleList = [
- {u"告警名称": faultType},
- {u"地址名称": group["groupName"]}
- ]
- title = make_title_from_dict(titleList)
- # 接下来的都是整机告警,这个地方需要通知到经销商
- self.notify_dealer(
- "device_fault",
- title=title,
- device=u" 号设备".format(self.device.logicalCode),
- faultType=faultType,
- notifyTime=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
- fault=""
- )
- # 记录错误故障
- fault_record = self.record(
- faultCode=cmdCode,
- description=desc,
- title=faultType,
- detail={"faultType": faultType, "errorCode": self.event_data.get("errorCode")}
- )
- self.north_to_liangxi(fault_record)
- # todo 0A的告警不要了,先注释掉,后续删除
- # def handler_0A_fault(self):
- # faultContent = self.event_data.get('faultContent', '')
- # level = self.event_data.get('level', '')
- # errCode = self.event_data.get('errCode')
- #
- # port = self.event_data.get('port')
- # if port and port != 255:
- # title = u'注意!您的设备{}号端口发出告警!'.format(port)
- # part = str(port)
- # else:
- # title = u'注意!您的设备发出告警!'
- # part = "0"
- #
- # if errCode in ['A3']: # 空载 无需显示在经销商后台
- # return
- #
- # elif errCode in ['00']: # 老设备上报继电器粘连 100206 不上报!!
- # return
- #
- # # 设备告警打入缓存
- # elif errCode in ['A0', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9', 'AA']:
- # warningData = {
- # "warningStatus": 2,
- # "warningDesc": faultContent,
- # "warningTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
- # }
- # Device.update_dev_warning_cache(self.device.devNo, {part: warningData})
- #
- # # 设备告警清除
- # elif errCode in ['20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '2A']: # 恢复信号 不操作
- # Device.clear_part_warning_cache(self.device.devNo, part)
- # # ctrInfo = Device.get_dev_control_cache(self.device.devNo)
- # # if 'statusInfo' in ctrInfo and 'errCode' in ctrInfo:
- # # if ctrInfo['errCode'][-1] == errCode[-1]:
- # # ctrInfo['status'] = None
- # # ctrInfo['errCode'] = None
- # # ctrInfo['faultContent'] = None
- # # ctrInfo['level'] = None
- # # ctrInfo['statusInfo'] = None
- # # ctrInfo['cmdCode'] = None
- # # Device.update_dev_control_cache(self.device['devNo'], ctrInfo)
- #
- # else:
- # pass
- # # Device.update_dev_control_cache(self.device['devNo'], self.event_data)
- #
- # # 记录错误故障
- # self.record(
- # title=title,
- # description=faultContent,
- # level=level
- # )
- #
- # group = Group.get_group(self.device.groupId)
- #
- # if self.is_notify_dealer:
- # self.notify_dealer(
- # templateName="device_fault",
- # title=title,
- # device=u'{}-{}'.format(self.device.devTypeName, self.device.logicalCode),
- # fault=faultContent,
- # location=u'{}-{}-{}号设备({})'.format(group["address"], group["groupName"], self.device["groupNumber"],
- # self.device["logicalCode"]),
- # notifyTime=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
- # )
- class AckEventPreProcessor(StartAckEventPreProcessor):
- def analysis_reason(self, reason, fault_code=None):
- FINISHED_CHARGE_REASON_MAP = {
- 0x00: u'设备结束充电,您订购的套餐用完了',
- 0x01: u'用户手动停止(拔插头,或是按了停止按钮)',
- 0x02: u'充电满了,自动停止',
- 0x03: u'超功率自停',
- 0x04: u'远程停止',
- 0x0B: u'设备或是端口出现问题,被迫停止',
- # 服务器定义的停止事件
- 0xC1: u'订购套餐已用完',
- 0xC2: u'订购时间已用完',
- 0xC3: u'订购电量已用完',
- 0xC4: u'动态计算功率后, 时间已用完',
- 0xC5: u'订单异常,设备可能离线超过1小时, 平台结单',
- 0xC6: u'系统检测到充电已结束, 平台结单(0x21)',
- 0xC7: u'系统检测到充电已结束, 平台结单(0x0F)',
- 0xC8: u'用户远程停止订单',
- 0xC9: u'经销商远程停止订单',
- 0xCA: u'系统检测到订单已结束, 平台结单(0xCA)',
- 0xCB: u'充电时长已达到最大限制(0xCB)',
- 0xCC: u'充电电量已达到最大限制(0xCC)',
- }
- return FINISHED_CHARGE_REASON_MAP.get(reason, '充电结束')
- 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())
|