aoqiang_socket.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import logging
  5. from apilib.monetary import RMB, VirtualCoin
  6. from apilib.utils_datetime import to_datetime
  7. from apps.web.constant import Const, DeviceCmdCode, DEALER_CONSUMPTION_AGG_KIND
  8. from apps.web.core.exceptions import ServiceException
  9. from apps.web.core.networking import MessageSender
  10. from apps.web.device.models import Group, Device
  11. from apps.web.eventer import EventBuilder
  12. from apps.web.eventer.base import WorkEvent
  13. from apps.web.user.models import ServiceProgress, Card, CardRechargeOrder, MyUser
  14. logger = logging.getLogger(__name__)
  15. card_is_normal = 1
  16. card_not_in_db = 2
  17. card_is_forzen = 3
  18. card_has_not_order = 4 # IC卡适用
  19. card_less_balance = 5 # ID卡适用
  20. card_type_is_ic = 6 # IC卡不允许当做ID卡使用
  21. class builder(EventBuilder):
  22. def __getEvent__(self, device_event):
  23. event_data = self.deviceAdapter.analyze_event_data(device_event['data'])
  24. if event_data is None:
  25. return None
  26. if event_data['fun_code'] in [0x35, 0x2C]:
  27. return ChargingWorkEvent(self.deviceAdapter, event_data)
  28. class ChargingWorkEvent(WorkEvent):
  29. def update_balance_for_IC_card(self, card, balance):
  30. card.balance = balance
  31. try:
  32. card.save()
  33. except Exception, e:
  34. pass
  35. def __translate_reason(self, cause, chrmt):
  36. if cause == 0:
  37. if chrmt == 1:
  38. return u'订购时间已经用完。'
  39. elif chrmt == 2:
  40. return u'订购电量已经用完。'
  41. elif cause == 1:
  42. return u'用户手工停止了充电'
  43. elif cause == 2:
  44. return u'充电满了,自动停止'
  45. elif cause == 3:
  46. return u'超功率自停'
  47. elif cause == 4:
  48. return u'远程断电'
  49. elif cause == 5:
  50. return u'刷卡断电'
  51. elif cause == 0x0B:
  52. return u'设备或是端口出现问题,被迫停止'
  53. return u'充电结束。'
  54. def response_id_card(self, msgDict):
  55. data = msgDict
  56. online_card_cost = RMB(self.device['otherConf'].get('online_card_cost', 10) * 0.1) # 元
  57. online_card_val = self.device['otherConf'].get('online_card_val', 240)
  58. online_card_chrmt = self.device['otherConf'].get('online_card_chrmt', 1)
  59. mqtt_data = {
  60. 'fun_code': 0x35,
  61. 'port_id': data['port_id'],
  62. 'card_no': data['card_no'],
  63. 'chrmt': online_card_chrmt,
  64. 'result': card_not_in_db,
  65. 'balance': 0,
  66. 'amount': 0,
  67. }
  68. cardNo = '{}'.format(int(data['card_no'], 16))
  69. Card.record_dev_card_no(self.device['devNo'], cardNo)
  70. card = self.update_card_dealer_and_type(cardNo)
  71. # 如果没有卡,直接返回
  72. # cardNo,portId,balance,result
  73. if card is None:
  74. return self.send_mqtt(mqtt_data)
  75. if card.frozen:
  76. data.update(
  77. {'result': card_is_forzen}
  78. )
  79. return self.send_mqtt(mqtt_data)
  80. if card.cardType == 'IC': # 如果以前是离线卡,只能用于离线卡,需要提醒用户
  81. data.update(
  82. {'result': card_type_is_ic}
  83. )
  84. return self.send_mqtt(mqtt_data)
  85. #: 首先检查订单,并进行充值
  86. #: 用户首先在手机客户端充值,需要这个时刻刷卡上报事件将充值的订单同步上来
  87. card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id))
  88. if card_recharge_order:
  89. result = self.recharge_id_card(card=card,
  90. rechargeType='append',
  91. order=card_recharge_order)
  92. card.reload()
  93. if card.balance >= online_card_cost:
  94. lineInfo = Device.get_dev_control_cache(self.device.devNo).get(str(data['port_id']), {})
  95. if lineInfo.get('status') == Const.DEV_WORK_STATUS_WORKING:
  96. return
  97. consumeDict = {
  98. 'chargeIndex': data['port_id']
  99. }
  100. if online_card_chrmt == 1:
  101. consumeDict.update({'needTime': online_card_val})
  102. elif online_card_chrmt == 2:
  103. consumeDict.update({'needElec': online_card_val})
  104. online_card_val = online_card_val * 100
  105. mqtt_data = {
  106. 'fun_code': 0x35,
  107. 'port_id': data['port_id'],
  108. 'card_no': data['card_no'],
  109. 'chrmt': online_card_chrmt,
  110. 'balance': int(card.balance - online_card_cost) * 10,
  111. 'result': card_is_normal,
  112. 'amount': int(online_card_val),
  113. }
  114. result = self.send_mqtt(mqtt_data)
  115. if result['rst'] == 0:
  116. self.consume_money_for_card(card, online_card_cost)
  117. orderNo, cardOrderNo = self.record_consume_for_card(card, online_card_cost, servicedInfo=consumeDict)
  118. lineInfo = {
  119. "isStart": True,
  120. "status": Const.DEV_WORK_STATUS_WORKING,
  121. "openId": card.openId,
  122. "cardNo": card.cardNo,
  123. "coins": round(online_card_cost, 2),
  124. "startTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  125. "consumeType": "card",
  126. "port": data['port_id'],
  127. "orderNo": orderNo,
  128. "cardOrderNo": cardOrderNo
  129. }
  130. Device.update_port_control_cache(self.device.devNo, lineInfo)
  131. ServiceProgress.register_card_service(
  132. self.device,
  133. data['port_id'],
  134. card,
  135. consumeOrder={
  136. "orderNo": orderNo,
  137. "cardOrderNo": cardOrderNo,
  138. }
  139. )
  140. else:
  141. data.update(
  142. {
  143. 'balance': int(card.balance),
  144. 'result': card_less_balance,
  145. 'amount': int(online_card_cost),
  146. }
  147. )
  148. return self.send_mqtt(data)
  149. def do(self, **args):
  150. funCode = self.event_data['fun_code']
  151. if funCode == 0x35:
  152. self.response_id_card(self.event_data)
  153. if funCode == 0x2C:
  154. self._do_finish()
  155. def send_mqtt(self, data, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, timeout=10):
  156. """
  157. 发送mqtt 指令默认210 返回data
  158. """
  159. payload = {'IMEI': self.device.devNo, 'data': data}
  160. result = MessageSender.send(self.device, cmd,
  161. payload=payload, timeout=timeout)
  162. if not result.has_key('rst'):
  163. raise ServiceException({'result': 2, 'description': u'报文异常'})
  164. if result['rst'] == -1:
  165. raise ServiceException({'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  166. if result['rst'] == 1:
  167. raise ServiceException({'result': 2, 'description': u'串口通讯失败,您稍候再试,或者联系客服'})
  168. if result['rst'] == 2:
  169. raise ServiceException({'result': 2, 'description': u'串口通讯失败,您稍候再试,或者联系客服'})
  170. else:
  171. return result
  172. def _do_finish(self):
  173. start_type = self.event_data.get('start_type')
  174. try:
  175. if start_type == 0: # 离线卡
  176. self._offline_card_finish()
  177. elif start_type == 1: # 在线卡
  178. cardHex = self.event_data.get('card_no')
  179. cardNo = '{}'.format(int(cardHex, 16))
  180. Card.record_dev_card_no(self.device['devNo'], cardNo)
  181. card = self.update_card_dealer_and_type(cardNo)
  182. # 如果没有卡,直接返回
  183. # cardNo,portId,balance,result
  184. if card is None:
  185. return
  186. self._online_card_finish(card)
  187. elif start_type == 2: # 扫码
  188. self._net_pay_finish()
  189. except:
  190. import traceback
  191. logger.info(traceback.format_exc())
  192. Device.clear_port_control_cache(self.device.devNo, str(self.event_data['port_id']))
  193. def _offline_card_finish(self):
  194. print(self.event_data)
  195. pass
  196. def _online_card_finish(self,card):
  197. leftTime = self.event_data.get('left_time')
  198. leftElec = round(self.event_data.get('left_elec', 0) * 0.001, 3)
  199. reason = self.event_data.get('reason')
  200. port = self.event_data.get('port_id')
  201. lineInfo = Device.get_dev_control_cache(self.device.devNo).get(str(port), {})
  202. if 'coins' not in lineInfo:
  203. logger.debug('port cache has no coins. no order in port {}'.format(lineInfo.get('port')))
  204. return
  205. orderNo = lineInfo.get('orderNo')
  206. coins = VirtualCoin(lineInfo.get('coins'))
  207. openId = lineInfo.get('openId')
  208. chrmt = self.event_data.get('chrmt') or lineInfo.get('chmrt')
  209. startTime = to_datetime(lineInfo['startTime'])
  210. nowTime = datetime.datetime.now()
  211. if startTime > nowTime: # 如果web服务器时间和事件监控服务器时间不一致,导致开始时间比事件时间还大
  212. usedTime = 0
  213. else:
  214. usedTime = int(round(((nowTime - startTime).total_seconds() / 60.0)))
  215. extra = []
  216. consumeDict = {
  217. 'port': port,
  218. 'reason': self.__translate_reason(reason, chrmt),
  219. DEALER_CONSUMPTION_AGG_KIND.COIN: coins.mongo_amount,
  220. }
  221. refundProtectionTime = lineInfo.get('refundProtectionTime', 5)
  222. backCoins = VirtualCoin(0)
  223. usedFee = coins
  224. if usedTime < refundProtectionTime:
  225. backCoins = coins
  226. usedFee = VirtualCoin(0)
  227. else:
  228. if self.device.is_auto_refund:
  229. if chrmt == 1:
  230. needTime = lineInfo.get('needTime')
  231. if leftTime == 0xFFFF:
  232. actualNeedTime = 0
  233. backCoins = coins
  234. usedFee = VirtualCoin(0)
  235. else:
  236. actualNeedTime = usedTime + leftTime
  237. backCoins = self.get_backCoins(coins, leftTime, actualNeedTime)
  238. usedFee = coins - backCoins
  239. consumeDict.update({
  240. 'needTime': needTime,
  241. 'actualNeedTime': actualNeedTime,
  242. 'leftTime': leftTime,
  243. })
  244. elif chrmt == 2:
  245. needElec = round(lineInfo.get('needElec', 0) * 0.001, 3)
  246. if leftElec > needElec:
  247. leftElec = needElec
  248. elec = 0
  249. backCoins = coins
  250. usedFee = VirtualCoin(0)
  251. else:
  252. elec = round((needElec - leftElec) * 0.001, 3)
  253. backCoins = self.get_backCoins(coins, leftElec, needElec)
  254. usedFee = coins - backCoins
  255. consumeDict.update({
  256. 'needElec': needElec,
  257. 'elec': elec,
  258. 'leftElec': leftElec,
  259. })
  260. else:
  261. pass
  262. ServiceProgress.update_progress_and_consume_rcd(
  263. self.device['ownerId'],
  264. {
  265. 'open_id': openId, 'device_imei': self.device['devNo'],
  266. 'port': port, 'isFinished': False
  267. },
  268. consumeDict)
  269. user = MyUser.objects(openId=openId, groupId=self.device['groupId']).first()
  270. group = Group.get_group(self.device['groupId'])
  271. # 处理退费 与推送
  272. if backCoins > VirtualCoin(0):
  273. extra.append({u'消费明细': u'消费{}金币,退费{}金币'.format(usedFee, backCoins)})
  274. self.refund_money_for_card(backCoins, str(card.id), orderNo)
  275. else:
  276. extra.append({u'消费明细': u'消费{}(金币)'.format(usedFee)})
  277. self.notify_user_service_complete(
  278. service_name='充电',
  279. openid=card.managerialOpenId,
  280. port=str(port),
  281. address=group.address,
  282. reason=self.event_data.get('reasonDesc'),
  283. finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  284. extra=extra)
  285. def _net_pay_finish(self):
  286. leftTime = self.event_data.get('left_time')
  287. leftElec = round(self.event_data.get('left_elec', 0) * 0.001, 3)
  288. reason = self.event_data.get('reason')
  289. port = self.event_data.get('port_id')
  290. lineInfo = Device.get_dev_control_cache(self.device.devNo).get(str(port), {})
  291. if 'coins' not in lineInfo:
  292. logger.debug('port cache has no coins. no order in port {}'.format(lineInfo.get('port')))
  293. Device.clear_port_control_cache(self.device.devNo, (port))
  294. return
  295. coins = VirtualCoin(lineInfo.get('coins'))
  296. openId = lineInfo.get('openId')
  297. chrmt = self.event_data.get('chrmt') or lineInfo.get('chmrt')
  298. startTime = to_datetime(lineInfo['startTime'])
  299. nowTime = datetime.datetime.now()
  300. if startTime > nowTime: # 如果web服务器时间和事件监控服务器时间不一致,导致开始时间比事件时间还大
  301. usedTime = 0
  302. else:
  303. usedTime = int(round(((nowTime - startTime).total_seconds() / 60.0)))
  304. extra = []
  305. consumeDict = {
  306. 'port': port,
  307. 'reason': self.__translate_reason(reason, chrmt),
  308. DEALER_CONSUMPTION_AGG_KIND.COIN: coins.mongo_amount,
  309. }
  310. refundProtectionTime = self.device['otherConf'].get('refundProtectionTime', 5)
  311. backCoins = VirtualCoin(0)
  312. usedFee = coins
  313. if usedTime < refundProtectionTime:
  314. backCoins = coins
  315. usedFee = VirtualCoin(0)
  316. else:
  317. if self.device.is_auto_refund:
  318. if chrmt == 1:
  319. needTime = lineInfo.get('needTime')
  320. if leftTime == 0xFFFF:
  321. actualNeedTime = 0
  322. backCoins = coins
  323. usedFee = VirtualCoin(0)
  324. else:
  325. actualNeedTime = usedTime + leftTime
  326. backCoins = self.get_backCoins(coins, leftTime, actualNeedTime)
  327. usedFee = coins - backCoins
  328. consumeDict.update({
  329. 'needTime': needTime,
  330. 'actualNeedTime': actualNeedTime,
  331. 'leftTime': leftTime,
  332. })
  333. elif chrmt == 2:
  334. needElec = round(lineInfo.get('needElec', 0) * 0.001, 3)
  335. if leftElec > needElec:
  336. leftElec = needElec
  337. elec = 0
  338. backCoins = coins
  339. usedFee = VirtualCoin(0)
  340. else:
  341. elec = round((needElec - leftElec) * 0.001, 3)
  342. backCoins = self.get_backCoins(coins, leftElec, needElec)
  343. usedFee = coins - backCoins
  344. consumeDict.update({
  345. 'needElec': needElec,
  346. 'elec': elec,
  347. 'leftElec': leftElec,
  348. })
  349. else:
  350. pass
  351. ServiceProgress.update_progress_and_consume_rcd(
  352. self.device['ownerId'],
  353. {
  354. 'open_id': openId, 'device_imei': self.device['devNo'],
  355. 'port': port, 'isFinished': False
  356. },
  357. consumeDict)
  358. user = MyUser.objects(openId=openId, groupId=self.device['groupId']).first()
  359. group = Group.get_group(self.device['groupId'])
  360. # 处理退费 与推送
  361. if backCoins > VirtualCoin(0):
  362. extra.append({u'消费明细': u'消费{}金币,退费{}金币'.format(usedFee, backCoins)})
  363. self.refund_net_pay(user, lineInfo, RMB(0), backCoins, consumeDict, False)
  364. else:
  365. extra.append({u'消费明细': u'消费{}(金币)'.format(usedFee)})
  366. self.notify_user_service_complete(
  367. service_name='充电',
  368. openid=user.managerialOpenId,
  369. port=str(port),
  370. address=group.address,
  371. reason=self.event_data.get('reasonDesc'),
  372. finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  373. extra=extra)