weifule2_policy.py 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import copy
  4. import datetime
  5. from apilib.utils_AES import EncryptDate
  6. from apilib.utils_datetime import to_datetime
  7. from apps.web.constant import DeviceCmdCode, Const, FAULT_CODE, ErrorCode, CONSUMETYPE
  8. from apps.web.core.adapter.weifuleCommon import PolicyBase
  9. from apps.web.core.exceptions import ServiceException
  10. from apps.web.core.networking import MessageSender
  11. from apps.web.device.models import Device, DeviceType
  12. from apps.web.user.models import ConsumeRecord, Card, ServiceProgress
  13. cardKey = 'FR4e1OFCnDdrYA7u'
  14. class PolicyWEIFULEBox(PolicyBase):
  15. def __init__(self, device):
  16. super(PolicyWEIFULEBox, self).__init__(device)
  17. def translate_funcode(self, fun_code):
  18. fun_codeDict = {
  19. '01': u'查询所有端口状态',
  20. '02': u'查询端口详细信息',
  21. '03': u'上报投币充电事件',
  22. '04': u'上报刷卡事件',
  23. '05': u'上报充电结束事件',
  24. '06': u'远程停止充电',
  25. '07': u'远程启动充电',
  26. '08': u'查询投币总额',
  27. '09': u'清除投币总数',
  28. '0A': u'查询订单信息',
  29. }
  30. return fun_codeDict.get(fun_code, '')
  31. def translate_event_cmdcode(self, cmdCode):
  32. cmdDict = {
  33. }
  34. return cmdDict.get(cmdCode, '')
  35. def test(self, coins):
  36. data = {'fun_code': 0x07, 'order_id': '1111', 'coins': coins, 'port': 1, 'time': 60}
  37. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  38. {'IMEI': self._device['devNo'], 'data': data})
  39. return devInfo
  40. def check_feedback_result(self, devInfo):
  41. if not devInfo.has_key('rst'):
  42. raise ServiceException({'result': 2, 'description': u'报文异常'})
  43. if devInfo['rst'] == -1:
  44. raise ServiceException({'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  45. if devInfo['rst'] == 1:
  46. raise ServiceException({'result': 2, 'description': u'串口通讯失败,您稍候再试,或者联系客服'})
  47. if devInfo['rst'] == 2:
  48. raise ServiceException({'result': 2, 'description': u'端口被禁用'})
  49. if devInfo['rst'] == 3:
  50. raise ServiceException({'result': 2, 'description': u'端口计量器故障'})
  51. if devInfo['rst'] == 4:
  52. raise ServiceException({'result': 2, 'description': u'设备订单已达上限'})
  53. if devInfo['rst'] == 5:
  54. raise ServiceException({'result': 2, 'description': u'设备正在自检'})
  55. if devInfo['rst'] == 6:
  56. raise ServiceException({'result': 2, 'description': u'续充单计费模式不一致'})
  57. if devInfo['rst'] == 7:
  58. raise ServiceException({'result': 2, 'description': u'端口故障'})
  59. if devInfo['rst'] == 8:
  60. raise ServiceException({'result': 2, 'description': u'未连接充电器'})
  61. if devInfo['rst'] == 9:
  62. raise ServiceException({'result': 2, 'description': u'订单已存在'})
  63. def analyze_event_data(self, data):
  64. if data['fun_code'] == '34': # 如果是结束事件,需要把reason翻译出来
  65. descDict = {
  66. '1': u'开始充电,但是没有接充电器',
  67. '2': u'充电过程中,插座脱落',
  68. '3': u'用户按下关闭按钮关闭',
  69. '4': u'用户刷卡结束充电',
  70. '5': u'远程结束充电',
  71. '6': u'充电端口故障,为了安全主动关闭',
  72. '7': u'订购的时间使用完毕',
  73. '8': u'订购的电量使用完毕',
  74. '9': u'本端口功率过载,主动关闭',
  75. '10': u'整机功率过载,主动关闭',
  76. '11': u'其他异常导致的关闭'
  77. }
  78. order = data['order']
  79. order['reason'] = descDict.get(str(order['closeType']), u'')
  80. data['order'] = order
  81. return data
  82. def get_dev_consume_count(self):
  83. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  84. {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x08}})
  85. self.check_feedback_result(devInfo)
  86. data = devInfo['data']
  87. return {'cardFee': round(data['total_card'] / 100.0,2), 'coinFee': data['total_coin'] / 100.0} # 单位为分
  88. def get_many_port_info(self, portList):
  89. devInfo = MessageSender.send(self.device, self.make_random_cmdcode(),
  90. {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x12}})
  91. self.check_feedback_result(devInfo)
  92. pay_unit = self.show_pay_unit
  93. resultDict = {}
  94. for port, data in devInfo['data']['details'].items():
  95. if port not in portList:
  96. continue
  97. result = {'index': port}
  98. result['status'] = self.__translate_status_from_str(data['status'])
  99. result['power'] = round(data.get('watt', 0), 2)
  100. result['ampere'] = round(data.get('ampr', 0) / 1000.0, 2)
  101. result['voltage'] = round(data.get('volt'), 2)
  102. _wait = []
  103. for exec_order in data['orders']:
  104. orderNo = exec_order.get('id')
  105. order = ConsumeRecord.objects.filter(orderNo=orderNo).first()
  106. if exec_order.get('status') == 'running':
  107. result['usedTime'] = round(exec_order.get('time', 0) / 60.0, 1)
  108. result['usedElec'] = round(exec_order.get('elec') / 1000000.0, 2)
  109. result['startTime'] = datetime.datetime.fromtimestamp(exec_order['execute_time']).strftime('%m-%d %H:%M:%S')
  110. result['nickName'] = order and order.nickname
  111. if 'card_no' in exec_order:
  112. result['cardNo'] = str(int(exec_order.get('card_no', 0), 16))
  113. policy = exec_order.get('policy', {})
  114. policyType = policy.get('type')
  115. billingMethod = policy.get('billingMethod')
  116. result['openId'] = policy.get('open_id')
  117. result['consumeMoney'] = '{}元'.format(round(exec_order.get('money') * 0.01, 2))
  118. if billingMethod == 'prepaid':
  119. result['coins'] = round(policy.get('money') * 0.01, 2)
  120. if exec_order.get('order_type') == 'apps_start':
  121. result['consumeType'] = 'mobile'
  122. elif exec_order.get('order_type') == 'card_start':
  123. result['consumeType'] = 'card'
  124. result['cardBalance'] = '{}元'.format(round(exec_order.get('balance') * 0.01, 2))
  125. elif billingMethod == 'postpaid':
  126. result['consumeType'] = 'postpaid'
  127. if exec_order.get('order_type') == 'apps_start':
  128. needFiled = 'need{}'.format(policyType.capitalize())
  129. result[needFiled] = order and order.servicedInfo.get(needFiled)
  130. elif exec_order.get('order_type') == 'card_start':
  131. result['cardBalance'] = '{}元'.format(round(exec_order.get('balance') * 0.01, 2))
  132. else:
  133. pass
  134. elif exec_order.get('status') == 'waiting':
  135. _one = {}
  136. # if policyType == 'time':
  137. # if exec_order.get('order_type') == 'apps_start':
  138. # pass
  139. #
  140. # elif exec_order.get('order_type') == 'card_start':
  141. # pass
  142. _wait.append(_one)
  143. else:
  144. pass
  145. if _wait:
  146. result['waittingOrder'] = _wait
  147. resultDict[port] = result
  148. return resultDict
  149. def __translate_status_from_str(self, status):
  150. dictConf = {
  151. 'idle': Const.DEV_WORK_STATUS_IDLE,
  152. 'busy': Const.DEV_WORK_STATUS_WORKING,
  153. 'forbid': Const.DEV_WORK_STATUS_FORBIDDEN,
  154. 'fault': Const.DEV_WORK_STATUS_FAULT,
  155. 'running': Const.DEV_WORK_STATUS_WORKING,
  156. 'link': Const.DEV_WORK_STATUS_CONNECTED
  157. }
  158. return dictConf.get(status, Const.DEV_WORK_STATUS_IDLE)
  159. # 访问设备,获取设备端口信息
  160. def get_port_status_from_dev(self):
  161. devInfo = MessageSender.send(self.device, self.make_random_cmdcode(),
  162. {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x01}})
  163. self.check_feedback_result(devInfo)
  164. data = devInfo['data']
  165. result = {}
  166. portNum = data.get('total', 10)
  167. ii = 0
  168. while ii < portNum:
  169. ii += 1
  170. result[str(ii)] = {'status': self.__translate_status_from_str(data['status'].get(str(ii)))}
  171. allPorts, usedPorts, usePorts = self.get_port_static_info(result)
  172. Device.update_dev_control_cache(self._device['devNo'],
  173. {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  174. # 这里存在多线程更新缓存的场景,可能性比较低,但是必须先取出来,然后逐个更新状态,然后再记录缓存
  175. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  176. for strPort, info in result.items():
  177. if ctrInfo.has_key(strPort):
  178. ctrInfo[strPort].update({'status': info['status']})
  179. else:
  180. ctrInfo[strPort] = info
  181. Device.update_dev_control_cache(self._device['devNo'], ctrInfo)
  182. return result
  183. def get_port_status(self, force=False):
  184. if force:
  185. return self.get_port_status_from_dev()
  186. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  187. if not ctrInfo.has_key('allPorts'):
  188. self.get_port_status_from_dev()
  189. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  190. allPorts = ctrInfo.get('allPorts', 10)
  191. statusDict = {}
  192. for ii in range(allPorts):
  193. tempDict = ctrInfo.get(str(ii + 1), {})
  194. if tempDict.has_key('status'):
  195. statusDict[str(ii + 1)] = {'status': tempDict.get('status')}
  196. elif tempDict.has_key('isStart'):
  197. if tempDict['isStart']:
  198. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_WORKING}
  199. else:
  200. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  201. else:
  202. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  203. allPorts, usedPorts, usePorts = self.get_port_static_info(statusDict)
  204. Device.update_dev_control_cache(self._device['devNo'],
  205. {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  206. return statusDict
  207. def lock_unlock_port(self, port, lock=True):
  208. portInfo = self.get_port_info(port)
  209. if portInfo and portInfo['status'] == Const.DEV_WORK_STATUS_WORKING and lock:
  210. raise ServiceException({'result': 2, 'description': u'端口正忙,请先关闭端口后,再禁止端口'})
  211. typeStr = 'deactive' if lock else 'active'
  212. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  213. {'IMEI': self._device['devNo'],
  214. 'data': {'port': int(port), 'type': typeStr, 'fun_code': 0x0D}})
  215. self.check_feedback_result(devInfo)
  216. if devInfo['rst'] == 0:
  217. if lock:
  218. Device.update_dev_control_cache(self._device['devNo'],
  219. {str(port): {'status': Const.DEV_WORK_STATUS_FORBIDDEN}})
  220. else:
  221. Device.update_dev_control_cache(self._device['devNo'],
  222. {str(port): {'status': Const.DEV_WORK_STATUS_IDLE}})
  223. else:
  224. Device.update_dev_control_cache(self._device['devNo'], {str(port): {'status': Const.DEV_WORK_STATUS_IDLE}})
  225. def active_deactive_port(self, port, active):
  226. if active:
  227. raise ServiceException({'result': 2, 'description': u'该设备不支持直接打开端口'})
  228. return self.stop_charging_port(port)
  229. # 停止该端口下的所有任务
  230. def stop_charging_port(self, port):
  231. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  232. {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x06, 'port': port}})
  233. self.check_feedback_result(devInfo)
  234. if devInfo['rst'] == 0:
  235. Device.update_dev_control_cache(self._device['devNo'], {str(port): {'status': Const.DEV_WORK_STATUS_IDLE}})
  236. return True if devInfo['rst'] == 0 else False
  237. def get_order(self, order_no):
  238. order = ConsumeRecord.objects.filter(orderNo=order_no).first()
  239. port = order.used_port
  240. devInfo = MessageSender.send(self.device, self.make_random_cmdcode(),
  241. {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x02, 'port': int(port)}})
  242. self.check_feedback_result(devInfo)
  243. data = devInfo.get('data', {})
  244. exec_orders = data.get('orders', [])
  245. if not exec_orders:
  246. return {}
  247. result = {}
  248. for exec_order in exec_orders:
  249. if not exec_order.get('order_type'): # 有可能没有订单,应该返回空
  250. return result
  251. if exec_order.get('status') != 'running':
  252. return {}
  253. result['power'] = round(data.get('watt', 0), 2)
  254. pay_unit = self.show_pay_unit
  255. # result['ampere'] = round(data.get('ampr', 0) / 1000.0, 2)
  256. # result['voltage'] = round(data.get('volt'), 2)
  257. result['status'] = exec_order.get('status', 0)
  258. orderNo = exec_order.get('id')
  259. order = ConsumeRecord.objects.filter(orderNo=orderNo).first()
  260. if order:
  261. result['coins'] = round(order.coin, 2)
  262. else:
  263. result['coins'] = round(exec_order.get('coins', 0) * 0.01, 2)
  264. result['usedTime'] = round(exec_order.get('time', 0) / 60.0, 1)
  265. result['usedElec'] = round(exec_order.get('elec') / 1000000.0, 2)
  266. if exec_order.get('status') == 'running':
  267. result['startTime'] = datetime.datetime.fromtimestamp(int(exec_order.get('execute_time', 0))).strftime(
  268. '%m-%d %H:%M:%S')
  269. if exec_order.get('chrmt') == 'TIME':
  270. result['needTime'] = round(exec_order.get('amount_time', 0) / 60.0, 1)
  271. result['leftTime'] = round(exec_order.get('left_time', 0) / 60.0, 1)
  272. leftMoney = round((result['coins'] * exec_order.get('left_time', 0) / exec_order.get('amount_time', 0)),
  273. 2)
  274. consumeMoney = round((result['coins'] - leftMoney), 2)
  275. if exec_order.get('order_type') == 'apps_start':
  276. result['consumeType'] = 'mobile'
  277. result['consumeMoney'] = '{}{}'.format(consumeMoney, pay_unit)
  278. result['leftMoney'] = '{}{}'.format(leftMoney, pay_unit)
  279. try:
  280. orderNo = exec_order.get('id', 0)
  281. order = ConsumeRecord.objects.get(orderNo=orderNo)
  282. result['nickName'] = order.user.nickname
  283. package = order.package
  284. if package.get('name') == '充满自停' and package.get('coins') == 0 and package.get(
  285. 'price') == 0: # 后付费
  286. coins = round(exec_order.get('coins', 0) * 0.01, 2)
  287. consumeMoney = round(
  288. (coins * exec_order.get('time', 0) / exec_order.get(
  289. 'amount_time', 0)), 2)
  290. result['consumeMoney'] = '{}{}'.format(consumeMoney, pay_unit)
  291. result['consumeType'] = 'postpaid'
  292. result['needTime'] = '充满自停'
  293. result.pop('needElec', None)
  294. result.pop('leftMoney', None)
  295. result.pop('leftTime', None)
  296. result.pop('leftElec', None)
  297. except:
  298. pass
  299. if exec_order.get('order_type') == 'card_start':
  300. result['consumeType'] = 'card'
  301. result['cardNo'] = str(int(exec_order.get('card_no', 0), 16))
  302. result['cardConsumeMoney'] = '{}{}'.format(consumeMoney, pay_unit)
  303. result['cardLeftMoney'] = '{}{}'.format(leftMoney, pay_unit)
  304. result['cardBalance'] = '{}{}'.format(round(exec_order.get('balance', 0) * 0.01, 2), pay_unit)
  305. try:
  306. card = Card.objects.get(cardNo=str(int(exec_order.get('card_no', 0), 16)))
  307. result['cardName'] = card.cardName or card.nickName
  308. if card.cardType == 'ID': # id卡 订单余额显示有问题
  309. result['cardBalance'] = '{}{}'.format(round(exec_order.get('balance', 0) * 0.001, 2),
  310. pay_unit)
  311. except:
  312. pass
  313. elif exec_order.get('chrmt') == 'ELEC':
  314. result['needElec'] = round(exec_order.get('amount_elec', 0) * 0.000001, 2)
  315. result['leftElec'] = round(exec_order.get('left_elec', 0) * 0.000001, 4)
  316. leftMoney = round((result['coins'] * exec_order.get('left_elec', 0) / exec_order.get('amount_elec', 0)),
  317. 2)
  318. consumeMoney = round((result['coins'] - leftMoney), 2)
  319. if exec_order.get('order_type') == 'apps_start':
  320. result['consumeType'] = 'mobile'
  321. result['consumeMoney'] = '{}{}'.format(consumeMoney, pay_unit)
  322. result['leftMoney'] = '{}{}'.format(leftMoney, pay_unit)
  323. try:
  324. orderNo = exec_order.get('id', 0)
  325. order = ConsumeRecord.objects.get(orderNo=orderNo)
  326. result['nickName'] = order.user.nickname
  327. package = order.package
  328. if package.get('name') == '充满自停' and package.get('coins') == 0 and package.get(
  329. 'price') == 0: # 后付费
  330. result['consumeType'] = 'postpaid'
  331. coins = round(exec_order.get('coins', 0) * 0.01, 2)
  332. consumeMoney = round(
  333. (coins * exec_order.get('elec', 0) / exec_order.get(
  334. 'amount_elec', 0)), 2)
  335. result['consumeMoney'] = '{}{}'.format(consumeMoney, pay_unit)
  336. result['needTime'] = '充满自停'
  337. result.pop('needElec', None)
  338. result.pop('leftMoney', None)
  339. result.pop('leftTime', None)
  340. result.pop('leftElec', None)
  341. if package.get('billingMethod') == CONSUMETYPE.BILL_AS_SERVICE:
  342. result.pop('consumeMoney', None)
  343. result['elecFee'] = self.device.bill_as_service_feature.current_elec_fee(
  344. consumeMoney).mongo_amount
  345. result['serviceFee'] = self.device.bill_as_service_feature.current_service_fee(
  346. consumeMoney).mongo_amount
  347. except:
  348. pass
  349. if exec_order.get('order_type') == 'card_start':
  350. result['consumeType'] = 'card'
  351. result['cardNo'] = str(int(exec_order.get('card_no', 0), 16))
  352. result['cardConsumeMoney'] = '{}{}'.format(consumeMoney, pay_unit)
  353. result['cardLeftMoney'] = '{}{}'.format(leftMoney, pay_unit)
  354. result['cardBalance'] = '{}{}'.format(round(exec_order.get('balance', 0) * 0.01, 2), pay_unit)
  355. try:
  356. card = Card.objects.get(cardNo=str(int(exec_order.get('card_no', 0), 16)))
  357. result['cardName'] = card.cardName or card.nickName
  358. if card.cardType == 'ID': # id卡 订单余额显示有问题
  359. result['cardBalance'] = '{}{}'.format(round(exec_order.get('balance', 0) * 0.001, 2),
  360. pay_unit)
  361. except:
  362. pass
  363. else:
  364. pass
  365. return result
  366. def response_card_balance(self, cardNo, balance, result):
  367. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  368. {'IMEI': self._device['devNo'],
  369. 'data': {'fun_code': 35, 'card_no': cardNo, 'balance': int(100 * float(balance)),
  370. 'result': result}})
  371. self.check_feedback_result(devInfo)
  372. # 获取设备配置参数
  373. def get_dev_setting(self):
  374. """
  375. 从主板上读取数据显示在前台
  376. :return:
  377. """
  378. # 从主板读取一部分
  379. devInfo = MessageSender.send(
  380. self.device,
  381. DeviceCmdCode.OPERATE_DEV_SYNC,
  382. {
  383. 'IMEI': self._device['devNo'],
  384. 'data': {'fun_code': 12}
  385. }
  386. )
  387. self.check_feedback_result(devInfo)
  388. result = devInfo['data']
  389. # 浮充相关参数的获取解析
  390. result.update({
  391. 'fcharge_watt': result['fcharge']['watt'],
  392. 'fcharge_time': result['fcharge']['time'],
  393. 'fcharge_check': result['fcharge'].get('check', 10),
  394. })
  395. # 空载部分适配
  396. noload_check = result.get('noload_check') or {'time': 99, 'watt': 99}
  397. result['emptyPower'] = noload_check['watt']
  398. result['emptyTime'] = noload_check['time']
  399. # 音量部分适配
  400. defaultVolume = 0
  401. volumeList = []
  402. for k, v in result['volume'].items():
  403. if k == 'default':
  404. defaultVolume = v
  405. else:
  406. tempList = k.split('-')
  407. volumeList.append({'start': tempList[0], 'end': tempList[1], 'volume': v})
  408. result.pop('volume')
  409. result['volume'] = defaultVolume
  410. result['volume_list'] = volumeList
  411. state = self.get_dev_consume_count()
  412. result.update(state)
  413. # 离线卡刷一次金额, 投币器一次脉冲金额
  414. policy = result['policy']
  415. policy['money'] = round(float(policy.get('money', 100)) * 0.01, 2)
  416. policy['once_coin'] = round(float(policy.get('once_coin', 100)) * 0.01, 2)
  417. rule = policy.pop('rule', {})
  418. # 功率计费部分
  419. prices = rule.get('prices', [])
  420. for item in prices:
  421. item['price'] = round(float(item['price']) * 0.01, 2)
  422. item['power'] = int(item['power'])
  423. prices = sorted(prices, key=lambda _: _['power'])
  424. # 电量计费部分
  425. price = round(float(rule.get('price', 100)) * 0.01, 2)
  426. rule.update({
  427. # 时间部分
  428. 'prices': prices,
  429. # 电量部分
  430. 'price': price
  431. })
  432. policy['rule'] = rule
  433. result.update({'policy': policy})
  434. result['refundProtectionTime'] = self.device['otherConf'].get('refundProtectionTime', 5)
  435. return result
  436. # 获取设备配置参数
  437. def set_dev_setting(self, setConf):
  438. """
  439. 功率计费 : x 功率设备 充电t小时 需要 y元 总价列式 y = t x
  440. 时间计费: x 功率设备 y元 能够充电 t 小时 总价列式 y = t x
  441. 其实参数一样 仅仅表现形式不一样 (前提: 以最大的功率进行计算金额)
  442. 服务器保存两套 x y t 的模板
  443. 当用户选择时间计费的时候 下发时间计费的一套
  444. 选择功率计费的时候 下发功率计费的一套
  445. :param setConf:
  446. :return:
  447. """
  448. # 如果是卡密码,直接进行修改
  449. if "card_pwd" in setConf:
  450. return self.set_card_pwd(setConf['card_pwd'])
  451. if 'policy' in setConf:
  452. policy = setConf['policy']
  453. _type = policy['type']
  454. money = int(float(policy['money']) * 100) # 离线卡刷一次金额 单位: 分
  455. once_coin = int(float(policy.get('once_coin') or 1.0) * 100) # 离线卡刷一次金额 单位: 分
  456. auto_stop = policy['auto_stop']
  457. rule = policy['rule']
  458. # 时间套餐格式化
  459. prices = rule.get('prices', [])
  460. # 单位准换
  461. for item in prices:
  462. item['price'] = int(float(item['price']) * 100)
  463. item['power'] = int(item['power'])
  464. # 排序
  465. prices = sorted(prices, key=lambda _: _['power'])
  466. # 电量部分格式化(电量的价格)
  467. price = int(float(policy.get('price', 1) * 100.0))
  468. setConf['policy'] = {
  469. 'type': _type,
  470. 'auto_stop': True if _type == 'elec' else auto_stop,
  471. 'over_money_stop': True,
  472. 'money': money,
  473. 'once_coin': once_coin,
  474. 'rule': {
  475. 'time': 'full_stop',
  476. 'prices': prices,
  477. 'elec': 'full_stop',
  478. 'price': price
  479. }
  480. }
  481. # 先不放出来: 按次的配置
  482. # if _type == 'count':
  483. # elec = rule.get('elec', 1) * 1000000 # 单位 3.6j
  484. # time = rule.get('time', 240) * 60 # 单位: 秒
  485. #
  486. # setconfig1['policy'] = {
  487. # 'type': 'elec',
  488. # 'auto_stop': auto_stop,
  489. # 'money': 100,
  490. # 'over_money_stop': False,
  491. # 'rule': {
  492. # 'elec': elec,
  493. # 'time': time,
  494. # 'price': 100
  495. # }
  496. # }
  497. # 浮充时间、浮充检测时间、浮充功率以及音量的范围检测
  498. if not (1 <= int(setConf['fcharge_time']) <= 300):
  499. raise ServiceException({'result': 2, 'description': u'浮充时间必须大于等于1分钟,小于等于300分钟'})
  500. if not (2 <= int(setConf['fcharge_check']) <= 30):
  501. raise ServiceException({'result': 2, 'description': u'浮充检测时间必须大于等于2分钟,小于等于30分钟'})
  502. if not (20 <= int(setConf['fcharge_watt']) <= 200):
  503. raise ServiceException({'result': 2, 'description': u'浮充功率必须大于等于20瓦,小于等于200瓦'})
  504. if not (0 <= int(setConf['volume']) <= 7):
  505. raise ServiceException({'result': 2, 'description': u'音量必须大于等于0,小于等于7'})
  506. if not (50 <= int(setConf['max_watt']) <= 10000):
  507. raise ServiceException({'result': 2, 'description': u'最大功率必须大于等于50瓦,小于等于10000瓦'})
  508. # 音量区间的处理
  509. volumeDict = {'default': int(setConf['volume'])}
  510. for vl in setConf['volume_list']:
  511. startTime = to_datetime('2020-01-01 %s:00' % vl['start'])
  512. endTime = to_datetime('2020-01-01 %s:00' % vl['end'])
  513. if endTime <= startTime:
  514. raise ServiceException({'result': 2, 'description': u'结束时间一定要大于起始时间。比如起始时间为07:00,结束时间为23:59'})
  515. volumeDict['%s-%s' % (vl['start'], vl['end'])] = int(vl['volume'])
  516. # 更新音量设置 并且去除不需要的音量中间量
  517. setConf['volume'] = volumeDict
  518. setConf.pop('volume_list')
  519. # 更新参数的类型以及参数的单位比例
  520. setConf['max_watt'] = int(setConf['max_watt'])
  521. # 浮充相关参数的设置
  522. setConf['fcharge'] = {
  523. 'watt': int(setConf.pop('fcharge_watt')),
  524. 'time': int(setConf.pop('fcharge_time')),
  525. 'check': int(setConf.pop('fcharge_check')),
  526. }
  527. # 空载检测参数配置
  528. setConf['noload_check'] = {}
  529. setConf['noload_check']['watt'] = int(setConf.pop('emptyPower'))
  530. setConf['noload_check']['time'] = int(setConf.pop('emptyTime'))
  531. # 加入funCode
  532. setConf.update({'fun_code': 11})
  533. # 发送设备参数
  534. devInfo = MessageSender.send(
  535. self.device,
  536. DeviceCmdCode.OPERATE_DEV_SYNC,
  537. {'IMEI': self._device['devNo'], 'data': setConf}
  538. )
  539. self.check_feedback_result(devInfo)
  540. def ack_event(self, orderNo, funCode):
  541. devInfo = MessageSender.send(self.device, DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE,
  542. {'IMEI': self._device['devNo'],
  543. 'data': {'fun_code': funCode, 'order_id': orderNo}})
  544. self.check_feedback_result(devInfo)
  545. def clear_dev_feecount(self):
  546. devInfo = MessageSender.send(self.device, self.make_random_cmdcode(),
  547. {'IMEI': self._device['devNo'],
  548. 'data': {'fun_code': 0x09, 'total_coin': True, 'total_card': True}})
  549. self.check_feedback_result(devInfo)
  550. def response_card_charge_result(self, cardNo, result):
  551. MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SERVER_ASYNC,
  552. {'IMEI': self._device['devNo'],
  553. 'data': {'fun_code': 37, 'card_no': cardNo, 'result': result}})
  554. def reboot_device(self):
  555. data = {'fun_code':0x0B,'reset_mcu':True}
  556. MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  557. {'IMEI': self._device['devNo'], 'data': data})
  558. MessageSender.async_send(self.device, DeviceCmdCode.SET_DEVINFO,
  559. {'IMEI': self._device['devNo'], 'restart': True})
  560. def set_device_function(self, request, lastSetConf):
  561. if request.POST.has_key('clearSum'):
  562. self.clear_dev_feecount()
  563. elif request.POST.has_key('reboot'):
  564. self.reboot_device()
  565. def set_device_function_param(self, request, lastSetConf):
  566. newConf = copy.deepcopy(request.POST)
  567. newConf.pop('logicalCode', None)
  568. if 'refundProtectionTime' in newConf:
  569. refundProtection = round(float(newConf.get('refundProtection', 0.0)), 1)
  570. Device.get_collection().update_one(filter={'devNo': self.device.devNo}, update={'$set': {'otherConf.refundProtection': refundProtection}})
  571. Device.invalid_device_cache(self.device.devNo)
  572. self.set_dev_setting(newConf)
  573. def get_card_pwd(self):
  574. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  575. {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x11}})
  576. self.check_feedback_result(devInfo)
  577. result = devInfo['data']
  578. enObj = EncryptDate(cardKey)
  579. cardPwd = enObj.decrypt(result['card_pwd'])
  580. if not cardPwd:
  581. cardPwd = ''
  582. return {'card_pwd': cardPwd}
  583. def translante_card_no(self, hexCardNo):
  584. return int(hexCardNo, 16)
  585. def check_pwd(self, pwd):
  586. if len(pwd) != 6:
  587. raise ServiceException({'result': 2, 'description': u'密码必须是6位纯数字密码'})
  588. for char in pwd:
  589. if not (char >= '0' and char <= '9'):
  590. raise ServiceException({'result': 2, 'description': u'密码必须是6位纯数字密码'})
  591. return
  592. def set_card_pwd(self, pwd):
  593. self.check_pwd(pwd)
  594. enObj = EncryptDate(cardKey)
  595. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  596. {'IMEI': self._device['devNo'],
  597. 'data': {'fun_code': 0x0B, 'card_pwd': enObj.encrypt(pwd)}})
  598. self.check_feedback_result(devInfo)
  599. def set_card_mode(self, setConf):
  600. cardMode = int(setConf.get('card_mode'))
  601. valueDict = {'fun_code': 0x10}
  602. if cardMode == 0:
  603. valueDict.update({'mode': cardMode})
  604. elif cardMode == 1: # 格式化为离线卡
  605. self.check_pwd(setConf['new_pwd'])
  606. newPwd = setConf['new_pwd']
  607. if not str(setConf['balance']).isdigit():
  608. raise ServiceException({'result': 2, 'description': u'余额必须是数字'})
  609. balance = int(setConf['balance'])
  610. if not (balance >= 0 and balance <= 5000):
  611. raise ServiceException({'result': 2, 'description': u'余额必须在0和5000元之间'})
  612. balance = balance * 10 # 硬件模块记录的单位是角
  613. enObj = EncryptDate(cardKey)
  614. valueDict.update({'mode': 1, 'new_pwd': enObj.encrypt(str(newPwd)), 'balance': balance})
  615. elif cardMode == 2:
  616. self.check_pwd(setConf['old_pwd'])
  617. self.check_pwd(setConf['new_pwd'])
  618. newPwd = setConf['new_pwd']
  619. oldPwd = setConf['old_pwd']
  620. enObj = EncryptDate(cardKey)
  621. valueDict.update({'mode': 2, 'old_pwd': enObj.encrypt(str(oldPwd)), 'new_pwd': enObj.encrypt(str(newPwd))})
  622. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  623. {'IMEI': self._device['devNo'], 'data': valueDict})
  624. self.check_feedback_result(devInfo)
  625. def get_card_mode(self):
  626. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  627. {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x11}})
  628. self.check_feedback_result(devInfo)
  629. cardMode = devInfo['data']['card_mode']
  630. return {'card_mode': cardMode}
  631. def check_alarm(self, alarm):
  632. if alarm.faultCode == FAULT_CODE.MCU_REBOOT:
  633. return u'此告警,建议您多观察,如果比较频繁不停上报单台设备的告警,可能会运行不稳定。需要您联系技术支持确认。'
  634. elif alarm.faultCode == FAULT_CODE.COUNTER_FAULT:
  635. portInfo = self.get_port_info(alarm.portNo)
  636. if portInfo['status'] == Const.DEV_WORK_STATUS_FAULT:
  637. return u'此端口目前已经为故障状态,继电器可能运行不稳定,建议您联系技术支持,以确认设备运行情况。'
  638. else:
  639. return u'端口状态检查正常,继电器或存偶尔无法获取数据。暂不影响使用。'
  640. elif alarm.faultCode == FAULT_CODE.RELAY_FAULT:
  641. return u'无法进行远程诊断,建议您到现场,直接插上插座,然后检查是否不用付款,就能够充电。如果是的,就属于继电器粘连。'
  642. elif alarm.faultCode == FAULT_CODE.DEV_OVERLOAD:
  643. return u'整机功率最大限定为7500瓦,接入的负载超过此负载,为了安全,将强行关闭所有充电端口。'
  644. elif alarm.faultCode == FAULT_CODE.COPY_CARD:
  645. return u'出现一模一样的卡,可能是用户复制了另外一张离线卡,然后使用,会给您造成经济上的损失,建议冻结。'
  646. return ''
  647. def get_part_info(self):
  648. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  649. {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x05}})
  650. self.check_feedback_result(devInfo)
  651. partInfo = devInfo['data']
  652. result = {}
  653. nameDesc = {'networkBoard': u'网络板', 'chargeBoard': u'充电板', 'cardBoard': u'刷卡板'}
  654. for k, v in partInfo.items():
  655. if k not in nameDesc:
  656. continue
  657. result[nameDesc.get(k)] = {'SN': v}
  658. return result
  659. def recharge_card(self, cardNo, money, orderNo=None):
  660. hex_cardNo = hex(int(cardNo))[2::].replace('L', '').upper()
  661. result = MessageSender.send(self.device, self.make_random_cmdcode(),
  662. {'IMEI': self.device['devNo'],
  663. 'data': {'fun_code': 37, 'result': 1, 'card_no': hex_cardNo,
  664. 'charge': int(money * 100),
  665. 'order_id': orderNo}})
  666. # 返回验证
  667. self.check_feedback_result(result)
  668. card = Card.objects.filter(cardNo=cardNo,dealerId=self.device.ownerId).first()
  669. if not card:
  670. return {
  671. 'result': ErrorCode.EXCEPTION,
  672. 'description': ''
  673. }, None
  674. balance = card.balance + money
  675. return {
  676. 'result': ErrorCode.SUCCESS,
  677. 'description': ''
  678. }, balance
  679. def recharge_card_async(self, cardNo, money, orderNo):
  680. cardNo = hex(int(cardNo))[2::].replace('L', '').upper()
  681. MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SERVER_ASYNC,
  682. {'IMEI': self._device['devNo'],
  683. 'data': {'fun_code': 37, 'result': 1, 'card_no': cardNo, 'charge': int(money * 100),
  684. 'order_id': orderNo}})
  685. @property
  686. def isHaveStopEvent(self):
  687. return True
  688. def stop(self, port = None):
  689. return self.stop_charging_port(port)
  690. def get_current_use(self, **kw):
  691. base_data = kw.get('base_data')
  692. spDict = kw.get('spDict')
  693. sp = ServiceProgress.objects.filter(device_imei=self.device.devNo, port=spDict['port']).first()
  694. data = {'fun_code': 2, 'port': spDict.get('port')}
  695. devInfo = self.send_mqtt(data)
  696. portInfo = devInfo.get('data', {})
  697. exec_orders = portInfo.get('orders', [])
  698. running_order = None
  699. waiting_order = []
  700. for exec_order in exec_orders: # 第一层筛选
  701. if exec_order['id'] not in sp.consumes:
  702. sp.consumes.remove(exec_order['id'])
  703. if exec_order['status'] == 'running':
  704. running_order = exec_order
  705. elif exec_order['status'] == 'waiting':
  706. waiting_order.append(exec_order)
  707. if len(sp.consumes) == 0: # 上一单没有正常结束 刷新此单的状态
  708. if running_order:
  709. sp.consumes.append(running_order['id'])
  710. sp.start_time = running_order['exec_time']
  711. sp.isFinished = False
  712. sp.finished_time = sp.start_time + running_order['amount_time']
  713. if waiting_order:
  714. for wait_one in waiting_order:
  715. sp.consumes.append(wait_one['id'])
  716. sp.finished_time = sp.start_time + wait_one['amount_time']
  717. sp.save()
  718. pay_unit = self.show_pay_unit
  719. result_list = [] # 数据整理返回
  720. for exec_order in exec_orders:
  721. item = {}
  722. item.update(base_data)
  723. item['orderNo'] = exec_order['id']
  724. item['port'] = exec_order['port']
  725. order = ConsumeRecord.objects.filter(orderNo=exec_order['id']).first()
  726. if exec_order['status'] == 'running':
  727. item['voltage'] = round(portInfo.get('volt', 0), 2)
  728. item['power'] = round(portInfo.get('watt', 0), 2)
  729. item['ampere'] = round((portInfo.get('ampr', 0) * 0.001), 2)
  730. item['usedTime'] = round(exec_order.get('time', 0) / 60.0, 1)
  731. # item['elec'] = round(exec_order.get('elec', 0) * 0.000001, 4)
  732. if exec_order['order_type'] == 'apps_start':
  733. try:
  734. item['consumeMoney'] = '{}{}'.format(round(exec_order.get('money', 0) * 0.01, 2), pay_unit)
  735. item['leftMoney'] = '{}{}'.format(round(exec_order.get('left_money', 0) * 0.01, 2), pay_unit)
  736. item['order'] = {
  737. 'orderNo': exec_order['id'], # 停止按钮传订单停单用
  738. 'coin': '{}{}'.format(round(order.coin, 2), pay_unit),
  739. 'port': exec_order['port'],
  740. 'consumeType': 'mobile',
  741. }
  742. except:
  743. pass
  744. elif exec_order['order_type'] == 'card_start':
  745. try:
  746. item['cardNo'] = exec_order['card_no']
  747. item['cardConsumeMoney'] = '{}{}'.format(round(exec_order.get('money', 0) * 0.01, 2), pay_unit)
  748. item['cardLeftMoney'] = '{}{}'.format(round(exec_order.get('left_money', 0) * 0.01, 2),
  749. pay_unit)
  750. item['order'] = {
  751. 'orderNo': exec_order['id'], # 停止按钮传订单停单用
  752. 'coin': '{}{}'.format(round(exec_order.get('amount', 0) * 0.01, 2), pay_unit),
  753. 'port': exec_order['port'],
  754. 'consumeType': 'card',
  755. }
  756. except:
  757. pass
  758. else:
  759. pass
  760. elif exec_order['status'] == 'waiting':
  761. if exec_order['chrmt'] == 'TIME':
  762. item['needTime'] = '{}分钟'.format(round(exec_order.get('amount_time', 0) / 60.0, 1))
  763. item['leftTime'] = round(exec_order.get('left_time', 0) / 60.0, 1)
  764. elif exec_order['chrmt'] == 'ELEC':
  765. item['needElec'] = round(exec_order.get('amount_elec', 0) * 0.000001, 2)
  766. item['leftElec'] = round(exec_order.get('left_elec', 0) * 0.000001, 2)
  767. item['desc'] = '此订单已经下发到设备上,上一单运行完毕就会自动运行此订单'
  768. item['order'] = {
  769. 'orderNo': exec_order['id'], # 停止按钮传订单停单用
  770. 'coin': '{}币'.format(round(exec_order.get('amount', 0) * 0.01, 2)),
  771. 'port': exec_order['port'],
  772. 'consumeType': 'card' if 'cardNo' in exec_order else 'mobile',
  773. }
  774. if order.monthlyPackageId: # 包月卡抵扣
  775. item.pop('cardConsumeMoney', None)
  776. item.pop('cardLeftMoney', None)
  777. item.pop('needTime', None)
  778. # item.pop('usedTime', None)
  779. item.pop('leftTime', None)
  780. if 'cardNo' in exec_order:
  781. item['cardNo'] = int(exec_order['card_no'], 16)
  782. item.update(DeviceType.get_services_button(self.device['devType']['id']))
  783. result_list.append(item)
  784. return result_list