zhixia2.py 68 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import logging
  5. import random
  6. import time
  7. from typing import TYPE_CHECKING
  8. from apilib.monetary import sum_rmb, RMB, VirtualCoin, Ratio
  9. from apilib.utils_datetime import to_datetime
  10. from apps.web.common.models import District
  11. from apps.web.constant import Const, FAULT_CODE, FAULT_LEVEL, DEALER_CONSUMPTION_AGG_KIND
  12. from apps.web.core.accounting import Accounting
  13. from apps.web.core.helpers import ActionDeviceBuilder
  14. from apps.web.dealer.models import Dealer
  15. from apps.web.device.models import PortReport, Part, Group, Device
  16. from apps.web.device.timescale import FluentedEngine
  17. from apps.web.eventer import EventBuilder
  18. from apps.web.eventer.base import FaultEvent, WorkEvent
  19. from apps.web.south_intf.delixi import DelixiNorther
  20. from apps.web.south_intf.liangxi_fire import LiangXiXiaoFang
  21. from apps.web.south_intf.platform import notify_event_to_north, notify_event_to_north_v2, \
  22. handle_and_notify_event_to_north_Dc
  23. from apps.web.south_intf.yuhuan_fire import YuhuanNorther
  24. from apps.web.south_intf.zhejiang_fire import send_event_to_zhejiang
  25. from apps.web.user.models import VCardConsumeRecord, ServiceProgress, UserVirtualCard, CardRechargeOrder, MyUser, \
  26. Redpack, RechargeRecord
  27. from apps.web.user.models import Card
  28. if TYPE_CHECKING:
  29. from apps.web.device.models import GroupDict
  30. logger = logging.getLogger(__name__)
  31. class builder(EventBuilder):
  32. def __getEvent__(self, device_event):
  33. if 'data' not in device_event:
  34. return None
  35. # 100228 互感器事件处理
  36. if 'type' in device_event:
  37. if device_event['type'] == 'alert':
  38. return InteroperatorAlertEvent(self.deviceAdapter, device_event)
  39. if device_event['type'] == 'report':
  40. return InteroperatorReport(self.deviceAdapter, device_event)
  41. else:
  42. event_data = self.deviceAdapter.analyze_event_data(device_event['data'])
  43. if event_data is None or 'cmdCode' not in event_data:
  44. return None
  45. if 'duration' in device_event:
  46. event_data.update({'duration': device_event['duration']})
  47. if 'elec' in device_event:
  48. event_data.update({'elec': device_event['elec']})
  49. if 'v' in device_event:
  50. event_data.update({'v': device_event['v']})
  51. if 'today_coins' in device_event and 'ts' in device_event:
  52. event_data.update({'today_coins': device_event['today_coins']})
  53. event_data.update({'ts': device_event['ts']})
  54. if event_data.get('cmdCode') in ['03', '05', '11', '12', '17', '22']:
  55. return ChargingZHIXIA2WorkEvent(self.deviceAdapter, event_data)
  56. if event_data.get('cmdCode') == '0D':
  57. return ZHIXIA2FaultEvent(self.deviceAdapter, event_data)
  58. if event_data.get('cmdCode') == '35' or event_data.get('cmdCode') == '41':
  59. return ZHIXIA2InductorEvent(self.deviceAdapter, event_data)
  60. if event_data.get("cmdCode") == "2C":
  61. return ZhongRunYiHeCardEvent(self.deviceAdapter, event_data)
  62. return None
  63. class ChargingZHIXIA2WorkEvent(WorkEvent):
  64. @property
  65. def support_playback(self):
  66. if self.event_data['cmdCode'] in ['05']:
  67. return True
  68. else:
  69. return False
  70. def __parse_device_finished_data(self, event_data):
  71. duration = event_data.get('duration', -1)
  72. if duration != -1:
  73. if 'v' in event_data:
  74. duration = (duration / 60)
  75. elec = event_data.get('elec', -1)
  76. if elec != -1:
  77. if 'v' in event_data:
  78. elec = round(elec / (10000.0 * 3600.0), 3)
  79. else:
  80. elec = round(elec / 3600.0, 3)
  81. logger.debug('device duration is {}, device elec is {}'.format(duration, elec))
  82. return duration, elec
  83. def do(self, **args):
  84. logger.info('zhixiakeji2 charging event detected, devNo=%s,info=%s' % (self.device.devNo, self.event_data))
  85. if self.event_data['cmdCode'] == '03':
  86. if 'today_coins' in self.event_data and 'ts' in self.event_data:
  87. Accounting.syncOfflineCoin(
  88. self.device,
  89. datetime.datetime.fromtimestamp(self.event_data['ts']).strftime('%Y-%m-%d'),
  90. self.event_data['today_coins'])
  91. if self.event_data['coins'] > 0:
  92. FluentedEngine().in_put_coins_udp(devNo=self.device.devNo,
  93. ts=int(time.time()),
  94. coins=self.event_data['coins'],
  95. mode='uart')
  96. else:
  97. # 老的流程会单条记录上报记录
  98. Accounting.recordOfflineCoin(device=self.device,
  99. report_ts=int(time.time()),
  100. coins=int(self.event_data['coins']),
  101. mode='uart',
  102. port=self.event_data.get('port', None))
  103. try:
  104. # 如果是投币,直接把端口状态刷新下
  105. self.deviceAdapter.get_port_status_from_dev()
  106. except Exception, e:
  107. logger.info('some err=%s' % e)
  108. YuhuanNorther.send_dev_status(self.device, self.event_data['port'], 1)
  109. elif self.event_data['cmdCode'] == '22': # ID在线卡刷卡查询余额
  110. cardNo = self.event_data['cardNo']
  111. fee = RMB(self.event_data['fee'])
  112. cardType = self.event_data['cardType']
  113. card = self.update_card_dealer_and_type(cardNo)
  114. # 首先检查订单,并进行充值
  115. # 不存在卡的情况下 直接
  116. if not card:
  117. res = "03"
  118. elif not card.openId:
  119. res = '03'
  120. elif card.frozen:
  121. res = '04'
  122. # 有绑定的卡 并且卡没被冻结的情况下
  123. else:
  124. # 刷新卡的金额
  125. card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id))
  126. result = self.recharge_id_card(card=card, rechargeType='append', order=card_recharge_order)
  127. card.reload()
  128. logger.info('cardNo=%s,openId=%s,result=%s,fee=%s,info=%s' % (
  129. cardNo, card.openId, result, fee, self.event_data))
  130. res = "01" if result["balance"] >= fee else "02"
  131. balance = RMB(0) if not card else card.balance
  132. try:
  133. logger.info("card is <{}> fee is <{}> response res is <{}>".format(cardNo, fee, res))
  134. result = self.deviceAdapter.response_card_status(cardNo, balance, res)
  135. except Exception, e:
  136. logger.info('resp back error=%s' % e)
  137. return
  138. if fee <= RMB(0):
  139. # 仅仅是查询余额
  140. logger.debug(
  141. 'query the balance of card<cardNo={}> in device<devNo={}>'.format(card.cardNo, self.device.devNo))
  142. return
  143. # 设备回复扣款成功,服务器就需要正式扣掉钱
  144. if result['status'] == '01' and res == '01':
  145. virtual_card = card.bound_virtual_card
  146. if virtual_card is not None:
  147. group = Group.get_group(self.device['groupId'])
  148. VCardConsumeRecord(
  149. orderNo=VCardConsumeRecord.make_no(self.device.logicalCode),
  150. openId=card.openId,
  151. cardId=str(virtual_card.id),
  152. dealerId=card.dealerId,
  153. devNo=self.device.devNo,
  154. devTypeCode = self.device.devTypeCode,
  155. devTypeName = self.device.devTypeName,
  156. logicalCode=self.device['logicalCode'],
  157. groupId=group['groupId'],
  158. address=group['address'],
  159. groupNumber=self.device['groupNumber'],
  160. groupName=group['groupName'],
  161. ).save()
  162. else:
  163. self.consume_money_for_card(card, fee)
  164. # 记录卡消费记录以及消费记录
  165. orderNo, cardOrderNo = self.record_consume_for_card(card, fee)
  166. # 记录当前服务的progress.没有上报端口号,所以先用-1标记,表示端口未知。端口等消费的时候会报上来
  167. ServiceProgress.register_card_service(
  168. self.device, -1, card,
  169. {
  170. 'orderNo': orderNo,
  171. 'money': self.event_data['fee'],
  172. 'coin': self.event_data['fee'],
  173. 'needTime': 0,
  174. 'cardOrderNo': cardOrderNo
  175. }
  176. )
  177. # 通知微信,已经扣费
  178. self.notify_balance_has_consume_for_card(card, fee)
  179. elif self.event_data['cmdCode'] == '11' and self.event_data['status'] == '01': # 开始启动某个端口
  180. # 按下端口,开始使用。IC卡和ID卡都会上报这条信息。IC卡只记录消费记录信息,ID卡主要是上报了具体的某一个端口.ID卡和劲能的类似,主要是表示开始某一个端口以及回收余额
  181. cardType = self.event_data['cardType']
  182. card = self.update_card_dealer_and_type(self.event_data['cardNo'], cardType)
  183. # 这个地方将计费方式更新到 portDict 刷卡退费的时候会将刷卡的退费金额上报过来 结束事件需要这个字段 主要的是要更新consumeDict的字段
  184. # 0 按时间收费
  185. # 1 按度计费
  186. consumeModule = self.device.get("otherConf", dict()).get("consumeModule", 0)
  187. if consumeModule == 0:
  188. self.event_data["billingType"] = "time"
  189. else:
  190. self.event_data["billingType"] = "elec"
  191. if cardType == 'ID':
  192. consumeDict = {'chargeIndex': self.event_data['port']}
  193. queryDict = {'device_imei': self.device.devNo,
  194. 'port': -1, 'isFinished': False,
  195. 'cardId': str(card.id), 'start_time': {'$gte': int(time.time()) - 3600}}
  196. progressDict = {'port': self.event_data['port']}
  197. ServiceProgress.update_progress_and_consume_rcd(ownerId=self.device.ownerId,
  198. queryDict=queryDict, consumeDict=consumeDict,
  199. updateConsume=True, progressDict=progressDict)
  200. # 找出对应的卡的ID记录到端口内存数据
  201. queryDict.update(progressDict)
  202. rcds = ServiceProgress.get_collection().find(queryDict, {'consumeOrder': 1},
  203. sort=[('start_time', -1)])
  204. allCoins = sum_rmb([rcd['consumeOrder']['coin'] for rcd in rcds])
  205. try:
  206. d = Device.objects(devNo=self.device.devNo).first()
  207. billingType = d.otherConf.get('billingType', 'time')
  208. if self.device.bill_as_service_feature.on:
  209. billingType = 'elec'
  210. actionBox = ActionDeviceBuilder.create_action_device(Device.get_dev(self.device.devNo))
  211. cardTime = actionBox.get_freemode_volume_andsoon_config()['card1Time']
  212. icMoney = int(actionBox.get_IC_coin_power_config()['icMoney']) / 10
  213. allCardTime = int(int(allCoins) / icMoney) * int(cardTime)
  214. except Exception as e:
  215. logger.error('allCardTime error: %s' % e)
  216. allCardTime = 0
  217. billingType = 'time'
  218. self.event_data.update({'cardOrderTime': str(allCardTime)})
  219. self.event_data.update({'cardId': str(card.id)})
  220. self.event_data.update({'openId': card.openId})
  221. self.event_data.update({'coins': str(allCoins)})
  222. self.event_data.update({'startTime': datetime.datetime.now().strftime(Const.DATETIME_FMT)})
  223. self.event_data.update({'isStart': True, 'status': Const.DEV_WORK_STATUS_WORKING})
  224. self.event_data.update({'billingType': billingType})
  225. Device.update_port_control_cache(self.device.devNo, self.event_data) # 记录该端口累计需要的时间和钱,cardId
  226. else: # IC卡,需要记录消费记录
  227. if card is not None:
  228. fee = RMB(self.event_data['fee'])
  229. card.balance = RMB(self.event_data['balance'])
  230. try:
  231. card.save()
  232. except Exception, e:
  233. logger.exception(e)
  234. orderNo, cardOrderNo = self.record_consume_for_card(card, fee)
  235. ServiceProgress.register_card_service(self.device, self.event_data['port'], card,
  236. {'orderNo': orderNo, 'money': self.event_data['fee'],
  237. 'coin': self.event_data['fee'], 'cardOrderNo': cardOrderNo})
  238. # 通知微信,已经扣费
  239. self.notify_balance_has_consume_for_card(card, fee)
  240. self.event_data.update({'cardId': str(card.id)})
  241. self.event_data.update({'openId': card.openId})
  242. self.event_data.update({'coins': self.event_data['fee']})
  243. self.event_data.update({'startTime': datetime.datetime.now().strftime(Const.DATETIME_FMT)})
  244. self.event_data.update({'isStart': True, 'status': Const.DEV_WORK_STATUS_WORKING})
  245. Device.update_port_control_cache(self.device.devNo, self.event_data) # 记录该端口累计需要的时间和钱,cardId
  246. YuhuanNorther.send_dev_status(self.device, self.event_data['port'], 1)
  247. elif self.event_data['cmdCode'] == '17': # 余额回收
  248. card = self.update_card_dealer_and_type(self.event_data['cardNo'], 'IC') # type: Card
  249. if not card:
  250. logger.warning('Card<dealerId={},cardNo={},type=IC> is not registered.'.format(
  251. str(self.dealer.id), self.event_data['cardNo']))
  252. return
  253. self.refund_money_for_card(RMB(self.event_data['backMoney']), card.id)
  254. elif self.event_data['cmdCode'] == '12':
  255. cardNo = self.event_data['cardNo']
  256. preBalance = RMB(self.event_data['balance'])
  257. card = self.update_card_dealer_and_type(cardNo, 'IC', balance=preBalance) # type: Card
  258. if not card:
  259. return
  260. if card.frozen:
  261. logger.debug('{} has been frozen.'.format(repr(card)))
  262. return
  263. card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id)) # type: CardRechargeOrder
  264. if not card_recharge_order:
  265. logger.debug('{} has no recharge order.'.format(repr(card)))
  266. return
  267. self.recharge_ic_card(card=card,
  268. preBalance=preBalance,
  269. rechargeType='overwrite',
  270. order=card_recharge_order)
  271. elif self.event_data['cmdCode'] == '05':
  272. # 新版本的结束事件,比就版本多刷卡ID卡的在线退费
  273. devNo = self.device.devNo
  274. port = str(self.event_data['port'])
  275. try:
  276. # 这个lineInfo 就是缓存全部的信息
  277. lineInfo = Device.clear_port_control_cache(devNo, port)
  278. if not lineInfo:
  279. logger.debug('get null control cache from {}'.format(repr(self.device)))
  280. return
  281. logger.debug('port<{}> cache is: {}'.format(port, str(lineInfo)))
  282. if lineInfo.get('isApi', False) is True:
  283. self.event_data.update({'deviceCode': self.device['logicalCode']})
  284. return handle_and_notify_event_to_north_Dc(self.device["devNo"], self.event_data)
  285. if lineInfo.get("ZRYH", False):
  286. return self._do_zryh_card_finish(devNo, port, lineInfo)
  287. billingType = lineInfo.get("billingType", "time")
  288. if self.device.bill_as_service_feature.on:
  289. billingType = 'elec'
  290. # 红包退费的判断处理
  291. if 'redpackInfo' in lineInfo:
  292. for _info in lineInfo['redpackInfo']:
  293. redpack = Redpack.get_one(_info['redpackId'])
  294. redpackCoins = VirtualCoin(redpack['redpackCoins'])
  295. lineInfo['coins'] = float(VirtualCoin(lineInfo['coins']) - redpackCoins)
  296. redpackMoney = RMB(redpack['redpackMoney'])
  297. lineInfo['price'] = float(RMB(lineInfo['price']) - redpackMoney)
  298. logger.debug(
  299. 'redpack is <{}> redpack money is: {}; redpack coins is: {}'.format(redpack.get('id'),
  300. str(redpackMoney.amount),
  301. str(redpackCoins.amount)))
  302. if billingType == "elec":
  303. return self.do_elec_finish(devNo, port, lineInfo, self.event_data)
  304. else:
  305. return self.do_time_finish(devNo, port, lineInfo, self.event_data)
  306. finally:
  307. notify_event_to_north_v2(self.device["devNo"], self.event_data)
  308. notify_event_to_north(self.dealer, self.device, level=Const.EVENT_NORMAL,
  309. desc=self.event_data['reason'])
  310. send_event_to_zhejiang(self.dealer, self.device, self.event_data)
  311. if self.event_data['reasonCode'] == '04': # 功率过载导致的断电,一条告警,一条恢复告警
  312. YuhuanNorther.send_dev_event(self.device, '97', port)
  313. YuhuanNorther.send_dev_event(self.device, '98', port)
  314. # 发送一条端口使用结束的告警
  315. YuhuanNorther.send_dev_status(self.device, port, 2)
  316. def _do_zryh_card_finish(self, devNo, port, lineInfo):
  317. """
  318. 中润易和的卡的结束
  319. :param devNo:
  320. :param port:
  321. :param lineInfo:
  322. :return:
  323. """
  324. logger.info("zryh dev = {} receice car stop event = {}".format(self.device.devNo, self.event_data))
  325. coins = lineInfo["coins"]
  326. cardNo = lineInfo["cardNo"]
  327. leftTime = self.event_data["leftTime"]
  328. duration, elec = self.__parse_device_finished_data(self.event_data)
  329. # 时间重算一次
  330. needTime = str(min(int(duration) + int(leftTime), int(lineInfo["needTime"])))
  331. duration = str(int(needTime) - int(leftTime))
  332. card = self.update_card_dealer_and_type(cardNo=cardNo, cardType="ID")
  333. if not card:
  334. return
  335. consumeDict = {
  336. 'reason': self.event_data['reason'],
  337. 'leftTime': leftTime,
  338. 'chargeIndex': port,
  339. 'actualNeedTime': u"%s分钟" % needTime,
  340. 'duration': duration,
  341. }
  342. # 获取退费信息
  343. if self.device.is_auto_refund:
  344. refundMoney = VirtualCoin(coins) * Ratio(Ratio(leftTime).amount / Ratio(needTime).amount)
  345. if refundMoney:
  346. self.refund_money_for_card(refundMoney, lineInfo["cardId"])
  347. # 结算sp以及消费
  348. ServiceProgress.update_progress_and_consume_rcd(
  349. self.device.ownerId,
  350. {
  351. 'open_id': lineInfo['openId'],
  352. 'port': int(port),
  353. 'device_imei': self.device.devNo,
  354. 'isFinished': False
  355. },
  356. consumeDict
  357. )
  358. # 发出通知
  359. self.notify_user_service_complete(
  360. service_name=u'充电',
  361. openid=card.managerialOpenId if card else '',
  362. port=port,
  363. address=self.device.group['address'],
  364. reason=self.event_data['reason'],
  365. finished_time=to_datetime(self.recvTime).strftime('%Y-%m-%d %H:%M:%S'),
  366. extra=[{
  367. u'实体卡号': cardNo
  368. }]
  369. )
  370. def do_elec_finish(self, devNo, port, lineInfo, msgDict):
  371. """
  372. 电川的板子 按电量退费
  373. :return:
  374. """
  375. logger.info("[{} do_elec_finish] devNo = {}, port = {}, lineInfo = {}, msgDict = {}, event = {}".format(
  376. self.__class__.__name__, devNo, port, lineInfo, msgDict, self.event_data))
  377. if not self.dealer:
  378. logger.error(
  379. "[{} do_elec_finish] dealer {} is not found!".format(self.__class__.__name__, self.device.ownerId))
  380. return
  381. # 提取事件信息
  382. leftElec = self.event_data.get("leftTime", 0) / 100.0
  383. reasonCode = self.event_data.get("endType")
  384. reasonStr = self.event_data.get("reason")
  385. cardNo = self.event_data.get("cardNo", None)
  386. cardType = self.event_data.get("cardType", None)
  387. price = RMB(lineInfo.get('price', 0))
  388. startTime = lineInfo.get("startTime")
  389. # 投币事件
  390. if not startTime:
  391. return
  392. startTime = to_datetime(startTime)
  393. recvTime = to_datetime(self.recvTime)
  394. # 启动时间小于300秒的 直接进行保险退款
  395. if int((recvTime - startTime).total_seconds()) <= 300:
  396. rechargeIds = list()
  397. payInfo = lineInfo.get('payInfo', list())
  398. for item in payInfo:
  399. if 'rechargeRcdId' not in item:
  400. continue
  401. rechargeIds.append(item['rechargeRcdId'])
  402. # 回归正常的处理流程
  403. deviceDuration, deviceElec = self.__parse_device_finished_data(self.event_data)
  404. try:
  405. consumeDict = {
  406. "reason": reasonStr,
  407. "chargeIndex": port,
  408. "uartData": self.event_data.get('uartData', '')
  409. }
  410. # 计算充电的时间
  411. if deviceDuration and deviceDuration > 0:
  412. usedTime = deviceDuration
  413. else:
  414. if startTime > recvTime:
  415. usedTime = 0
  416. else:
  417. usedTime = int(round(((recvTime - startTime).total_seconds() / 60.0)))
  418. refundProtectionTime = self.device.get_other_conf_item('refundProtectionTime', 3)
  419. consumeDict.update({"duration": usedTime})
  420. # 获取组信息
  421. group = Group.get_group(self.device["groupId"])
  422. coins = VirtualCoin(lineInfo.get("coins"))
  423. # 扫码的结束
  424. if not cardNo:
  425. consumeDict.update({
  426. "reason": reasonStr,
  427. "chargeIndex": port
  428. })
  429. needElec = lineInfo.get("needElec")
  430. refundCoins = VirtualCoin(0)
  431. refundedMoney = RMB(0)
  432. if leftElec == int("FFFF", 16):
  433. refundCoins = VirtualCoin(coins)
  434. refundedMoney = RMB(price)
  435. leftElec = needElec
  436. spendElec = 0
  437. else:
  438. if usedTime < refundProtectionTime:
  439. refundCoins = VirtualCoin(coins)
  440. refundedMoney = RMB(price)
  441. spendElec = 0
  442. else:
  443. if leftElec > needElec:
  444. logger.error('left elec is bigger than need elec. something is wrong')
  445. leftElec = 0
  446. spendElec = needElec - leftElec
  447. refundSwitch = self.device.is_auto_refund
  448. if refundSwitch:
  449. refundCoins = VirtualCoin(coins) * Ratio(leftElec / needElec)
  450. refundedMoney = RMB(price) * Ratio(leftElec / needElec)
  451. logger.debug('left elec = {}, duration = {}, need elec = {}, back coins = {}, refund cash = {}'.format(
  452. leftElec, usedTime, needElec, refundCoins, refundedMoney))
  453. if 'consumeRcdId' in lineInfo and lineInfo['consumeRcdId']:
  454. # 虚拟卡的结束
  455. consumeRcdId = lineInfo['consumeRcdId']
  456. vCardId = lineInfo.get("vCardId", "")
  457. vCard = UserVirtualCard.objects(id=vCardId).first()
  458. if not vCard:
  459. logger.info('can not find the vCard id = %s' % vCardId)
  460. return
  461. try:
  462. ServiceProgress.update_progress_and_consume_rcd(
  463. self.device.ownerId,
  464. {
  465. 'open_id': lineInfo['openId'],
  466. 'port': int(port),
  467. 'device_imei': self.device.devNo,
  468. 'isFinished': False
  469. },
  470. consumeDict
  471. )
  472. if refundCoins > VirtualCoin(0):
  473. vCardConsumeRcd = VCardConsumeRecord.objects.get(id=consumeRcdId)
  474. vCard.refund_quota(vCardConsumeRcd, needElec - spendElec, spendElec,
  475. refundCoins.mongo_amount)
  476. finally:
  477. user = MyUser.objects(openId=lineInfo['openId'],
  478. groupId=self.device.groupId).first()
  479. self.notify_user_service_complete(
  480. service_name=u'充电',
  481. openid=user.managerialOpenId if user else '',
  482. port=port,
  483. address=group['address'],
  484. reason=self.event_data['reason'],
  485. finished_time=recvTime.strftime('%Y-%m-%d %H:%M:%S'),
  486. extra=[
  487. {u'虚拟卡号': vCard.cardNo}
  488. ]
  489. )
  490. elif 'openId' in lineInfo and lineInfo['openId']:
  491. # 扫码使用金币启动
  492. user = MyUser.objects(openId=lineInfo['openId'],
  493. groupId=self.device.groupId).first()
  494. try:
  495. is_cash = False
  496. if 'refundRMB_device_event' in self.device.owner.features and refundedMoney > RMB(0):
  497. is_cash = True
  498. consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.COIN: VirtualCoin(coins).mongo_amount})
  499. if refundedMoney > RMB(0) or refundCoins > VirtualCoin(0):
  500. consumeDict.update(
  501. {DEALER_CONSUMPTION_AGG_KIND.COIN: (VirtualCoin(coins) - refundCoins).mongo_amount})
  502. self.refund_net_pay(user, lineInfo, refundedMoney, refundCoins, consumeDict, is_cash)
  503. if self.device.bill_as_service_feature.on:
  504. serviceFee = round(spendElec * float(self.device.bill_as_service_feature.service_charge),2)
  505. elecFee = round(spendElec * float(self.device.bill_as_service_feature.elec_charge),2)
  506. consumeDict.update({"elec": spendElec, "elecFee": elecFee,"serviceFee":serviceFee})
  507. ServiceProgress.update_progress_and_consume_rcd(
  508. self.device.ownerId,
  509. {
  510. 'open_id': lineInfo['openId'],
  511. 'port': int(port),
  512. 'device_imei': self.device.devNo,
  513. 'isFinished': False
  514. },
  515. consumeDict)
  516. finally:
  517. extra = []
  518. if DEALER_CONSUMPTION_AGG_KIND.REFUNDED_CASH in consumeDict:
  519. real_refund = RMB(consumeDict[DEALER_CONSUMPTION_AGG_KIND.REFUNDED_CASH])
  520. if real_refund > RMB(0):
  521. extra.append({u'消费金额': '{}(元)'.format(RMB(price) - real_refund)})
  522. extra.append({u'退款金额': '{}(元)'.format(real_refund)})
  523. else:
  524. extra.append({u'消费金额': '{}(元)'.format(price)})
  525. if self.device.bill_as_service_feature.on:
  526. elecCharge = round(spendElec * float(self.device.bill_as_service_feature.elec_charge),2)
  527. serviceCharge = round(spendElec * float(self.device.bill_as_service_feature.service_charge),2)
  528. extra.append({u'服务费金额': '{}(元)'.format(serviceCharge)})
  529. extra.append({u'电费金额': '{}(元)'.format(elecCharge)})
  530. elif DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS in consumeDict:
  531. real_refund = VirtualCoin(consumeDict[DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS])
  532. if real_refund > VirtualCoin(0):
  533. extra.append({u'消费金额': '{}(金币)'.format(VirtualCoin(coins) - real_refund)})
  534. extra.append({u'退款金额': '{}(金币)'.format(real_refund)})
  535. else:
  536. extra.append({u'消费金额': '{}(金币)'.format(VirtualCoin(coins))})
  537. if self.device.bill_as_service_feature.on:
  538. elecCharge = round(spendElec * float(self.device.bill_as_service_feature.elec_charge),2)
  539. serviceCharge = round(spendElec * float(self.device.bill_as_service_feature.service_charge),2)
  540. extra.append({u'服务费金额': '{}(金币)'.format(serviceCharge)})
  541. extra.append({u'电费金额': '{}(金币)'.format(elecCharge)})
  542. else:
  543. extra.append({u'消费金额': '{}(金币)'.format(VirtualCoin(coins))})
  544. if self.device.bill_as_service_feature.on:
  545. elecCharge = spendElec * float(self.device.bill_as_service_feature.elec_charge)
  546. serviceCharge = spendElec * float(self.device.bill_as_service_feature.service_charge)
  547. extra.append({u'服务费金额': '{}(金币)'.format(serviceCharge)})
  548. extra.append({u'电费金额': '{}(金币)'.format(elecCharge)})
  549. self.notify_user_service_complete(
  550. service_name=u'充电',
  551. openid=user.managerialOpenId if user else '',
  552. port=port,
  553. address=group['address'],
  554. reason=self.event_data['reason'],
  555. finished_time=recvTime.strftime('%Y-%m-%d %H:%M:%S'),
  556. extra=extra)
  557. else:
  558. logger.error('not net pay rather user virtual card pay. something is wrong.')
  559. else:
  560. backMoney = self.event_data.get("backMoney")
  561. # 刷卡的结束 如果没有开启刷卡退费 则结束上报过来的backMoney = 0
  562. if cardType == "IC":
  563. # IC卡结束 只有刷卡结束的IC卡才能够刷新IC卡余额
  564. card = self.update_card_dealer_and_type(cardNo, "IC")
  565. if self.device.bill_as_service_feature.on:
  566. total = float(self.deviceAdapter.get_elecCharge_function())
  567. backMoney = leftElec * total
  568. needCoins = float(lineInfo.get('coins'))
  569. usedMoney = needCoins - backMoney
  570. elec = usedMoney / total
  571. elecFee = elec * float(self.device.bill_as_service_feature.elec_charge)
  572. serviceFee = elec * float(self.device.bill_as_service_feature.service_charge)
  573. consumeDict.update(
  574. {"elec": elec, "elecFee": elecFee, "serviceFee": serviceFee})
  575. if reasonCode == "05" and backMoney > 0:
  576. # 原因为05表示刷卡结束充电,会把退费金额报上来. 否则只能通过11指令报上来
  577. self.refund_money_for_card(VirtualCoin(backMoney), card.id)
  578. consumeDict.update({'balance': str(card.balance + VirtualCoin(backMoney))})
  579. else:
  580. consumeDict.update({'balance': str(card.balance)}) # IC卡必须下次刷卡的时候回收上来
  581. elif cardType == "ID":
  582. # ID卡结束 直接退费就行了
  583. card = self.update_card_dealer_and_type(cardNo, "ID")
  584. if self.device.bill_as_service_feature.on:
  585. total = self.device.otherConf.get('totalCharge')
  586. backMoney = round(leftElec * total,2)
  587. needCoins = float(lineInfo.get('coins'))
  588. usedMoney = needCoins - backMoney
  589. elec = round(usedMoney / total,2)
  590. elecFee = round(elec * float(self.device.bill_as_service_feature.elec_charge),2)
  591. serviceFee = round(elec * float(self.device.bill_as_service_feature.service_charge),2)
  592. consumeDict.update(
  593. {"elec": elec, "elecFee": elecFee, "serviceFee": serviceFee})
  594. if backMoney > 0:
  595. self.refund_money_for_card(RMB(backMoney), str(card.id))
  596. consumeDict.update({
  597. DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: VirtualCoin(backMoney).mongo_amount
  598. })
  599. consumeDict.update({'balance': str(card.balance + VirtualCoin(backMoney))})
  600. else:
  601. logger.error("invalid card type event data is <{}>".format(self.event_data))
  602. ServiceProgress.update_progress_and_consume_rcd(
  603. self.device.ownerId,
  604. {
  605. 'open_id': lineInfo['openId'],
  606. 'port': int(port),
  607. 'device_imei': self.device.devNo,
  608. 'isFinished': False
  609. },
  610. consumeDict
  611. )
  612. extra = [{u'实体卡号': cardNo}]
  613. if backMoney > 0:
  614. extra.append({
  615. u'退款金额': u'{}(金币)'.format(backMoney)
  616. })
  617. if self.device.bill_as_service_feature.on:
  618. extra.append({
  619. u'消费电量':u'{}(度)'.format(elec),
  620. u'电费金额': u'{}(金币)'.format(elecFee),
  621. u'服务费金额': u'{}(金币)'.format(serviceFee),
  622. })
  623. self.notify_user_service_complete(
  624. service_name=u'充电',
  625. openid=self.get_managerialOpenId_by_openId(lineInfo['openId']),
  626. port=port,
  627. address=group['address'],
  628. reason=self.event_data['reason'],
  629. finished_time=recvTime.strftime('%Y-%m-%d %H:%M:%S'),
  630. extra=extra
  631. )
  632. except Exception as e:
  633. logger.exception(e)
  634. def do_time_finish(self, devNo, port, lineInfo, msgDict):
  635. logger.info("[{} do_time_finish] devNo = {}, port = {}, lineInfo = {}, msgDict = {}, event = {}".format(
  636. self.__class__.__name__, devNo, port, lineInfo, msgDict, self.event_data))
  637. try:
  638. if not self.dealer:
  639. logger.error(
  640. "[{} do_time_finish] dealer {} is not found!".format(self.__class__.__name__, self.device.ownerId))
  641. return
  642. price = RMB(lineInfo.get('price', 0))
  643. refundProtectionTime = self.device.get_other_conf_item('refundProtectionTime', 5)
  644. deviceDuration, deviceElec = self.__parse_device_finished_data(self.event_data)
  645. recvTime = to_datetime(self.recvTime)
  646. if lineInfo is not None and 'startTime' in lineInfo:
  647. startTime = to_datetime(lineInfo['startTime'])
  648. if startTime > recvTime:
  649. logger.error('start time is bigger than now time,devNo={}'.format(devNo))
  650. serverDuration = -1
  651. else:
  652. serverDuration = int(round((((recvTime - startTime).total_seconds() + 59) / 60.0)))
  653. else:
  654. logger.info('lineinfo has not startTime,devNo=%s' % devNo)
  655. serverDuration = -1
  656. leftTime = self.event_data['leftTime']
  657. logger.debug('serverDuration = {}; deviceDuration = {}; leftTime = {}; lineInfo = {}'.format(
  658. serverDuration, deviceDuration, leftTime, lineInfo))
  659. if serverDuration < 0 and deviceDuration < 0:
  660. logger.warning('serverDuration and deviceDuration is valid. ignore this event.')
  661. return
  662. usedTime = 0
  663. if leftTime == 65535: # 设备返回65535说明端口没有启动
  664. leftTimeStr = u'端口未使用'
  665. actualNeedTime = 0
  666. else:
  667. usedTime = max(serverDuration, deviceDuration)
  668. leftTimeStr = leftTime
  669. actualNeedTime = usedTime + leftTime
  670. if usedTime < refundProtectionTime:
  671. # 通过使用的时间来判断是否可以进行保险的退款
  672. rechargeIds = list()
  673. payInfo = lineInfo.get('payInfo', list())
  674. for item in payInfo:
  675. if 'rechargeRcdId' not in item:
  676. continue
  677. rechargeIds.append(item['rechargeRcdId'])
  678. group = self.device.group # type: GroupDict
  679. consumeDict = {
  680. 'reason': self.event_data['reason'],
  681. 'leftTime': leftTimeStr,
  682. 'chargeIndex': port,
  683. 'actualNeedTime': u'动态功率计算为%s分钟' % actualNeedTime,
  684. 'duration': usedTime,
  685. 'deviceDuration': deviceDuration,
  686. 'serverDuration': serverDuration,
  687. 'uartData': self.event_data.get('uartData', '')
  688. }
  689. try:
  690. groupObj = Group.objects(id=self.device.groupId).first()
  691. if groupObj.otherConf.get('zhuxing', None) is not None:
  692. spendElec = round((float(random.randint(15, 19)) / 100) * (float(usedTime) / 60), 3)
  693. consumeDict.update({'elec': spendElec})
  694. consumeDict.update({'elecFee': self.deviceAdapter.calc_elec_fee(spendElec)})
  695. else:
  696. if deviceElec != -1:
  697. consumeDict.update({'elec': deviceElec})
  698. if deviceElec == 0 and usedTime > 0:
  699. consumeDict.update(
  700. {'elec': round((float(random.randint(15, 19)) / 100) * (float(usedTime) / 60), 3)})
  701. else:
  702. consumeDict.update({'elec': 0.0})
  703. consumeDict.update({'elecFee': RMB(0.0).mongo_amount})
  704. except Exception as e:
  705. logger.error("device <{}> elec add error! <{}>".format(self.device.devNo, e))
  706. # 涉及到卡的退费 不要以缓存为准 以设备上报的为准
  707. cardNo = self.event_data.get("cardNo", None)
  708. if cardNo:
  709. # 如果是刷卡的,直接更新消费记录,发通知消息,支持ID卡的退费。
  710. # IC卡的退费, 如果结束原因是刷卡退费(05), 则会把退费信息返回;
  711. # 否则不会把退费信息传过来,会通过11号命令返回
  712. cardType = self.event_data.get("cardType")
  713. if cardType == 'ID':
  714. card = self.update_card_dealer_and_type(cardNo=cardNo, cardType="ID")
  715. consumeDict.update({'balance': str(card.balance + VirtualCoin(self.event_data['backMoney']))})
  716. if self.event_data.has_key('backMoney') and self.event_data['backMoney'] > 0:
  717. dealer = Dealer.objects(id=self.device.ownerId).first()
  718. if dealer is not None and 'doNotRefundIDcardForDC' in dealer.features:
  719. pass
  720. else:
  721. self.refund_money_for_card(VirtualCoin(self.event_data['backMoney']), card.id)
  722. # 通知微信,已经退费
  723. self.notify_user(card.managerialOpenId, 'refund_coins', **{
  724. 'title': u'退币完成!您的卡号是%s,卡别名:%s' % (card.cardNo, card.nickName),
  725. 'backCount': u'金币:%s' % VirtualCoin(self.event_data['backMoney']),
  726. 'finishTime': recvTime.strftime('%Y-%m-%d %H:%M:%S')
  727. })
  728. elif cardType == "IC":
  729. card = self.update_card_dealer_and_type(cardNo=cardNo, cardType="IC")
  730. if not card:
  731. logger.warning('Card<dealerId={},cardNo={},type=IC> is not registered.'.format(
  732. str(self.dealer.id), cardNo))
  733. return
  734. # IC 并且结束code是05 表示卡贴上去了
  735. if self.event_data['endType'] == '05': # 刷卡退费结束的时候,才能够刷新IC卡的余额
  736. self.refund_money_for_card(VirtualCoin(self.event_data['backMoney']), card.id)
  737. consumeDict.update({'balance': str(card.balance + VirtualCoin(self.event_data['backMoney']))})
  738. else:
  739. consumeDict.update({'balance': str(card.balance)}) # IC卡必须下次刷卡的时候回收上来
  740. else:
  741. logger.error("invalid card type event data is <{}>".format(self.event_data))
  742. ServiceProgress.update_progress_and_consume_rcd(self.device.ownerId,
  743. {'open_id': lineInfo['openId'], 'port': int(port),
  744. 'device_imei': self.device.devNo,
  745. 'isFinished': False}, consumeDict)
  746. extra = [{
  747. u'实体卡号': lineInfo['cardNo']
  748. }]
  749. if VirtualCoin(self.event_data['backMoney']) > VirtualCoin(0):
  750. extra.append({
  751. u'退费金额': u'{}(金币)'.format(self.event_data['backMoney'])
  752. })
  753. templateMap = {
  754. 'cardCoins': lineInfo.get('coins', 0),
  755. 'cardOrderTime': lineInfo.get('cardOrderTime', 0),
  756. 'actualNeedTime': actualNeedTime,
  757. 'usedTime': usedTime,
  758. 'backCoins': self.event_data['backMoney'],
  759. 'reason': self.event_data['reason']
  760. }
  761. title = self.generate_service_complete_title_by_devType(self.device['devType']['id'], templateMap)
  762. if title != '':
  763. # 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}",
  764. self.notify_user(
  765. card.managerialOpenId if card else '',
  766. 'service_complete',
  767. **{
  768. 'title': title,
  769. 'service': u'充电服务',
  770. 'finishTime': recvTime.strftime('%Y-%m-%d %H:%M:%S'),
  771. 'remark': u'动态功率计算时间是指按照你的电动车功率大小进行折算出来的实际充电时间。'
  772. })
  773. else:
  774. self.notify_user_service_complete(
  775. service_name=u'充电',
  776. openid=card.managerialOpenId if card else '',
  777. port=port,
  778. address=group['address'],
  779. reason=self.event_data['reason'],
  780. finished_time=recvTime.strftime('%Y-%m-%d %H:%M:%S'),
  781. extra=extra)
  782. else:
  783. if 'coins' not in lineInfo:
  784. logger.warning('has no coins fields in lineInfo. may be coins start.')
  785. return
  786. coins = VirtualCoin(lineInfo['coins'])
  787. refundCoins = VirtualCoin(0)
  788. refundedMoney = RMB(0)
  789. if leftTime == 65535 or (usedTime < refundProtectionTime):
  790. refundCoins = coins
  791. refundedMoney = RMB(price)
  792. elif self.device.is_auto_refund:
  793. refundCoins = coins * (float(leftTime) / float(actualNeedTime))
  794. refundedMoney = RMB(price * (float(leftTime) / float(actualNeedTime)))
  795. if refundCoins > coins:
  796. refundCoins = coins
  797. if refundedMoney > RMB(price):
  798. refundedMoney = RMB(price)
  799. logger.debug('left time = {}, duration = {}, need time = {}, back coins = {}'.format(
  800. leftTime, usedTime, actualNeedTime, refundCoins))
  801. if 'consumeRcdId' in lineInfo and lineInfo['consumeRcdId']:
  802. vCardId = lineInfo['vCardId']
  803. vCard = UserVirtualCard.objects(id=vCardId).first() # type: UserVirtualCard
  804. if not vCard:
  805. logger.info('can not find the vCard id = %s' % vCardId)
  806. return
  807. try:
  808. ServiceProgress.update_progress_and_consume_rcd(self.device.ownerId,
  809. {'open_id': lineInfo['openId'],
  810. 'port': int(port),
  811. 'device_imei': self.device.devNo,
  812. 'isFinished': False}, consumeDict)
  813. if refundCoins > VirtualCoin(0):
  814. vCardConsumeRcd = VCardConsumeRecord.objects.get(id=lineInfo['consumeRcdId'])
  815. vCard.refund_quota(vCardConsumeRcd, usedTime, 0.0, refundCoins.mongo_amount)
  816. finally:
  817. user = MyUser.objects(openId=lineInfo['openId'],
  818. groupId=self.device.groupId).first()
  819. self.notify_user_service_complete(
  820. service_name=u'充电',
  821. openid=user.managerialOpenId if user else '',
  822. port=port,
  823. address=group['address'],
  824. reason=self.event_data['reason'],
  825. finished_time=recvTime.strftime('%Y-%m-%d %H:%M:%S'),
  826. extra=[
  827. {u'虚拟卡号': vCard.cardNo}
  828. ]
  829. )
  830. elif 'openId' in lineInfo and lineInfo['openId']:
  831. user = MyUser.objects(openId=lineInfo['openId'],
  832. groupId=self.device.groupId).first()
  833. try:
  834. is_cash = False
  835. if 'refundRMB_device_event' in self.device.owner.features and refundedMoney > RMB(0):
  836. is_cash = True
  837. consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.COIN: VirtualCoin(coins).mongo_amount})
  838. if refundedMoney > RMB(0) or refundCoins > VirtualCoin(0):
  839. consumeDict.update(
  840. {DEALER_CONSUMPTION_AGG_KIND.COIN: (VirtualCoin(coins) - refundCoins).mongo_amount})
  841. self.refund_net_pay(user, lineInfo, refundedMoney, refundCoins, consumeDict, is_cash)
  842. ServiceProgress.update_progress_and_consume_rcd(self.device.ownerId,
  843. {'open_id': lineInfo['openId'],
  844. 'port': int(port),
  845. 'device_imei': self.device.devNo,
  846. 'isFinished': False}, consumeDict)
  847. finally:
  848. extra = []
  849. if DEALER_CONSUMPTION_AGG_KIND.REFUNDED_CASH in consumeDict:
  850. real_refund = RMB(consumeDict[DEALER_CONSUMPTION_AGG_KIND.REFUNDED_CASH])
  851. if real_refund > RMB(0):
  852. extra.append({u'消费金额': '{}(元)'.format(RMB(price) - real_refund)})
  853. extra.append({u'退款金额': '{}(元)'.format(real_refund)})
  854. else:
  855. extra.append({u'消费金额': '{}(元)'.format(price)})
  856. elif DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS in consumeDict:
  857. real_refund = VirtualCoin(consumeDict[DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS])
  858. if real_refund > VirtualCoin(0):
  859. extra.append({u'消费金额': '{}(金币)'.format(VirtualCoin(coins) - real_refund)})
  860. extra.append({u'退款金额': '{}(金币)'.format(real_refund)})
  861. else:
  862. extra.append({u'消费金额': '{}(金币)'.format(VirtualCoin(coins))})
  863. else:
  864. extra.append({u'消费金额': '{}(金币)'.format(VirtualCoin(coins))})
  865. self.notify_user_service_complete(
  866. service_name=u'充电',
  867. openid=user.managerialOpenId if user else '',
  868. port=port,
  869. address=group['address'],
  870. reason=self.event_data['reason'],
  871. finished_time=recvTime.strftime('%Y-%m-%d %H:%M:%S'),
  872. extra=extra)
  873. else:
  874. logger.error('not net pay rather user virtual card pay. something is wrong.')
  875. except Exception as e:
  876. logger.exception(e)
  877. class ZHIXIA2FaultEvent(FaultEvent):
  878. LX_FAULE_CODE_MAP = {
  879. "01": "07",
  880. "02": "03",
  881. "03": "05"
  882. }
  883. def do_norther(self):
  884. """
  885. 上报其他平台的,都在这个地方处理 可迁移至异步任务
  886. :return:
  887. """
  888. # 玉环的消防对接
  889. YuhuanNorther.send_dev_event(self.device, self.event_data['FaultCode'], self.event_data['port'])
  890. DelixiNorther.send_dev_event(self.device, self.event_data)
  891. # 梁溪消防局的对接
  892. faultContent = self.event_data["statusInfo"]
  893. faultCode = self.LX_FAULE_CODE_MAP.get(self.event_data["FaultCode"], "")
  894. districtInfo = District.get_district(self.device.group["districtId"])
  895. self.device.update({"districtInfo": districtInfo, "groupAddr": self.device.group["address"]})
  896. LiangXiXiaoFang.send_to_xf_fault(self.device, faultCode, faultContent, self.event_data["port"])
  897. def do(self, **args):
  898. # 将告警的消息打入相应的缓存
  899. port = self.event_data["port"]
  900. # 0 表示整机
  901. if port == 0xFF:
  902. part = str(0)
  903. else:
  904. part = str(port)
  905. warningData = {
  906. "warningStatus": 2,
  907. "warningDesc": self.event_data["statusInfo"],
  908. "warningTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  909. "warningUart": self.event_data["uart"]
  910. }
  911. Device.update_dev_warning_cache(self.device.devNo, {part: warningData})
  912. self.do_norther()
  913. super(ZHIXIA2FaultEvent, self).do()
  914. class ZHIXIA2InductorEvent(FaultEvent):
  915. def do(self, **args):
  916. # 处理数据
  917. voltage = self.event_data.get('voltage', -1)
  918. temperature = self.event_data.get('temperature', -1)
  919. smokeWarning = self.event_data.get('smokeWarning', False)
  920. try:
  921. otherConf = Device.objects.get(devNo=self.device.devNo).otherConf
  922. maxVoltage = otherConf.get('voltageThre', 230)
  923. if voltage >= maxVoltage:
  924. desc = u'当前电压%s伏超过了门限值:%s伏' % (voltage, maxVoltage)
  925. self.record(faultCode=FAULT_CODE.OVER_VOLTAGE, description=desc, title=u'主机电压过高', detail=None,
  926. level=FAULT_LEVEL.CRITICAL)
  927. maxTemperature = otherConf.get('temThre', 60)
  928. if temperature > maxTemperature:
  929. desc = u'当前主机温度%s度超过了门限值:%s度' % (temperature, maxTemperature)
  930. self.record(faultCode=FAULT_CODE.OVER_TEMPERATURE, description=desc, title=u'主机温度过高',
  931. detail=None, level=FAULT_LEVEL.CRITICAL)
  932. self.send_fault_to_xf()
  933. if smokeWarning:
  934. desc = u'当前主机出现冒烟,请第一时间确定是否有起火。此告警十万火急,请迅速联系物业、消防相关部门!'
  935. self.record(faultCode=FAULT_CODE.SMOKE, description=desc, title=u'主机出现冒烟', detail=None,
  936. level=FAULT_LEVEL.FATAL)
  937. self.send_fault_to_xf()
  938. except Exception, e:
  939. logger.error('some error=%s' % e)
  940. return
  941. def send_fault_to_xf(self):
  942. try:
  943. code = self.event_data.get('cmdCode')
  944. # 无锡 梁溪区 粤万通 消防对接 推送
  945. if code == '41':
  946. faultCode = '02'
  947. faultContent = u'烟雾报警'
  948. elif code == '35':
  949. faultCode = '01'
  950. faultContent = u'温度报警'
  951. else:
  952. return
  953. from taskmanager.mediator import task_caller
  954. task_caller('send_to_xf_falut',
  955. dealerId=self.device.ownerId,
  956. devNo=self.device.devNo,
  957. faultCode=faultCode,
  958. faultContent=faultContent)
  959. except Exception:
  960. pass
  961. class InteroperatorAlertEvent(FaultEvent):
  962. def __Analyze_alert_data(self, data):
  963. alertInfo = {'cmdCode': data['cmd'], 'logicalCode': self.device['logicalCode']}
  964. address = Group.get_group(self.device['groupId'])['address']
  965. # 这里判断数据格式
  966. if 'status' not in data:
  967. logger.error('Data arrays have no keywords status')
  968. return
  969. # 这里做漏电告警处理
  970. if '5' in data['status']:
  971. electricityNum = str(int(data['values'][0:4], 16)) + 'mA'
  972. alertInfo['electricity'] = {'electricityNum': electricityNum,
  973. 'address': address,
  974. 'reasonCode': '12',
  975. 'reason': u'在{}编号为{}发生漏电,漏电量为{}'
  976. .format(address, self.device['logicalCode'], electricityNum)}
  977. # 这里做高温告警处理
  978. if '6' in data['status']:
  979. temperatureAccess = [index for index, acces in enumerate(data['status'], 1) if acces == '6']
  980. temperatureAlertList = []
  981. for i in temperatureAccess:
  982. temperatureValue = str(int(data['values'][(i - 1) * 4:(i - 1) * 4 + 4], 16))
  983. temperatureAlertList.append(
  984. {'temperatureValue': temperatureValue,
  985. 'address': address,
  986. 'reasonCode': '11',
  987. 'reason': u'在{}编号为{}的设备有高温预警,当前温度为{}摄氏度'
  988. .format(address, self.device['logicalCode'], temperatureValue)})
  989. alertInfo['temperature'] = temperatureAlertList
  990. return alertInfo
  991. def do(self, **args):
  992. # 判断不存在的设备网上报
  993. if not self.device.ownerId:
  994. logger.error('This device cant find a dealer')
  995. return
  996. # 是否存在温感和电感
  997. temperaturePart = Part.objects(logicalCode=self.device['logicalCode'], partType='3001')
  998. electricityPart = Part.objects(logicalCode=self.device['logicalCode'], partType='3002')
  999. if not temperaturePart.count() or not electricityPart.count():
  1000. logger.error(
  1001. 'There are no transformers in the locigalcode {} equipment'.format(self.device['logicalCode']))
  1002. return
  1003. # 处理数据
  1004. eventInfo = self.__Analyze_alert_data(self.event_data['data'])
  1005. try:
  1006. # 先处理高温情况
  1007. if 'temperature' in eventInfo:
  1008. for InfoDetail in eventInfo['temperature']:
  1009. send_event_to_zhejiang(self.dealer, self.device, InfoDetail, partId=temperaturePart[0].id)
  1010. # 提示用户
  1011. group = Group.get_group(self.device['groupId'])
  1012. self.notify_dealer('device_fault', **{
  1013. 'title': u'注意!注意!您的设备发生故障',
  1014. 'device': u'组号::%s, 二维码编号:%s' % (self.device['groupNumber'], self.device['logicalCode']),
  1015. 'location': u'组名称:%s, 地址:%s' % (group['groupName'], group['address']),
  1016. 'fault': InfoDetail['reason'],
  1017. 'notifyTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  1018. })
  1019. # 上报高温至消防
  1020. # if self.device["ownerId"] in ("5b4ed32e8732d67bd0626528", "5b6c29388732d669f3ae6f94"):
  1021. group = Group.get_group(self.device['groupId'])
  1022. districtInfo = District.get_district(group["districtId"])
  1023. self.device.update({"districtInfo": districtInfo, "groupAddr": group["address"]})
  1024. LiangXiXiaoFang.send_to_xf_fault(self.device, "01", u"设备温度过高")
  1025. # 处理漏电情况
  1026. elif 'electricity' in eventInfo:
  1027. # 获取漏电告警插件
  1028. send_event_to_zhejiang(self.dealer, self.device, eventInfo['electricity'],
  1029. partId=electricityPart[0].id)
  1030. # 提示用户
  1031. group = Group.get_group(self.device['groupId'])
  1032. self.notify_dealer('device_fault', **{
  1033. 'title': u'注意!注意!您的设备发生故障',
  1034. 'device': u'组号::%s, 二维码编号:%s' % (self.device['groupNumber'], self.device['logicalCode']),
  1035. 'location': u'组名称:%s, 地址:%s' % (group['groupName'], group['address']),
  1036. 'fault': eventInfo['electricity']['reason'],
  1037. 'notifyTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  1038. })
  1039. # 上报漏电至消防
  1040. # if self.device["ownerId"] in ("5b4ed32e8732d67bd0626528", "5b6c29388732d669f3ae6f94"):
  1041. group = Group.get_group(self.device['groupId'])
  1042. districtInfo = District.get_district(group["districtId"])
  1043. self.device.update({"districtInfo": districtInfo, "groupAddr": group["address"]})
  1044. LiangXiXiaoFang.send_to_xf_fault(self.device, "04", u"设备发生漏电")
  1045. except:
  1046. logger.error('Array {} nonspecification'.format(eventInfo))
  1047. return
  1048. self.record(detail=eventInfo)
  1049. class InteroperatorReport(WorkEvent):
  1050. def do(self, **args):
  1051. if 'type' not in self.event_data:
  1052. logger.error('Array {} is not format,lose a key named "type"'.format(self.event_data))
  1053. if self.event_data.get('type') == 'report':
  1054. devReportDict = {'logicalCode': 'logicalCode', 'time': self.event_data['time_stamp'], 'portInfo': {}}
  1055. temperature = ''
  1056. voltage = 220
  1057. try:
  1058. # 拿到个数判断是不是第一次
  1059. reportNum = PortReport.get_collection().find({
  1060. 'logicalCode': self.device['logicalCode']
  1061. }).sort('time', -1).count()
  1062. if reportNum:
  1063. # 获取上一次存储的信息
  1064. reportLast = PortReport.get_collection().find({
  1065. 'logicalCode': self.device['logicalCode']
  1066. }).sort('time', -1)[0]
  1067. for ii in range(10):
  1068. power = self.__saveDate(1, msgDict=self.event_data, ii=ii)
  1069. if power:
  1070. electricity = float(power) / voltage / 10
  1071. else:
  1072. electricity = reportLast['portInfo'][str(ii + 1)]['electricity']
  1073. temperatureR = self.__saveDate(2, msgDict=self.event_data, ii=ii, electricity=electricity,
  1074. devReportDict=devReportDict)
  1075. if temperatureR:
  1076. temperature = temperatureR
  1077. devReportDict.update({'temperature': temperature})
  1078. # 查看现在的跟以前差距多少
  1079. timeInterval = devReportDict['time'] - reportLast['time']
  1080. if timeInterval > 2:
  1081. PortReportNewList = [
  1082. {"logicalCode": self.device['logicalCode'], "temperature": reportLast['temperature'],
  1083. 'portInfo': reportLast['portInfo'],
  1084. 'time': reportLast['time'] + (v + 1) * 2}
  1085. for v in range(int(timeInterval / 2) - 1)]
  1086. PortReport.get_collection().insert_many(PortReportNewList)
  1087. # 首存的情况
  1088. else:
  1089. for ii in range(10):
  1090. power = self.__saveDate(1, msgDict=self.event_data, ii=ii)
  1091. electricity = float(power) / voltage / 10
  1092. temperatureR = self.__saveDate(2, msgDict=self.event_data, ii=ii, electricity=electricity,
  1093. devReportDict=devReportDict)
  1094. if temperatureR:
  1095. temperature = temperatureR
  1096. devReportDict.update({'temperature': temperature})
  1097. except Exception, e:
  1098. logger.error('solve dev=%s device report has an error e=%s' % (self.device.devNo, e))
  1099. finally:
  1100. newInfo = PortReport(
  1101. logicalCode=self.device['logicalCode'],
  1102. temperature=devReportDict['temperature'],
  1103. time=devReportDict['time'],
  1104. portInfo=devReportDict['portInfo']
  1105. )
  1106. newInfo.save()
  1107. def __saveDate(self, data, msgDict, ii, electricity=None, devReportDict=None):
  1108. # 存储数据库
  1109. if data == 1:
  1110. powerData = msgDict['data']['power_data'][0 + 4 * ii:4 + 4 * ii]
  1111. power = int(powerData, 16)
  1112. return power
  1113. if data == 2:
  1114. temperature = ''
  1115. status = 'idle' if electricity == 0 else 'busy'
  1116. devReportDict['portInfo'].update(
  1117. {str(ii + 1): {'electricity': round(electricity, 3), 'status': status}})
  1118. if ii < 4 and msgDict['data']['temp_data'][0 + 4 * ii:4 + 4 * ii] != '0000':
  1119. temperatureNum = msgDict['data']['temp_data'][0 + 4 * ii:4 + 4 * ii]
  1120. temperature = int(temperatureNum, 16)
  1121. return temperature
  1122. class ZhongRunYiHeCardEvent(WorkEvent):
  1123. # 中润易和 的刷卡启动
  1124. def do(self, **args):
  1125. logger.info("zhong run yi he card event, event data = {}".format(self.event_data))
  1126. cardNo = self.event_data["cardNo"]
  1127. portStr = self.event_data["portStr"]
  1128. num = min(self.event_data["num"], 3) # 最多刷卡3次
  1129. # 是否是有效刷卡 当端口号为0的时候无效
  1130. if portStr == 0:
  1131. logger.info("illegal card start ,cardNo = {}, devNo = {}".format(cardNo, self.device.devNo))
  1132. return
  1133. # 判断卡
  1134. card = self.update_card_dealer_and_type(cardNo) # type: Card
  1135. if not card or card.frozen:
  1136. logger.info("illegal card ,cardNo = {}, devNo = {}".format(cardNo, self.device.devNo))
  1137. return
  1138. card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id))
  1139. self.recharge_id_card(card, rechargeType="append", order=card_recharge_order)
  1140. card.reload()
  1141. # 判断钱
  1142. otherConf = self.device.get("otherConf") or dict()
  1143. try:
  1144. icMoney = otherConf['icMoney']
  1145. except KeyError:
  1146. icSettings = self.deviceAdapter.get_IC_coin_power_config()
  1147. icMoney = icSettings['icMoney']
  1148. needMoney = RMB(int(icMoney) / 10.0) * Ratio(num)
  1149. if card.balance < needMoney:
  1150. logger.info(
  1151. "more Money, cardNo = {}, devNo = {}, icMoney = {}, num = {}".format(cardNo, self.device.devNo, icMoney,
  1152. num))
  1153. return
  1154. # 然后就是启动设备
  1155. # 获取启动的时间
  1156. try:
  1157. card1Time = otherConf["card1Time"]
  1158. card2Time = otherConf["card2Time"]
  1159. card3Time = otherConf["card3Time"]
  1160. except KeyError:
  1161. cardTimeSettings = self.deviceAdapter.get_freemode_volume_andsoon_config()
  1162. card1Time = cardTimeSettings["card1Time"]
  1163. card2Time = cardTimeSettings["card2Time"]
  1164. card3Time = cardTimeSettings["card3Time"]
  1165. if num == 1:
  1166. needTime = card1Time
  1167. elif num == 2:
  1168. needTime = card1Time + card2Time
  1169. else:
  1170. needTime = card1Time + card2Time + card3Time
  1171. self.deviceAdapter._start_device_for_card(needTime, portStr)
  1172. # 记录卡消费
  1173. card.balance -= needMoney
  1174. card.save()
  1175. orderNo, cardOrderNo = self.record_consume_for_card(card, needMoney, desc=u"刷卡消费")
  1176. serviceDict = {
  1177. "orderNo": orderNo,
  1178. "cardOrderNo": cardOrderNo
  1179. }
  1180. # 记录缓存
  1181. ServiceProgress.register_card_service(
  1182. self.device,
  1183. int(portStr),
  1184. card,
  1185. serviceDict
  1186. )
  1187. portDict = {
  1188. "orderNo": orderNo, # 订单号码
  1189. "cardOrderNo": cardOrderNo,
  1190. "cardNo": cardNo, # 卡号 0-99999999
  1191. 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  1192. 'coins': str(needMoney),
  1193. 'price': str(needMoney),
  1194. 'isStart': True,
  1195. 'openId': card.openId,
  1196. 'ZRYH': True,
  1197. "needTime": needTime,
  1198. "cardId": str(card.id)
  1199. }
  1200. Device.update_dev_control_cache(self.device["devNo"], {portStr: portDict})