bolai_ten.py 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import logging
  4. import time
  5. import datetime
  6. from mongoengine import DoesNotExist
  7. from apilib.utils_string import make_title_from_dict
  8. from apilib.monetary import VirtualCoin, Ratio, RMB
  9. from apps.web.constant import Const
  10. from apps.web.device.models import Device, Group
  11. from apps.web.eventer import EventBuilder
  12. from apps.web.eventer.base import WorkEvent
  13. from apps.web.user.models import ConsumeRecord, ServiceProgress, MyUser, CardConsumeRecord, Card, VCardConsumeRecord, \
  14. UserVirtualCard
  15. from apps.web.user.transaction_deprecated import refund_money
  16. logger = logging.getLogger(__name__)
  17. class builder(EventBuilder):
  18. def __getEvent__(self, device_event):
  19. return BolaiTenWorkEvent(self.deviceAdapter, device_event)
  20. class BolaiTenWorkEvent(WorkEvent):
  21. def __translate_reason(self, cause):
  22. if cause == 0:
  23. return u'结束充电。'
  24. elif cause == 1:
  25. return u'正在充电的时候,结束了,可能是用户拔掉充电器,或者插座脱落,或者没有接入充电器'
  26. elif cause == 2:
  27. return u'充满自停。'
  28. elif cause == 3:
  29. return u'金额已经用完。'
  30. elif cause == 4:
  31. return u'平台下发停止充电'
  32. return u'充电结束。'
  33. # 网关心跳
  34. def event_gateway_heartbeat(self):
  35. rssi = self.event_data.get('rssi')
  36. logger.info('receive EVENT_GATEWAY_HEARTBEAT')
  37. Device.update_online_cache(self.device.devNo, True, rssi, (long(time.time() * 1000) - 600000))
  38. # 充电结束上报按量
  39. def do_finished_by_quantity(self, data):
  40. """
  41. {
  42. "type": "EVENT_CHARGE_FINISHED",
  43. "time": 1548141575, # 事件时间戳
  44. "gateway_id": "99503935742305", # 网关uid
  45. "data": {
  46. "node_index": 1, # 插座号
  47. "port_index": 1, # 插孔号
  48. "transaction_id": "1", # 业务号
  49. "charge_status": 2, # 0表示未充电,1表示充电,2表示充满自停 ,3金额用完(时间到)
  50. "time_consumed": 0, # 已耗时,单位分
  51. "energy_consumed": 0, # 已耗电能,单位Wh
  52. "flag_over_load": 0, # 过载标志
  53. "flag_no_load": 0, # 空载标志
  54. "flag_over_current": 0, # 过流标志
  55. }
  56. }
  57. """
  58. reason = self.__translate_reason(data['data']['charge_status'])
  59. orderNo = data['data']['transaction_id']
  60. dev = self.device
  61. devNo = dev['devNo']
  62. consumeRcd = ConsumeRecord.objects.filter(sequanceNo=orderNo).first()
  63. if not consumeRcd: # 投币的不会生成订单
  64. return
  65. port = consumeRcd.attachParas['chargeIndex']
  66. try:
  67. servicedInfo = consumeRcd.servicedInfo
  68. cardNo = servicedInfo.get('cardNo') or ''
  69. if cardNo:
  70. data['data'].update({'card_no': cardNo})
  71. self.do_card_finished(data)
  72. except Exception, e:
  73. logger.exception('some exception happed,devNo=%s,e=%s' % (devNo, e))
  74. progress = ServiceProgress.objects.filter(device_imei=self.device.devNo,
  75. port=int(port), isFinished=False).first() # type: ServiceProgress
  76. if progress is None:
  77. return
  78. progress.status = 'finished'
  79. progress.isFinished = True
  80. progress.expireAt = datetime.datetime.now()
  81. progress.save()
  82. try:
  83. group = Group.get_group(dev['groupId'])
  84. billingType = servicedInfo['billingType']
  85. usedTime = data['data']['time_consumed']
  86. usedElec = data['data']['energy_consumed'] * 0.001
  87. consumeDict = {
  88. 'reason': reason,
  89. 'chargeIndex': str(port),
  90. 'duration': usedTime,
  91. 'elec': usedElec
  92. }
  93. consumeDict.update(servicedInfo)
  94. notifyDict = {
  95. 'service': u'充电服务完成',
  96. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  97. 'remark': u'谢谢您的支持'
  98. }
  99. refundDict = {
  100. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  101. }
  102. coins = consumeRcd.coin
  103. if billingType == 'time': # 按照时间计费
  104. needTime = servicedInfo['needTime']
  105. leftTime = needTime - usedTime if needTime > usedTime else 0
  106. leftTimeStr = leftTime
  107. consumeDict.update({
  108. 'needTime': needTime,
  109. 'leftTime': leftTimeStr,
  110. })
  111. titleDictList = [
  112. {u'设备编号': devNo},
  113. {u'端口': str(port)},
  114. {u'地址': group['address']},
  115. {u'结束原因': reason},
  116. {u'订购时间': u'%s分钟' % needTime},
  117. {u'充电时间': u'%s分钟' % usedTime},
  118. {u'剩余时间': u'%s分钟' % leftTimeStr}
  119. ]
  120. notifyDict.update(
  121. {
  122. 'title': make_title_from_dict(titleDictList)
  123. }
  124. )
  125. backCoins = Ratio(float(leftTime) / float(needTime)) * VirtualCoin(coins)
  126. titleDictList = [
  127. {u'设备编号': devNo},
  128. {u'端口': str(port)},
  129. {u'付款': u'%s元' % coins},
  130. {u'预定时间': u'%s分钟' % needTime},
  131. {u'充电时间': u'%s分钟' % usedTime},
  132. {u'剩余时间': u'%s分钟' % leftTimeStr},
  133. ]
  134. refundDict.update(
  135. {
  136. 'title': make_title_from_dict(titleDictList),
  137. 'backCount': u'金币:%s' % backCoins
  138. }
  139. )
  140. else: # 按电量付费的
  141. needElec = servicedInfo['needElec']
  142. leftElec = needElec - usedElec if needElec > usedElec else 0.0
  143. consumeDict.update({
  144. 'needElec': needElec,
  145. 'leftElec': leftElec
  146. })
  147. titleDictList = [
  148. {u'设备编号': devNo},
  149. {u'端口': str(port)},
  150. {u'结束原因': reason},
  151. {u'订购电量': u'%s度' % needElec},
  152. {u'消耗电量': u'%s度' % usedElec},
  153. {u'剩余电量': u'%s度' % leftElec},
  154. ]
  155. notifyDict.update(
  156. {
  157. 'title': make_title_from_dict(titleDictList)
  158. }
  159. )
  160. backCoins = Ratio(float(leftElec) / float(needElec)) * VirtualCoin(coins)
  161. titleDictList = [
  162. {u'设备编号': devNo},
  163. {u'端口': str(port)},
  164. {u'结束原因': reason},
  165. {u'订购电量': u'%s度' % needElec},
  166. {u'消耗电量': u'%s度' % usedElec},
  167. {u'剩余电量': u'%s度' % leftElec},
  168. ]
  169. refundDict.update(
  170. {
  171. 'title': make_title_from_dict(titleDictList),
  172. 'backCount': u'金币:%s' % backCoins
  173. }
  174. )
  175. if u'虚拟卡' in consumeRcd.remarks:
  176. # 退额度
  177. try:
  178. vRcd = VCardConsumeRecord.objects.get(orderNo=consumeRcd.orderNo)
  179. vCard = UserVirtualCard.objects.get(id=vRcd.cardId)
  180. except DoesNotExist, e:
  181. logger.info('can not find the vCard id = %s' % vRcd.cardId)
  182. return
  183. # 通知服务结束
  184. notifyOpenId = self.get_managerialOpenId_by_openId(vRcd.openId) if vCard else ''
  185. self.notify_user(notifyOpenId, 'service_complete', **notifyDict)
  186. # 不需要退款,直接返回,不通知
  187. if self.device.is_auto_refund:
  188. refundProtectionTime = self.device.get("otherConf", dict()).get("refundProtectionTime", 5)
  189. if usedTime <= refundProtectionTime:
  190. backCoins = coins
  191. if billingType == 'time':
  192. vCard.refund_quota(vRcd, usedTime, 0.0, backCoins.mongo_amount)
  193. else:
  194. vCard.refund_quota(vRcd, 0.0, usedElec, backCoins.mongo_amount)
  195. else: # 扫码的
  196. user = MyUser.objects(openId=consumeRcd.openId, groupId=dev['groupId']).first()
  197. if not user:
  198. return
  199. # 通知服务结束
  200. notifyOpenId = user.managerialOpenId if user else ''
  201. self.notify_user(notifyOpenId, 'service_complete', **notifyDict)
  202. # 如果需要退款,计算退款数据.
  203. if self.device.is_auto_refund:
  204. refundProtectionTime = self.device.get("otherConf", dict()).get("refundProtectionTime",5)
  205. if usedTime <= refundProtectionTime:
  206. backCoins = coins
  207. consumeDict.update({'refundedMoney': str(backCoins)})
  208. refund_money(dev, backCoins, consumeRcd.openId)
  209. self.notify_user(notifyOpenId, 'refund_coins', **refundDict)
  210. ServiceProgress.update_progress_and_consume_rcd(dev['ownerId'], {
  211. "open_id": notifyOpenId,
  212. "port": int(port),
  213. "device_imei": self.device.devNo,
  214. "isFinished": False
  215. },
  216. consumeDict)
  217. except Exception, e:
  218. logger.exception('some exception happed,devNo=%s,e=%s' % (devNo, e))
  219. finally:
  220. Device.clear_port_control_cache(self.device.devNo,port)
  221. self.deviceAdapter.get_port_status_from_dev()
  222. # 充电结束上报按功率
  223. def do_finished_by_power(self, data):
  224. """
  225. {
  226. "type": "EVENT_CHARGE_FINISHED_3",
  227. "time": 1548141575, # 事件时间戳
  228. "gateway_id": "99503935742305", # 网关uid
  229. "data": {
  230. "node_index": 1, # 插座号
  231. "port_index": 1, # 插孔号
  232. "charge_status": 2, # 0表示未充电,1表示充电,2表示充满自停 ,3金额用完(时间到)
  233. "flag_over_load": 0, # 过载标志
  234. "flag_no_load": 0, # 空载标志
  235. "flag_over_current": 0, # 过流标志
  236. "transaction_id": "1", # 业务号
  237. "power": 1, # 当前功率
  238. "current": 1, # 当前电流
  239. "energy_consumed": 0, # 已耗电能,单位Wh
  240. "time_consumed": 0, # 已耗时,单位分
  241. "finish_time": 1548141575, # 结束时间
  242. "finish_reason": 2, # 结束原因,1表示充满自停,2表示异常停止,3表示正常停止(时间到),4平台下发停止
  243. "money_consumed": 100, # 该参数禁止使用
  244. "settle_power": 600, # 结算功率,单位W
  245. "power_time_stat": "2,0" # 结算功率档位时间统计,时间用半角逗号","隔开,单位分
  246. }
  247. }
  248. """
  249. reason = self.__translate_reason(data['data']['charge_status'])
  250. orderNo = data['data']['transaction_id']
  251. dev = self.device
  252. devNo = dev['devNo']
  253. consumeRcd = ConsumeRecord.objects.filter(sequanceNo=orderNo).first()
  254. if not consumeRcd: # 投币的不会生成订单
  255. return
  256. port = consumeRcd.attachParas['chargeIndex']
  257. try:
  258. servicedInfo = consumeRcd.servicedInfo
  259. cardNo = servicedInfo.get('cardNo') or ''
  260. if cardNo:
  261. data['data'].update({'card_no': cardNo})
  262. self.do_card_finished(data)
  263. except Exception, e:
  264. logger.exception('some exception happed,devNo=%s,e=%s' % (devNo, e))
  265. progress = ServiceProgress.objects.filter(device_imei=self.device.devNo,
  266. port=int(port), isFinished=False).first() # type: ServiceProgress
  267. if progress is None or progress.status == 'finished':
  268. return
  269. progress.status = 'finished'
  270. progress.isFinished = True
  271. progress.expireAt = datetime.datetime.now()
  272. progress.save()
  273. try:
  274. group = Group.get_group(dev['groupId'])
  275. spendMoney = self.calc_spend_money(data)
  276. coins = consumeRcd.coin
  277. if spendMoney >= coins:
  278. spendMoney = coins
  279. usedTime = data['data']['time_consumed']
  280. usedElec = data['data']['energy_consumed']
  281. consumeDict = {
  282. 'reason': reason,
  283. 'chargeIndex': str(int(port)+1),
  284. 'spendMoney': str(spendMoney),
  285. 'settlePower': data['data']['settle_power'],
  286. 'usedTime': usedTime,
  287. 'usedElec': usedElec,
  288. 'power': data['data']['power'],
  289. 'current': data['data']['current']
  290. }
  291. consumeDict.update(consumeRcd.servicedInfo)
  292. notifyDict = {
  293. 'service': u'充电服务完成',
  294. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  295. 'remark': u'谢谢您的支持'
  296. }
  297. refundDict = {
  298. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  299. }
  300. backCoins = coins - spendMoney if coins > spendMoney else VirtualCoin(0.0)
  301. usedTime = data['data']['time_consumed']
  302. consumeDict.update({
  303. 'duration': usedTime,
  304. })
  305. titleDictList = [
  306. {u'设备编号': devNo},
  307. {u'端口': str(port)},
  308. {u'地址': group['address']},
  309. {u'结束原因': reason},
  310. {u'结算功率': u'%s瓦' % data['data']['settle_power']},
  311. {u'使用时间': u'%s分钟' % usedTime}
  312. ]
  313. notifyDict.update(
  314. {
  315. 'title': make_title_from_dict(titleDictList)
  316. }
  317. )
  318. refundDict.update(
  319. {
  320. 'title': make_title_from_dict(titleDictList),
  321. 'backCount': u'金币:%s' % backCoins
  322. }
  323. )
  324. if u'虚拟卡' in consumeRcd.remarks:
  325. # 退额度
  326. try:
  327. vRcd = VCardConsumeRecord.objects.get(orderNo=consumeRcd.orderNo)
  328. vCard = UserVirtualCard.objects.get(id=vRcd.cardId)
  329. except DoesNotExist, e:
  330. logger.info('can not find the vCard id = %s' % vRcd.cardId)
  331. return
  332. # 通知服务结束
  333. notifyOpenId = self.get_managerialOpenId_by_openId(vRcd.openId) if vCard else ''
  334. self.notify_user(notifyOpenId, 'service_complete', **notifyDict)
  335. # 不需要退款,直接返回,不通知
  336. if self.device.is_auto_refund:
  337. refundProtectionTime = self.device.get("otherConf", dict()).get("refundProtectionTime", 5)
  338. if usedTime <= refundProtectionTime:
  339. backCoins = coins
  340. vCard.refund_quota(vRcd, 0.0, 0.0, backCoins.mongo_amount)
  341. else: # 扫码的
  342. user = MyUser.objects(openId=consumeRcd.openId, groupId=dev['groupId']).first()
  343. if not user:
  344. return
  345. # 通知服务结束
  346. notifyOpenId = user.managerialOpenId if user else ''
  347. self.notify_user(notifyOpenId, 'service_complete', **notifyDict)
  348. # 如果需要退款,计算退款数据.
  349. if self.device.is_auto_refund:
  350. refundProtectionTime = self.device.get("otherConf", dict()).get("refundProtectionTime", 5)
  351. if usedTime <= refundProtectionTime:
  352. backCoins = coins
  353. consumeDict.update({'refundedMoney': str(backCoins)})
  354. refund_money(dev, backCoins, consumeRcd.openId)
  355. self.notify_user(notifyOpenId, 'refund_coins', **refundDict)
  356. ServiceProgress.update_progress_and_consume_rcd(dev['ownerId'], {
  357. "open_id": notifyOpenId,
  358. "port": int(port),
  359. "device_imei": self.device.devNo,
  360. "isFinished": False
  361. },
  362. consumeDict)
  363. except Exception, e:
  364. logger.exception('some exception happed,devNo=%s,e=%s' % (devNo, e))
  365. finally:
  366. self.deviceAdapter.get_port_status_from_dev()
  367. # NFC刷卡充电启动上报
  368. def do_card_start(self, data):
  369. """
  370. {
  371. "type": "EVENT_NFC_CHARGE_STARTED", # 组网版事件名称
  372. "type": "EVENT_CARD_CHARGE_STARTED", # 独立版 & 六路机 & 十二路机事件名称(组网版中带金额上报事件也为该事件)
  373. "time": 1548141575, # 事件时间戳
  374. "gateway_id": "99503935742305", # 网关uid
  375. "data": {
  376. "node_index": 1, # 插座号
  377. "port_index": 1, # 插孔号
  378. "charge_status": 2, # 0表示未充电,1表示充电,2表示充满自停 ,3金额用完(时间到)
  379. "flag_over_load": 0, # 过载标志
  380. "flag_no_load": 0, # 空载标志
  381. "flag_over_current": 0, # 过流标志
  382. "transaction_id": "1", # 业务号
  383. "card": {
  384. "no": "112233445566", # 卡号
  385. "charge_money":888, # 12路机专有,表示用户选择刷卡扣费的金额,单位0.01元。
  386. "type": 1, # 卡类型,0表示在线卡,1表示离线卡目前对接,只支持在线卡对接, 离线卡不考虑
  387. "balance": 100, # [仅离线卡含此字段]余额
  388. "charge_type": 3, # [仅离线卡含此字段]结算类型,1表示按时,2表示按量,3表示分功率
  389. "charge_limit_per_transaction": 1, # [仅离线卡含此字段]一次消费额度,单位元
  390. "price_by_time": 1, # [仅离线卡含此字段]按时单价
  391. "price_by_power": 1, # [仅离线卡含此字段]按量单价
  392. "power_config": [{
  393. "power": 200, # 档位
  394. "price": 1 # 单价
  395. },
  396. {
  397. "power": 400, # 目前对接,只支持在线卡对接, 离线卡不考虑
  398. "price": 1
  399. },
  400. {
  401. "power": 600,
  402. "price": 1
  403. },
  404. {
  405. "power": 800,
  406. "price": 1
  407. }] # [仅离线卡含此字段]分功率配置
  408. }
  409. }
  410. }
  411. """
  412. cardInfo = data['data'].get('card') # 获取上报的卡信息
  413. if not cardInfo:
  414. logger.debug("bolaiTen_get null card infomation from {}".format(repr(self.device)))
  415. return
  416. cardNo = cardInfo.get('no').upper() # 获取上报卡号
  417. if not cardNo:
  418. logger.debug("bolaiTen get null card number from {}".format(repr(self.device)))
  419. return
  420. card = self.find_card_by_card_no(cardNo) # 查找此卡是否存在
  421. if not card:
  422. logger.debug("bolaiTen get null card from {}".format(repr(self.device)))
  423. return
  424. self.update_card_dealer_and_type(cardNo)
  425. port = self.event_data['data']['port_index'] # 获取上报需要启动的端口号
  426. chargeMoney = self.event_data['data']['card']['charge_money'] * 0.01 # 总刷卡金额
  427. # 获取卡充电相关配置
  428. otherConf = self.device.my_obj.otherConf
  429. billingType = otherConf.get('billingType', 'time') # elec :电量 time:时间 power:功率
  430. config_list = otherConf.get('config_list', []) # 参数设置中,将分档功率存入otherConf
  431. onceCard = otherConf.get('onceCard', 100) # 参数设置中,将刷一次卡所以需要消费的金额存入otherConf
  432. cardTime = otherConf.get('cardTime', 180) # 参数设置中,将刷一次卡获得的充电时间存入otherConf
  433. cardElec = otherConf.get('cardElec', 1) # 参数设置中,将刷一次卡获得的电量存入otherConf
  434. cardTimes = chargeMoney / onceCard # 刷卡次数
  435. if card.balance < RMB(onceCard * 0.01): # 如果卡内约小于1分钱,则不给启动
  436. return
  437. if card.frozen: # 如果卡被冻结,则不给启动
  438. return
  439. # 组合启动设备所需要的参数
  440. jsonPara = {
  441. 'gateway_id': data['gateway_id'],
  442. 'port_index': port,
  443. 'node_index': 0,
  444. # 'transaction_id': sequanceNo,
  445. 'switch_state': 1,
  446. 'timeout': 15,
  447. 'control__type': 2,
  448. }
  449. servicedInfo = {}
  450. if billingType == 'time':
  451. jsonPara.update({
  452. 'charge_type': 1,
  453. 'charge_time': cardTimes * cardTime,
  454. 'charge_energy': 0
  455. })
  456. servicedInfo.update(
  457. {'billingType': 'time', 'cardNo': cardNo,
  458. 'needTime': cardTime * cardTimes,'unit':'分钟'})
  459. finishedTime = int(time.time()) + cardTime * cardTimes * 60
  460. elif billingType == 'elec':
  461. jsonPara.update({
  462. 'charge_type': 2,
  463. 'charge_energy': cardTimes * cardElec * 1000,
  464. 'charge_time': 0
  465. })
  466. servicedInfo.update(
  467. {'billingType': 'elec', 'cardNo': cardNo,
  468. 'needElec': cardTimes * cardElec,'unit':'度'})
  469. finishedTime = int(time.time()) + 60 * 60 * 10
  470. else:
  471. jsonPara.update({
  472. 'charge_type': 3,
  473. 'charge_power': {'money': onceCard, 'config_list': config_list}
  474. })
  475. servicedInfo.update(
  476. {'billingType': 'power', 'cardNo': cardNo,
  477. 'configList': config_list,'unit':'瓦'})
  478. finishedTime = int(time.time()) + 60 * 60 * 10
  479. devInfo = self.deviceAdapter._start_device(jsonPara)
  480. if devInfo['data']['code'] != 0:
  481. logger.info('start device by card failed,devNo=%s', self.device.devNo)
  482. return
  483. if devInfo['rst'] != 0: # 成功
  484. logger.info('start device by card failed,devNo=%s', self.device.devNo)
  485. return
  486. port = port + 1
  487. newValue = {
  488. str(port): {
  489. 'status': Const.DEV_WORK_STATUS_WORKING,
  490. 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  491. 'controlType': 1
  492. }
  493. }
  494. sequanceNo = devInfo['data']['data']['transaction_id']
  495. newValue[str(port)].update({'finishedTime': finishedTime, 'sequanceNo': sequanceNo})
  496. Device.clear_port_control_cache(self.device['devNo'], port)
  497. Device.update_dev_control_cache(self.device['devNo'], newValue)
  498. attachParas = {'category': 'chargeIndex', 'chargeIndex': port}
  499. order, card_order = self.new_card_consume_record(card=card,
  500. money=RMB(onceCard),
  501. sid=sequanceNo,
  502. servicedInfo=servicedInfo,
  503. attachParas=attachParas)
  504. self.create_progress_for_card(self.device, order, port, card_order,attachParas)
  505. self.consume_money_for_card(card, RMB(onceCard))
  506. # NFC刷卡充电结束上报
  507. def do_card_finished(self, data):
  508. """
  509. {
  510. "type": "EVENT_NFC_CHARGE_FINISHED", # 组网版事件名称
  511. "type": "EVENT_CARD_CHARGE_FINISHED", # 独立版&六路机事件名称
  512. "time": 1548141575, # 事件时间戳
  513. "gateway_id": "99503935742305", # 网关uid
  514. "data": {
  515. "node_index": 1, # 插座号
  516. "port_index": 1, # 插孔号
  517. "charge_status": 2, # 0 表示未充电,1 表示充电,2 表示充满自停 ,3金额用完(时间到)
  518. "flag_over_load": 0, # 过载标志
  519. "flag_no_load": 0, # 空载标志
  520. "flag_over_current": 0, # 过流标志
  521. "transaction_id": "1", # 业务号
  522. "power": 1, # 当前功率
  523. "current": 1, # 当前电流
  524. "energy_consumed": 0, # 已耗电能,单位Wh
  525. "time_consumed": 0, # 已耗时,单位分
  526. "card_no": "112233445566", # 卡号
  527. "card_type": 0, # 卡类型,0表示在线卡,1表示离线卡
  528. "charge_type": 3, # 结算类型,1表示按时,2表示按量,3表示分功率
  529. "money_consumed": 100, # 该参数禁止使用
  530. "settle_power": 600, # 结算功率,单位W
  531. "power_time_stat": "2,0" # 结算功率档位时间统计,时间用半角逗号","隔开,单位分
  532. }
  533. }
  534. """
  535. reason = self.__translate_reason(data['data']['charge_status'])
  536. sequanceNo = data['data']['transaction_id']
  537. devNo = self.device.devNo
  538. order = ConsumeRecord.objects.filter(sequanceNo=sequanceNo).first()
  539. if not order: # 投币的不会生成订单
  540. return
  541. port = order.attachParas['chargeIndex']
  542. progress = ServiceProgress.objects.filter(device_imei=self.device.devNo,
  543. port=int(port), isFinished=False).first() # type: ServiceProgress
  544. if progress is None or progress.status == 'finished':
  545. return
  546. progress.status = 'finished'
  547. progress.isFinished = True
  548. progress.expireAt = datetime.datetime.now()
  549. progress.save()
  550. try:
  551. group = Group.get_group(self.device.groupId)
  552. servicedInfo = order.servicedInfo
  553. billingType = servicedInfo['billingType']
  554. usedTime = data['data']['time_consumed']
  555. usedElec = data['data']['energy_consumed'] * 0.001
  556. consumeDict = {
  557. 'reason': reason,
  558. 'chargeIndex': port,
  559. 'duration': usedTime,
  560. 'elec': usedElec
  561. }
  562. consumeDict.update(servicedInfo)
  563. notifyDict = {
  564. 'service': u'充电服务完成',
  565. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  566. 'remark': u'谢谢您的支持'
  567. }
  568. refundDict = {
  569. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  570. }
  571. coins = order.coin
  572. if billingType == 'time': # 按照时间计费
  573. needTime = servicedInfo['needTime']
  574. leftTime = needTime - usedTime if needTime > usedTime else 0
  575. consumeDict.update({
  576. 'needTime': needTime,
  577. 'leftTime': leftTime,
  578. })
  579. titleDictList = [
  580. {u'设备编号': self.device.logicalCode},
  581. {u'端口': str(port)},
  582. {u'地址': group['address']},
  583. {u'结束原因': reason},
  584. {u'订购时间': u'%s分钟' % needTime},
  585. {u'充电时间': u'%s分钟' % usedTime},
  586. {u'剩余时间': u'%s分钟' % leftTime}
  587. ]
  588. notifyDict.update(
  589. {
  590. 'title': make_title_from_dict(titleDictList)
  591. }
  592. )
  593. backCoins = coins * (float(leftTime) / float(needTime))
  594. titleDictList = [
  595. {u'设备编号': self.device.logicalCode},
  596. {u'端口': str(port)},
  597. {u'付款': u'%s元' % coins},
  598. {u'预定时间': u'%s分钟' % needTime},
  599. {u'充电时间': u'%s分钟' % usedTime},
  600. {u'剩余时间': u'%s分钟' % leftTime},
  601. ]
  602. refundDict.update(
  603. {
  604. 'title': make_title_from_dict(titleDictList),
  605. 'backCount': u'金币:%s' % backCoins
  606. }
  607. )
  608. elif billingType == 'elec': # 按电量付费的
  609. needElec = servicedInfo['needElec']
  610. leftElec = needElec - usedElec if needElec > usedElec else 0.0
  611. consumeDict.update({
  612. 'needElec': needElec,
  613. 'leftElec': leftElec
  614. })
  615. titleDictList = [
  616. {u'设备编号': self.device.logicalCode},
  617. {u'端口': str(port) },
  618. {u'结束原因': reason},
  619. {u'订购电量': u'%s度' % needElec},
  620. {u'消耗电量': u'%s度' % usedElec},
  621. {u'剩余电量': u'%s度' % leftElec},
  622. ]
  623. notifyDict.update(
  624. {
  625. 'title': make_title_from_dict(titleDictList)
  626. }
  627. )
  628. backCoins = coins * (float(leftElec) / float(needElec))
  629. titleDictList = [
  630. {u'设备编号': self.device.logicalCode},
  631. {u'端口': str(port)},
  632. {u'结束原因': reason},
  633. {u'订购电量': u'%s度' % needElec},
  634. {u'消耗电量': u'%s度' % usedElec},
  635. {u'剩余电量': u'%s度' % leftElec},
  636. ]
  637. refundDict.update(
  638. {
  639. 'title': make_title_from_dict(titleDictList),
  640. 'backCount': u'金币:%s' % backCoins
  641. }
  642. )
  643. else: # 功率付费
  644. spendMoney = self.calc_spend_money(data)
  645. if spendMoney >= coins:
  646. spendMoney = coins
  647. consumeDict.update(
  648. {
  649. 'settlePower': data['data']['settle_power'],
  650. 'power': data['data']['power'],
  651. 'current': data['data']['current'],
  652. 'spendMoney': str(spendMoney),
  653. }
  654. )
  655. consumeDict.update(order.servicedInfo)
  656. notifyDict = {
  657. 'service': u'充电服务完成',
  658. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  659. 'remark': u'谢谢您的支持'
  660. }
  661. refundDict = {
  662. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  663. }
  664. coins = order.coin
  665. backCoins = coins - spendMoney if coins > spendMoney else VirtualCoin(0.0)
  666. usedTime = data['data']['time_consumed']
  667. consumeDict.update({
  668. 'duration': usedTime,
  669. })
  670. titleDictList = [
  671. {u'设备编号': self.device.logicalCode},
  672. {u'端口': str(port)},
  673. {u'地址': group['address']},
  674. {u'结束原因': reason},
  675. {u'结算功率': u'%s瓦' % data['data']['settle_power']},
  676. {u'使用时间': u'%s分钟' % usedTime}
  677. ]
  678. notifyDict.update(
  679. {
  680. 'title': make_title_from_dict(titleDictList)
  681. }
  682. )
  683. refundDict.update(
  684. {
  685. 'title': make_title_from_dict(titleDictList),
  686. 'backCount': u'金币:%s' % backCoins
  687. }
  688. )
  689. dealer = self.device.owner
  690. card = Card.objects.filter(agentId=dealer.agentId, cardNo=str(servicedInfo['cardNo'])).first()
  691. if card is None: # 离线卡没有绑定或者在线卡被解绑了
  692. return
  693. # 先通知
  694. notifyOpenId = card.managerialOpenId if card else ''
  695. self.notify_user(notifyOpenId, 'service_complete', **notifyDict)
  696. # 不需要退款,直接返回.在线卡需要退费,离线卡只登记使用记录就OK
  697. if self.device.is_auto_refund and card.cardType == 'ID':
  698. refundProtectionTime = self.device.get("otherConf", dict()).get("refundProtectionTime", 5)
  699. if usedTime <= refundProtectionTime:
  700. backCoins = coins
  701. card = self.refund_money_for_card(RMB(backCoins), card.id)
  702. card.showBalance = card.balance
  703. card.save()
  704. consumeDict.update({'refundedMoney': str(backCoins)})
  705. self.notify_user(notifyOpenId, 'refund_coins', **refundDict)
  706. ServiceProgress.update_progress_and_consume_rcd(self.device.ownerId,{
  707. "open_id": notifyOpenId,
  708. "port": int(port),
  709. "device_imei": self.device.devNo,
  710. "isFinished": False
  711. },
  712. consumeDict)
  713. except Exception, e:
  714. logger.exception('some exception happed,devNo=%s,e=%s' % (devNo, e))
  715. finally:
  716. self.deviceAdapter.get_port_status_from_dev()
  717. # 插座心跳
  718. def update_port_info(self, data):
  719. devNo = data['gateway_id']
  720. port_list = data['data']['port_list']
  721. temperature = data['data']['temperature']
  722. portInfos = dict()
  723. for portInfo in port_list:
  724. power = portInfo['power']
  725. current = portInfo['current']
  726. port = portInfo['port_index'] + 1
  727. voltage = portInfo['voltage']
  728. usedElec = portInfo['energy_consumed'] * 0.001
  729. usedTime = portInfo['time_consumed']
  730. tempStatus = portInfo['charge_status']
  731. order = portInfo['transaction_id']
  732. if tempStatus == 0:
  733. status = Const.DEV_WORK_STATUS_IDLE
  734. elif tempStatus == 1:
  735. status = Const.DEV_WORK_STATUS_WORKING
  736. else:
  737. status = Const.DEV_WORK_STATUS_WORKING
  738. portInfos[str(port)] = {
  739. 'power': power, 'current': current, 'voltage': voltage,\
  740. 'usedElec':usedElec, 'usedTime':usedTime, 'order':order, 'status':status
  741. }
  742. # allPorts, usedPorts, usePorts = self.deviceAdapter.get_port_static_info(portInfos)
  743. # 端口信息更新
  744. devCache = Device.get_dev_control_cache(devNo) or dict()
  745. for portStr, value in devCache.items():
  746. if not portStr.isdigit() or not isinstance(value, dict):
  747. continue
  748. # 更新每个端口的信息
  749. tempPortInfo = portInfos.get(portStr, dict())
  750. value.update(tempPortInfo)
  751. devCache[portStr] = value
  752. devCache['temperature'] = temperature
  753. Device.update_dev_control_cache(devNo, devCache)
  754. def do(self):
  755. data = self.event_data
  756. if 'type' in self.event_data:
  757. massageTpye = self.event_data['type']
  758. if massageTpye == "EVENT_GATEWAY_HEARTBEAT": # 网关心跳
  759. self.event_gateway_heartbeat()
  760. elif massageTpye == "EVENT_PLUG_MP_HEARTBEAT": # 心跳上报3.9
  761. self.update_port_info(data)
  762. elif massageTpye == "EVENT_CHARGE_FINISHED": # 充电结束上报
  763. self.do_finished_by_quantity(data)
  764. elif massageTpye == "EVENT_CHARGE_FINISHED_3": # 按功率充电结束上报
  765. self.do_finished_by_power(data)
  766. elif massageTpye == "EVENT_CARD_CHARGE_STARTED": # NFC刷卡充电开始上报
  767. self.do_card_start(data)
  768. elif massageTpye == "EVENT_CARD_CHARGE_FINISHED": # NFC刷卡充电结束上报
  769. self.do_card_finished(data)
  770. # elif massageTpye == "EVENT_PLUG_MP_HEARTBEAT": # 插座心跳
  771. # self.update_port_info(data)
  772. def new_card_consume_record(self, card, money, sid, servicedInfo, attachParas):
  773. group = self.device.group # type: GroupDict
  774. address = group.address
  775. group_number = self.device.groupNumber
  776. now = datetime.datetime.now()
  777. orderNo = ConsumeRecord.make_no()
  778. new_record = {
  779. 'orderNo': orderNo,
  780. 'time': now.strftime("%Y-%m-%d %H:%M:%S"),
  781. 'dateTimeAdded': now,
  782. 'openId': card.openId,
  783. 'ownerId': self.device.ownerId,
  784. 'coin': money.mongo_amount,
  785. 'money': money.mongo_amount,
  786. 'devNo': self.device.devNo,
  787. 'logicalCode': self.device.logicalCode,
  788. 'groupId': self.device.groupId,
  789. 'address': address,
  790. 'groupNumber': group_number,
  791. 'groupName': group.groupName,
  792. 'devTypeCode': self.device.devTypeCode,
  793. 'devTypeName': self.device.devTypeName,
  794. 'isNormal': True,
  795. 'status': ConsumeRecord.Status.RUNNING,
  796. 'remarks': u'刷卡消费',
  797. 'errorDesc': '',
  798. 'sequanceNo': sid,
  799. 'desc': '',
  800. 'attachParas': attachParas,
  801. 'servicedInfo': servicedInfo
  802. }
  803. order = ConsumeRecord(**new_record)
  804. order.save()
  805. # 刷卡消费也记录一条数据
  806. new_card_record = {
  807. 'orderNo': orderNo,
  808. 'openId': card.openId,
  809. 'cardId': str(card.id),
  810. 'money': money.mongo_amount,
  811. 'balance': card.balance.mongo_amount,
  812. 'devNo': self.device.devNo,
  813. 'devType': self.device.devTypeName,
  814. 'logicalCode': self.device.logicalCode,
  815. 'groupId': self.device.groupId,
  816. 'address': address,
  817. 'groupNumber': group_number,
  818. 'groupName': group.groupName,
  819. 'result': 'success',
  820. 'remarks': u'刷卡消费',
  821. 'sid': sid,
  822. 'dateTimeAdded': datetime.datetime.now(),
  823. 'desc': '',
  824. 'servicedInfo': servicedInfo,
  825. 'linkedConsumeRcdOrderNo': str(order.orderNo)
  826. }
  827. card_order = CardConsumeRecord(**new_card_record)
  828. card_order.save()
  829. return order, card_order
  830. def create_progress_for_card(self, dev, consumeRcd, port, card_order,attachParas):
  831. try:
  832. progress = ServiceProgress.objects.filter(device_imei=self.device.devNo,
  833. port=int(port), isFinished=False).first()
  834. if progress:
  835. return
  836. consumeOrder = consumeRcd.servicedInfo
  837. new_service_progress = ServiceProgress(
  838. open_id=consumeRcd.openId,
  839. device_imei=consumeRcd.devNo,
  840. devTypeCode=dev['devType']['code'],
  841. port=port,
  842. attachParas=attachParas,
  843. start_time=int(time.time()),
  844. finished_time=int(time.time()) + 24 * 60 * 60,
  845. consumeOrder=consumeOrder,
  846. expireAt=datetime.datetime.now() + datetime.timedelta(days=91),
  847. cardId=card_order.cardId,
  848. )
  849. new_service_progress.save()
  850. except Exception as e:
  851. logger.exception(e)
  852. # 功率计费模式下,根据返回的数据,计算消耗的钱
  853. def calc_spend_money(self, data):
  854. settle_power = data['data']['settle_power']
  855. usedTime = data['data']['time_consumed']
  856. config_list = self.device.my_obj.otherConf.get('config_list', [])
  857. powerList = []
  858. configDict = {}
  859. for _ in config_list:
  860. configDict[_['power']] = _
  861. powerList.append(_['power'])
  862. powerList.sort()
  863. for power in powerList:
  864. if settle_power <= power:
  865. break
  866. else:
  867. continue
  868. pricePower = power
  869. priceConfig = configDict[pricePower]
  870. # spendMoney = VirtualCoin(float(usedTime) / priceConfig['time'] * 100 * 0.01)
  871. spendMoney = VirtualCoin(float(usedTime) * (float(priceConfig['price']) / float(priceConfig['time'])) * 0.01)
  872. return spendMoney