cxjz.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. # -*- coding: utf-8 -*-
  2. #!/usr/bin/env python
  3. import datetime
  4. import logging
  5. from decimal import Decimal
  6. from apilib.monetary import RMB
  7. from apilib.utils_datetime import to_datetime
  8. from apps.web.constant import Const, DEALER_CONSUMPTION_AGG_KIND, FAULT_LEVEL
  9. from apps.web.device.models import Device, Group
  10. from apps.web.eventer.base import FaultEvent, WorkEvent
  11. from apps.web.eventer import EventBuilder
  12. from apps.web.south_intf.platform import notify_event_to_north
  13. from apps.web.user.models import ServiceProgress, MyUser
  14. from apps.web.user.transaction_deprecated import refund_money
  15. logger = logging.getLogger(__name__)
  16. class builder(EventBuilder):
  17. def __getEvent__(self, device_event):
  18. if 'data' not in device_event:
  19. logger.warning('event of device<devNo={}> has no data.'.format(self.device.devNo))
  20. return
  21. event_data = self.deviceAdapter.analyze_event_data(device_event['data'])
  22. if event_data is None or 'cmdCode' not in event_data:
  23. return
  24. if event_data['cmdCode'] in ['88', '80', '01', '02', '03']:
  25. return ChargingCXJZWorkEvent(self.deviceAdapter, event_data)
  26. if event_data['cmdCode'] in ['04', 'AE']:
  27. return CXJZEventerFailure(self.deviceAdapter, event_data)
  28. class CXJZEventerFailure(FaultEvent):
  29. def do(self, **args):
  30. # 不改变之前的流程
  31. if self.event_data["cmdCode"] == '04':
  32. return
  33. if self.event_data["cmdCode"] == "AE":
  34. return self._do_AE()
  35. def _do_AE(self):
  36. # 告警开关有无打开 以及 是否处于告警状态
  37. if not self.event_data["buzzer"] or not self.event_data["smokeSwitch"]:
  38. return
  39. # 通知经销商
  40. if self.is_notify_dealer():
  41. self.notify_dealer(
  42. 'device_fault',
  43. **{
  44. 'title': u"设备故障告警",
  45. 'device': u'%s号设备\\n' % self.device.groupNumber,
  46. 'faultType': u'设备烟雾告警\\n',
  47. 'notifyTime': u'%s\\n' % datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  48. 'fault': u"设备当前温度 {}, 烟雾浓度 {}".format(self.event_data["temperature"], self.event_data["smokeCon"]),
  49. }
  50. )
  51. # 写入故障记录
  52. self.record(
  53. faultCode = "AE",
  54. description = u"设备故障告警 当前温度 {}, 当前烟雾浓度 {}",
  55. title = u"设备故障",
  56. detail = u"",
  57. level = FAULT_LEVEL.NORMAL,
  58. portNo = 0
  59. )
  60. class ChargingCXJZWorkEvent(WorkEvent):
  61. def do(self, **args):
  62. devNo = self.device['devNo']
  63. logger.info('cxjz charging event detected, devNo=%s, curInfo=%s' % (devNo, self.event_data))
  64. if self.event_data['cmdCode'] in ['88', '80', '01', '02', '03']:
  65. port = str(self.event_data['port'])
  66. try:
  67. ctrInfo = Device.get_dev_control_cache(self.device['devNo'])
  68. lineInfo = ctrInfo.get(port)
  69. if (not lineInfo) or ('openId' not in lineInfo):
  70. logger.warning('openId is missing, lineInfo=%s' % lineInfo)
  71. return
  72. # 锁定用户
  73. user = MyUser.objects(openId=lineInfo['openId'], groupId=self.device['groupId']).first()
  74. # 判断是否为远程关闭
  75. if 'leftTime' in lineInfo and 'leftElec' in lineInfo:
  76. leftTime = lineInfo['leftTime']
  77. leftElec = lineInfo['leftElec']
  78. reason = u'充电结束, 经销商远程关闭充电!'
  79. else:
  80. leftTime = self.event_data['leftTime']
  81. leftElec = self.event_data['leftElec']
  82. reason = self.event_data['reason']
  83. startTime = to_datetime(lineInfo['startTime'])
  84. nowTime = datetime.datetime.now()
  85. if startTime > nowTime:#如果web服务器时间和事件监控服务器时间不一致,导致开始时间比事件时间还大
  86. usedTime = 0
  87. else:
  88. usedTime = int(round(((nowTime - startTime).total_seconds() / 60.0)))
  89. actualNeedTime = usedTime + leftTime
  90. group = Group.get_group(self.device['groupId'])
  91. consumeDict = {'reason': self.event_data['reason'], 'leftTime': leftTime, 'leftElec': leftElec,
  92. 'chargeIndex': port, 'actualNeedTime': u'动态功率计算为%s分钟' % actualNeedTime,
  93. 'duration': usedTime}
  94. coins = RMB(lineInfo['coins'])
  95. if lineInfo['workMode'] == 0: # 按时间计费
  96. self.notify_user(user.managerialOpenId if user else '', 'service_complete',
  97. **{
  98. 'title': u'%s 按动态功率计算总时间为:%s分钟,剩余时间:%s分钟' % (
  99. reason,
  100. actualNeedTime,
  101. leftTime),
  102. 'service': u'充电服务(设备编号:%s, 端口:%s,地址:%s)' % (
  103. self.device['logicalCode'], port, group['address']),
  104. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  105. 'remark': u'谢谢您的支持'
  106. })
  107. # 如果需要退款,计算退款数据.
  108. if not self.device.is_auto_refund:
  109. consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.COIN: coins.mongo_amount})
  110. ServiceProgress.update_progress_and_consume_rcd(self.device['ownerId'],
  111. {'open_id': lineInfo['openId'],
  112. 'port': int(port),
  113. 'device_imei': self.device['devNo'],
  114. 'isFinished': False}, consumeDict)
  115. else:
  116. backCoins = coins * (float(leftTime) / float(actualNeedTime))
  117. if backCoins > coins:
  118. backCoins = coins
  119. consumeDict.update({
  120. DEALER_CONSUMPTION_AGG_KIND.COIN: (coins - backCoins).mongo_amount,
  121. DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: backCoins.mongo_amount
  122. })
  123. refund_money(self.device, backCoins, lineInfo['openId'])
  124. ServiceProgress.update_progress_and_consume_rcd(self.device['ownerId'],
  125. {'open_id': lineInfo['openId'], 'port': int(port),
  126. 'device_imei': self.device['devNo'],
  127. 'isFinished': False}, consumeDict)
  128. self.notify_user(user.managerialOpenId if user else '', 'refund_coins', **{
  129. 'title': u'%s 按动态功率计算总时间为:%s分钟,还有:%s分钟的时间未使用完,给您退还成相应的金币:%s元,您下次可以接着使用哦' % (
  130. self.event_data['reason'], actualNeedTime, leftTime, backCoins),
  131. 'backCount': u'金币:%s' % backCoins,
  132. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  133. })
  134. elif lineInfo['workMode'] in [1, 2]: # 智能模式统一按照计量模式进行判断
  135. self.notify_user(user.managerialOpenId if user else '', 'service_complete',
  136. **{
  137. 'title': u'%s 您购买的电量为:%s度,剩余电量:%s度' % (
  138. reason,
  139. lineInfo['needElec'],
  140. leftElec),
  141. 'service': u'充电服务(设备编号:%s, 端口:%s,地址:%s)' % (
  142. self.device['logicalCode'], port, group['address']),
  143. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  144. 'remark': u'谢谢您的支持'
  145. })
  146. # 如果需要退款,计算退款数据.
  147. if not self.device.is_auto_refund:
  148. consumeDict.update({
  149. DEALER_CONSUMPTION_AGG_KIND.COIN: coins.mongo_amount
  150. })
  151. ServiceProgress.update_progress_and_consume_rcd(self.device['ownerId'],
  152. {'open_id': lineInfo['openId'],
  153. 'port': int(port),
  154. 'device_imei': self.device['devNo'],
  155. 'isFinished': False}, consumeDict)
  156. return
  157. else:
  158. backCoins = RMB(coins.amount - coins.amount * (Decimal(lineInfo.get('needElec',1000)) - Decimal(str(leftElec)))/Decimal(lineInfo.get('needElec',1000)))
  159. if backCoins > coins:
  160. backCoins = coins
  161. consumeDict.update({
  162. DEALER_CONSUMPTION_AGG_KIND.COIN: (coins - backCoins).mongo_amount,
  163. DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: backCoins.mongo_amount
  164. })
  165. consumeDict.update({'refundedMoney': str(backCoins)})
  166. refund_money(self.device, backCoins, lineInfo['openId'])
  167. ServiceProgress.update_progress_and_consume_rcd(self.device['ownerId'],
  168. {'open_id': lineInfo['openId'], 'port': int(port),
  169. 'device_imei': self.device['devNo'],
  170. 'isFinished': False}, consumeDict)
  171. self.notify_user(user.managerialOpenId if user else '', 'refund_coins', **{
  172. 'title': u'%s 您购买的电量为:%s度,还有:%s度未使用完,给您退还成相应的金币:%s元,您下次可以接着使用哦' % (
  173. self.event_data['reason'], lineInfo['needElec'], leftElec, backCoins),
  174. 'backCount': u'金币:%s' % backCoins,
  175. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  176. })
  177. finally:
  178. Device.clear_port_control_cache(devNo, str(port))
  179. elif self.event_data['cmdCode'] == '04':
  180. notify_event_to_north(self.dealer, self.device, level = Const.EVENT_NORMAL,
  181. desc = self.event_data['reason'])