weifule_touch.py 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import time
  5. from decimal import Decimal
  6. from apilib.monetary import RMB
  7. from apilib.utils_AES import EncryptDate
  8. from apps.web.constant import DeviceCmdCode, Const, MQTT_TIMEOUT, ErrorCode
  9. from apps.web.core.adapter.base import SmartBox
  10. from apps.web.core.exceptions import ServiceException
  11. from apps.web.core.networking import MessageSender
  12. from apps.web.device.models import Device, DeviceType
  13. from apps.web.user.models import Card, ServiceProgress, ConsumeRecord
  14. cardKey = 'FR4e1OFCnDdrYA7u'
  15. class TouchPadBox(SmartBox):
  16. def __init__(self, device):
  17. super(TouchPadBox, self).__init__(device)
  18. def translate_funcode(self, fun_code):
  19. fun_codeDict = {
  20. '01': u'查询所有端口状态',
  21. '02': u'查询端口详细信息',
  22. '03': u'上报投币充电事件',
  23. '04': u'上报刷卡事件',
  24. '05': u'上报充电结束事件',
  25. '06': u'远程停止充电',
  26. '07': u'远程启动充电',
  27. '08': u'查询投币总额',
  28. '09': u'清除投币总数',
  29. '0A': u'查询订单信息',
  30. }
  31. return fun_codeDict.get(fun_code, '')
  32. def translate_event_cmdcode(self, cmdCode):
  33. cmdDict = {
  34. }
  35. return cmdDict.get(cmdCode, '')
  36. def check_dev_status(self, attachParas=None):
  37. order = "" # type: ConsumeRecord
  38. package = order.package
  39. port = int(attachParas['chargeIndex'])
  40. port_stat = self.get_port_status_from_dev().get(str(port), {})
  41. if port_stat.get('status') not in [Const.DEV_WORK_STATUS_CONNECTED, Const.DEV_WORK_STATUS_WORKING]:
  42. raise ServiceException({'result': 2, 'description': u'请先连接充电插座,再启动充电'})
  43. if package.get('name') == '充满自停' and package.get('coins') == 0 and package.get('price') == 0:
  44. if port_stat.get('status') == Const.DEV_WORK_STATUS_WORKING:
  45. raise ServiceException({'result': 2, 'description': u'当前端口已处于工作状态,无法使用充满自停套餐, 请换个端口进行充电'})
  46. def get_port_info(self, port):
  47. data = {'fun_code': 2, 'port': int(port)}
  48. devInfo = self.send_mqtt(data)
  49. portInfo = devInfo.get('data', {})
  50. result = {}
  51. pay_unit = self.show_pay_unit
  52. exec_orders = portInfo.get('exec_orders', [])
  53. _wait = []
  54. for exec_order in exec_orders:
  55. if exec_order['status'] == 'running':
  56. result['voltage'] = round(portInfo.get('volt', 0), 2)
  57. result['power'] = round(portInfo.get('watt', 0), 2)
  58. result['ampere'] = round((portInfo.get('ampr', 0) * 0.001), 2)
  59. result['coins'] = '{}'.format(round(exec_order['amount'] / 100.0, 2))
  60. result['usedTime'] = round(exec_order.get('time', 0) / 60.0, 1)
  61. result['usedElec'] = round(exec_order.get('elec', 0) * 0.000001, 4)
  62. result['startTime'] = datetime.datetime.fromtimestamp(int(exec_order['exec_time'])).strftime(
  63. '%m-%d %H:%M:%S')
  64. if exec_order['chrmt'] == 'TIME':
  65. result['leftTime'] = round(exec_order.get('left_time', 0) / 60.0, 1)
  66. result['needTime'] = '{}分钟'.format(round(exec_order['amount_time'] / 60.0, 1))
  67. elif exec_order['chrmt'] == 'ELEC':
  68. result['needElec'] = round(exec_order.get('amount_elec', 0) * 0.000001, 2)
  69. result['leftElec'] = round(exec_order.get('left_elec', 0) * 0.000001, 4)
  70. else:
  71. pass
  72. if exec_order['order_type'] == 'apps_start':
  73. result['consumeType'] = 'mobile'
  74. result['consumeMoney'] = '{}{}'.format(round(exec_order.get('money', 0) * 0.01, 2), pay_unit)
  75. result['leftMoney'] = '{}{}'.format(round(exec_order.get('left_money', 0) * 0.01, 2),
  76. pay_unit)
  77. try:
  78. orderNo = exec_order['id']
  79. order = ConsumeRecord.objects.filter(orderNo=orderNo).first()
  80. if not order:
  81. result['nickName'] = '经销商上分'
  82. else:
  83. result['nickName'] = order.user.nickname
  84. package = order.package
  85. if package.get('name') == '充满自停' and package.get('coins') == 0 and package.get(
  86. 'price') == 0: # 后付费
  87. result['consumeType'] = 'postpaid'
  88. result['needTime'] = '充满自停'
  89. result.pop('needElec', None)
  90. result.pop('leftMoney', None)
  91. result.pop('leftTime', None)
  92. result.pop('leftElec', None)
  93. except:
  94. pass
  95. if exec_order['order_type'] == 'card_start':
  96. result['consumeType'] = 'card'
  97. result['cardNo'] = exec_order['card_no']
  98. result['cardConsumeMoney'] = '{}{}'.format(round(exec_order.get('money', 0) * 0.01, 2),
  99. pay_unit)
  100. result['cardLeftMoney'] = '{}{}'.format(round(exec_order.get('left_money', 0) * 0.01, 2),
  101. pay_unit)
  102. result['cardBalance'] = '{}{}'.format(round(exec_order['balance'] * 0.01, 2), pay_unit)
  103. try:
  104. card = Card.objects.get(cardNo=exec_order['card_no'])
  105. result['nickName'] = card.cardName or card.nickName
  106. except:
  107. pass
  108. else:
  109. pass
  110. if exec_order['status'] == 'waiting':
  111. _one = {}
  112. if exec_order['chrmt'] == 'TIME':
  113. _one['leftTime'] = round(exec_order.get('left_time', 0) / 60.0, 1)
  114. _one['needTime'] = '{}分钟'.format(round(exec_order['amount_time'] / 60.0, 1))
  115. elif exec_order['chrmt'] == 'ELEC':
  116. _one['leftElec'] = round(exec_order.get('left_elec', 0) * 0.000001, 4)
  117. _one['needElec'] = round(exec_order.get('amount_elec', 0) * 0.000001, 2)
  118. else:
  119. pass
  120. if exec_order['order_type'] == 'apps_start':
  121. _one['consumeType'] = 'mobile'
  122. _one['coins'] = '{}{}'.format(round(exec_order['amount'] / 100.0, 2), pay_unit)
  123. try:
  124. order = ConsumeRecord.objects.filter(orderNo=exec_order['id']).first()
  125. if not order:
  126. _one['nickName'] = '经销商上分'
  127. else:
  128. _one['nickName'] = order.user.nickname
  129. except Exception:
  130. pass
  131. elif exec_order['order_type'] == 'card_start':
  132. _one['consumeType'] = 'card'
  133. _one['coins'] = '{}{}'.format(round(exec_order['amount'] / 100.0, 2), pay_unit)
  134. _one['cardNo'] = exec_order['card_no']
  135. _one['cardBalance'] = '{}{}'.format(round(exec_order['balance'] * 0.01, 2), pay_unit)
  136. try:
  137. card = Card.objects.get(cardNo=exec_order['card_no'])
  138. _one['nickName'] = card.cardName or card.nickName
  139. except:
  140. pass
  141. else:
  142. pass
  143. _wait.append(_one)
  144. if _wait:
  145. result['waittingOrder'] = _wait
  146. return result
  147. def send_mqtt(self, data, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, timeout=10):
  148. """
  149. 发送mqtt 指令默认210 返回data
  150. """
  151. payload = {'IMEI': self.device.devNo, 'data': data}
  152. result = MessageSender.send(self.device, cmd,
  153. payload=payload, timeout=timeout)
  154. if not result.has_key('rst'):
  155. raise ServiceException({'result': 2, 'description': u'报文异常'})
  156. if result['rst'] == -1:
  157. raise ServiceException({'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  158. if result['rst'] == 1:
  159. raise ServiceException({'result': 2, 'description': u'串口通讯失败,您稍候再试,或者联系客服'})
  160. if result['rst'] == 2:
  161. raise ServiceException({'result': 2, 'description': u'订单已满'})
  162. if result['rst'] == 3:
  163. raise ServiceException({'result': 2, 'description': u'端口已被禁用'})
  164. if result['rst'] == 4:
  165. raise ServiceException({'result': 2, 'description': u'检测插头未连接,请先插上插头后再使用'})
  166. if result['rst'] == 5:
  167. raise ServiceException({'result': 2, 'description': u'计费类型无效'})
  168. if result['rst'] == 7:
  169. raise ServiceException({'result': 2, 'description': u'端口计量故障'})
  170. else:
  171. if cmd in [DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE]:
  172. return
  173. if result.get('data') == '00':
  174. raise ServiceException({'result': 2, 'description': u'设备操作失败.请重试'})
  175. else:
  176. return result
  177. def check_params_range(self, params, minData=None, maxData=None, desc=''):
  178. # type:(str,float,float,str) -> str
  179. """
  180. 检查参数,返回字符串参数
  181. """
  182. if params is None:
  183. raise ServiceException({'result': 2, 'description': u'{} 参数错误(值为空).'.format(desc)})
  184. if not isinstance(params, Decimal):
  185. params = Decimal(params)
  186. if not minData and maxData:
  187. if not isinstance(maxData, Decimal):
  188. maxData = Decimal(maxData)
  189. if params <= maxData:
  190. return '%g' % params
  191. else:
  192. raise ServiceException(
  193. {'result': 2, 'description': u'%s超出可选范围(值为: %s),可选最大值为%g' % (desc, params, maxData)})
  194. if not maxData and minData:
  195. if not isinstance(minData, Decimal):
  196. minData = Decimal(minData)
  197. if minData <= params:
  198. return '%g' % params
  199. else:
  200. raise ServiceException(
  201. {'result': 2, 'description': u'%s超出可选范围(值为: %s),可选最小值为%g' % (desc, params, minData)})
  202. if not minData and not maxData:
  203. return '%g' % params
  204. else:
  205. if not isinstance(minData, Decimal):
  206. minData = Decimal(minData)
  207. if not isinstance(maxData, Decimal):
  208. maxData = Decimal(maxData)
  209. if minData <= params <= maxData:
  210. return '%g' % params
  211. else:
  212. raise ServiceException(
  213. {'result': 2, 'description': u'%s参数超出可选范围(值为: %s),可取范围为%g-%g' % (desc, params, minData, maxData)})
  214. def test(self, coins):
  215. data = {'fun_code': 0x07, 'order_id': '1111', 'coins': coins, 'port': 1, 'time': 60}
  216. devInfo = self.send_mqtt(data)
  217. return devInfo
  218. def _check_package(self, package):
  219. """
  220. 获取设备启动的发送数据 根据设备的当前模式以及套餐获取
  221. :param package:
  222. :return:
  223. """
  224. unit = package.get('unit')
  225. _time = package.get('time')
  226. chrmt = self.device['otherConf'].get('chrmt')
  227. if not chrmt:
  228. chrmt = self.get_dev_setting().get('chrmt')
  229. # 按时间计费
  230. if chrmt == 'TIME' or chrmt == 'POWER':
  231. billingType = 'time'
  232. if unit == u'小时':
  233. _time *= 60
  234. elif unit == u'天':
  235. _time = _time * 24 * 60
  236. elif unit == u'秒':
  237. _time /= 60
  238. elif unit == u'分钟':
  239. _time = _time
  240. else:
  241. raise ServiceException({'result': 2, 'description': u'套餐单位错误,应选取单位(时间),请联系经销商'})
  242. # 按电量计费
  243. elif chrmt == 'ELEC':
  244. billingType = 'elec'
  245. if unit != u'度':
  246. raise ServiceException({'result': 2, 'description': u'套餐单位错误,应选取单位(度),请联系经销商'})
  247. else:
  248. _time *= 100
  249. else:
  250. raise ServiceException({'result': 2, 'description': u'套餐单位错误,应选取单位(时间,度),请联系经销商'})
  251. return _time, unit, billingType
  252. # 访问设备,获取设备端口信息
  253. def get_port_status_from_dev(self):
  254. data = {'fun_code': 0x01, 'all': True}
  255. devInfo = self.send_mqtt(data)
  256. portData = devInfo['data']['port_stat']
  257. result = {}
  258. for k, v in portData.items():
  259. if v == 'idle':
  260. result[k] = {'status': Const.DEV_WORK_STATUS_IDLE}
  261. elif v == 'link':
  262. result[k] = {'status': Const.DEV_WORK_STATUS_CONNECTED}
  263. elif v == 'busy':
  264. result[k] = {'status': Const.DEV_WORK_STATUS_WORKING}
  265. elif v == 'fault':
  266. result[k] = {'status': Const.DEV_WORK_STATUS_FAULT}
  267. elif v == 'forbid':
  268. result[k] = {'status': Const.DEV_WORK_STATUS_FORBIDDEN}
  269. else:
  270. result[k] = {'status': Const.DEV_WORK_STATUS_FAULT}
  271. allPorts, usedPorts, usePorts = self.get_port_static_info(result)
  272. Device.update_dev_control_cache(self._device['devNo'],
  273. {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  274. # 这里存在多线程更新缓存的场景,可能性比较低,但是必须先取出来,然后逐个更新状态,然后再记录缓存
  275. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  276. for strPort, info in result.items():
  277. if ctrInfo.has_key(strPort):
  278. ctrInfo[strPort].update({'status': info['status']})
  279. else:
  280. ctrInfo[strPort] = info
  281. Device.update_dev_control_cache(self._device['devNo'], ctrInfo)
  282. return result
  283. def get_port_status(self, force=False):
  284. if force:
  285. return self.get_port_status_from_dev()
  286. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  287. if not ctrInfo.has_key('allPorts'):
  288. self.get_port_status_from_dev()
  289. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  290. allPorts = ctrInfo.get('allPorts', 2)
  291. statusDict = {}
  292. for ii in range(allPorts):
  293. tempDict = ctrInfo.get(str(ii + 1), {})
  294. if tempDict.has_key('status'):
  295. statusDict[str(ii + 1)] = {'status': tempDict.get('status')}
  296. elif tempDict.has_key('isStart'):
  297. if tempDict['isStart']:
  298. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_WORKING}
  299. else:
  300. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  301. else:
  302. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  303. allPorts, usedPorts, usePorts = self.get_port_static_info(statusDict)
  304. Device.update_dev_control_cache(self._device['devNo'],
  305. {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  306. return statusDict
  307. def start_device(self, package, openId, attachParas):
  308. if attachParas is None:
  309. raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路、电池类型信息'})
  310. if not attachParas.has_key('chargeIndex'):
  311. raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路'})
  312. port = int(attachParas['chargeIndex'])
  313. _time, unit, billingType = self._check_package(package)
  314. if package.get('name') == '充满自停' and package.get('coins') == 0 and package.get('price') == 0:
  315. coins = 10 * 100
  316. else:
  317. coins = int(float(package['coins']) * 100) # 单位为分
  318. if attachParas.get('onPoints'):
  319. orderNo = ConsumeRecord.make_no() + self.device.owner.username
  320. else:
  321. orderNo = attachParas.get('orderNo')
  322. data = {
  323. 'fun_code': 0x07,
  324. 'port': port,
  325. 'amount': coins,
  326. 'order_id': orderNo
  327. }
  328. devInfo = self.send_mqtt(data, timeout=MQTT_TIMEOUT.START_DEVICE)
  329. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  330. lineInfo = ctrInfo.get(str(port), {})
  331. if not lineInfo or lineInfo.get('status') == Const.DEV_WORK_STATUS_IDLE:
  332. lineInfo = {
  333. 'port': str(port),
  334. 'status': Const.DEV_WORK_STATUS_WORKING,
  335. 'order_type': 'apps_start',
  336. 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  337. 'orderNo': orderNo,
  338. }
  339. Device.update_port_control_cache(self._device['devNo'], lineInfo)
  340. devInfo['consumeOrderNo'] = orderNo
  341. devInfo['sequanceNo'] = orderNo
  342. if billingType == 'time':
  343. devInfo['finished_time'] = int(time.time()) + _time * 60
  344. else:
  345. devInfo['finished_time'] = int(time.time()) + 3600 * 12
  346. devInfo['ignoreService'] = True
  347. devInfo['servicedInfo'] = {
  348. 'chargeIndex': port
  349. }
  350. return devInfo
  351. def active_deactive_port(self, port, active):
  352. if active:
  353. raise ServiceException({'result': 2, 'description': u'该设备不支持直接打开端口'})
  354. return self.stop_charging_port(port)
  355. # 停止该端口下的所有任务
  356. def stop_charging_port(self, port):
  357. data = {'fun_code': 0x06, 'port': port}
  358. devInfo = self.send_mqtt(data)
  359. if devInfo['rst'] == 0:
  360. Device.update_dev_control_cache(self._device['devNo'], {str(port): {'status': Const.DEV_WORK_STATUS_IDLE}})
  361. return True if devInfo['rst'] == 0 else False
  362. def analyze_event_data(self, data):
  363. if data['fun_code'] == '32': # 成功启动设备需要回复该事件
  364. return data['order']
  365. if data['fun_code'] == '34': # 如果是结束事件,需要把reason翻译出来
  366. descDict = {
  367. 'outofmoney': u'购买的充电时间或电量用完了',
  368. 'userstop': u'用户远程结束充电',
  369. 'adminstop': u'管理员远程结束充电',
  370. 'dealerstop': u'经销商远程结束充电',
  371. 'unplug': u'未检测到充电器, 结束充电',
  372. 'wattoverload': u'功率过载, 结束充电',
  373. 'voltoverload': u'电压过载, 结束充电',
  374. 'amproverload': u'电流过载, 结束充电',
  375. 'machoverload': u'整机功率过载, 结束充电',
  376. 'tempexcursion': u'温度过载, 结束充电',
  377. 'stopforfull': u'充满自停, 结束充电'
  378. }
  379. order = data['order']
  380. order['reasonDesc'] = descDict.get(str(order['cause_desc']), u'')
  381. data['order'] = order
  382. return data
  383. def ack_event(self, order_id, fun_code):
  384. data = {'order_id': order_id, 'fun_code': fun_code}
  385. self.send_mqtt(data)
  386. def get_card_pwd(self):
  387. devInfo = self.send_mqtt({'fun_code': 17})
  388. data = devInfo.get('data', {})
  389. card_cur_key = data.get('card_cur_key')
  390. enObj = EncryptDate(cardKey)
  391. card_cur_key = enObj.decrypt(card_cur_key)
  392. return {'card_pwd': card_cur_key}
  393. def set_device_function(self, request, lastSetConf):
  394. if request.POST.has_key('clearSum'):
  395. self.clear_dev_feecount()
  396. elif request.POST.has_key('reboot'):
  397. self.reboot_device()
  398. def set_device_function_param(self, request, lastSetConf):
  399. """
  400. 设置参数 对于计费模式做一下转换 顺便服务器保留一份
  401. :param request:
  402. :param lastSetConf:
  403. :return:
  404. """
  405. # 服务器侧的参数 先处理掉
  406. if request.POST.get('id_card_oncefee') is not None:
  407. id_card_oncefee = request.POST['id_card_oncefee']
  408. if not id_card_oncefee:
  409. raise ServiceException({'result': 2, 'description': u'在线卡单次刷卡金额不能为0'})
  410. Device.get_collection().update_one(filter={'devNo': self.device.devNo},
  411. update={'$set': {
  412. 'otherConf.id_card_oncefee': round(float(id_card_oncefee), 2),
  413. }})
  414. Device.invalid_device_cache(self.device.devNo)
  415. if request.POST.get('refundProtection') is not None:
  416. refundProtection = request.POST['refundProtection']
  417. Device.get_collection().update_one(filter={'devNo': self.device.devNo},
  418. update={'$set': {
  419. 'otherConf.refundProtection': round(float(refundProtection), 2),
  420. }})
  421. Device.invalid_device_cache(self.device.devNo)
  422. if request.POST.get('minAfterStartCoins') is not None:
  423. minAfterStartCoins = request.POST['minAfterStartCoins']
  424. Device.get_collection().update_one(filter={'devNo': self.device.devNo},
  425. update={'$set': {
  426. 'otherConf.minAfterStartCoins': round(float(minAfterStartCoins), 2),
  427. }})
  428. Device.invalid_device_cache(self.device.devNo)
  429. # 参数发向设备 11, 16指令
  430. setconfig1 = {'fun_code': 11}
  431. setconfig2 = {'fun_code': 16}
  432. # 音量适配
  433. if request.POST.get('volume_list'):
  434. volume_list = request.POST.get('volume_list')
  435. volumes = {}
  436. self.check_params_range(params=request.POST.get('volume'), minData=0.0, maxData=7.0, desc='语音音量参数')
  437. volumes.update({'default': int(request.POST.get('volume'))})
  438. for obj in volume_list:
  439. self.check_params_range(params=obj['volume'], minData=0.0, maxData=7.0, desc='音量参数')
  440. volumes.update({'{}-{}'.format(obj['start'], obj['end']): int(obj['volume'])})
  441. setconfig1.update({'volumes': volumes})
  442. # 计价模式适配
  443. chrmt = request.POST.get('chrmt')
  444. package_time = request.POST.get('package_time', [])
  445. package_elec = request.POST.get('package_elec', [])
  446. package_power = request.POST.get('package_power', [])
  447. if chrmt:
  448. price = {'ELEC': int(float(package_elec[0]['elec']) * 1000000)}
  449. # 组织功率收费价格参数
  450. TIME = []
  451. if chrmt == 'POWER': # 主板实际并没有power计费方式 实际为功率分档模式的换一种显示
  452. for _item in package_power:
  453. if float(_item['price']) == 0:
  454. raise ServiceException({'result': 2, 'description': u'计费参数设置(按功率收费)价格不能为0'})
  455. TIME.append(
  456. [int(_item['power']), round(1.0 / float(_item['price']) * 60 * 60, 1)]
  457. # 价格倍率 * 固定时间(60分钟) * 60秒
  458. )
  459. if not TIME:
  460. raise ServiceException({'result': 2, 'description': u'计费参数设置(按功率收费)缺少收费标准'})
  461. setconfig1['chrmt'] = 'TIME'
  462. elif chrmt == 'TIME': # 时间模式下 的时间计费规则
  463. for _item in package_time:
  464. TIME.append(
  465. [int(_item['power']), int(_item['time']) * 60]
  466. )
  467. if not TIME:
  468. raise ServiceException({'result': 2, 'description': u'计费参数设置(按时间收费)缺少收费标准'})
  469. price['TIME'] = TIME
  470. setconfig1['chrmt'] = 'TIME'
  471. elif chrmt == 'ELEC': # 同样也处理一次时间计费规则 用于下发给设备
  472. for _item in package_time:
  473. TIME.append(
  474. [int(_item['power']), int(_item['time']) * 60]
  475. )
  476. setconfig1['chrmt'] = 'ELEC'
  477. price['TIME'] = TIME
  478. setconfig1.update({'price': price})
  479. if request.POST.get('auto_close') is not None:
  480. setconfig1.update({'auto_close': request.POST.get('auto_close')})
  481. if request.POST.get('float_time') or request.POST.get('float_watt') or request.POST.get('check_time'):
  482. float_charge = {}
  483. if request.POST.get('float_time'):
  484. float_charge['float_time'] = int(request.POST.get('float_time'))
  485. if request.POST.get('float_watt'):
  486. float_charge['float_watt'] = int(request.POST.get('float_watt'))
  487. if request.POST.get('check_time'):
  488. float_charge['check_time'] = int(request.POST.get('check_time'))
  489. setconfig1.update({'float_charge': float_charge})
  490. if request.POST.get('port_max_watt'):
  491. setconfig1.update({'port_max_watt': int(request.POST.get('port_max_watt'))})
  492. if request.POST.get('port_max_ampr'):
  493. setconfig1.update({'port_max_ampr': int(request.POST.get('port_max_ampr'))})
  494. if request.POST.get('mach_max_watt'):
  495. setconfig1.update({'mach_max_watt': int(request.POST.get('mach_max_watt'))})
  496. if request.POST.get('mach_max_temp'):
  497. setconfig1.update({'mach_max_temp': int(request.POST.get('mach_max_temp'))})
  498. if request.POST.get('mach_max_volt'):
  499. setconfig1.update({'mach_max_volt': int(request.POST.get('mach_max_volt'))})
  500. if request.POST.get('noload_check_watt') or request.POST.get('noload_check_time'):
  501. noload_check = {}
  502. if request.POST.get('noload_check_watt'):
  503. noload_check['watt'] = int(request.POST.get('noload_check_watt'))
  504. if request.POST.get('noload_check_time'):
  505. noload_check['time'] = int(request.POST.get('noload_check_time'))
  506. setconfig1.update({'noload_check': noload_check})
  507. if request.POST.get('card_pwd'):
  508. card_pwd = request.POST.get('card_pwd')
  509. card_pwd2 = request.POST.get('card_pwd2')
  510. if card_pwd != card_pwd2:
  511. raise ServiceException({'result': 2, 'description': u'两次密码输入不一致'})
  512. self.check_pwd(card_pwd)
  513. enObj = EncryptDate(cardKey)
  514. setconfig2.update({'card_cur_key': enObj.encrypt(card_pwd)})
  515. self.send_mqtt(setconfig1)
  516. unit_price = None
  517. if chrmt == 'TIME':
  518. unit_price = {'unit': '分钟', 'value': max(map(lambda _: _[1] / 60.0, setconfig1['price']['TIME']))}
  519. elif chrmt == 'POWER':
  520. unit_price = {'unit': '分钟', 'value': max(map(lambda _: _[1] / 60.0, setconfig1['price']['TIME']))}
  521. elif chrmt == 'ELEC':
  522. unit_price = {'unit': '度', 'value': round(setconfig1['price']['ELEC'] / 1000000.0, 2)}
  523. if unit_price:
  524. Device.get_collection().update_one(filter={'devNo': self.device.devNo},
  525. update={'$set': {
  526. 'otherConf.unit_price': unit_price,
  527. 'otherConf.chrmt': chrmt,
  528. }})
  529. Device.invalid_device_cache(self.device.devNo)
  530. if request.POST.get('card_refund', None) is not None:
  531. setconfig2.update({'card_refund': request.POST.get('card_refund')})
  532. if request.POST.get('card_disable', None) is not None:
  533. setconfig2.update({'card_disable': request.POST.get('card_disable')})
  534. if request.POST.get('card_timeout'):
  535. setconfig2.update({'card_timeout': int(request.POST.get('card_timeout'))})
  536. if request.POST.get('card_oncefee'):
  537. setconfig2.update({'card_oncefee': int(float(request.POST.get('card_oncefee')) * 100)})
  538. self.send_mqtt(setconfig2)
  539. def get_dev_setting(self):
  540. """
  541. 获取参数显示在前台
  542. :return:
  543. """
  544. # 服务器侧的参数
  545. otherConf = self.device['otherConf']
  546. # 获取主板侧的参数
  547. devInfo = self.send_mqtt({'fun_code': 12})
  548. # 参数整合
  549. data = dict()
  550. # ID卡刷卡一次的费用
  551. data['id_card_oncefee'] = otherConf.get('id_card_oncefee', 1)
  552. data['refundProtection'] = otherConf.get('refundProtection', 5)
  553. data['minAfterStartCoins'] = otherConf.get('minAfterStartCoins', 0)
  554. configs = devInfo['data']
  555. # 价格适配
  556. price_for_time = configs['price']['TIME']
  557. package_time = []
  558. chrmt = configs.get('chrmt')
  559. # 按功率分档收费显示
  560. for _item in price_for_time:
  561. package_time.append({'power': _item[0], 'time': round(_item[1] / 60.0, 1)})
  562. # 按电量收费
  563. price_for_elec = round(configs['price']['ELEC'] / 1000000.0, 4)
  564. package_elec = [{'elec': price_for_elec, 'price': 1}] # 电量的固定单位是1
  565. # 按功率收费显示
  566. package_power = []
  567. for _item in price_for_time:
  568. package_power.append({'power': _item[0], 'price': round(1.0 / (_item[1] / 60.0 / 60.0), 2)})
  569. # 计费规则的添加 有就显示服务器的, 没有就显示主板的
  570. data.update({
  571. 'package_time': package_time,
  572. 'package_elec': package_elec,
  573. 'package_power': package_power,
  574. 'chrmt': otherConf.get('chrmt', chrmt) # 以缓存的为准
  575. })
  576. # 浮充参数的添加
  577. float_charge = configs.get('float_charge', {})
  578. data.update(float_charge)
  579. data['auto_close'] = configs.get('auto_close', {})
  580. data['mach_max_watt'] = configs.get('mach_max_watt', 0)
  581. data['port_max_ampr'] = configs.get('port_max_ampr', 0)
  582. data['port_max_watt'] = configs.get('port_max_watt', 0)
  583. data['mach_max_volt'] = configs.get('mach_max_volt', 0)
  584. data['mach_max_temp'] = configs.get('mach_max_temp', 0)
  585. # 空载适配
  586. data.update({
  587. 'noload_check_watt': int(configs['noload_check'].get('watt', 0)),
  588. 'noload_check_time': int(configs['noload_check'].get('time', 0)),
  589. })
  590. # 音量适配
  591. volume_list = []
  592. for k, v in configs['volumes'].items():
  593. item = {}
  594. if k == 'default':
  595. continue
  596. if '-' in k:
  597. item['start'], item['end'] = k.split('-')
  598. item['volume'] = v
  599. volume_list.append(item)
  600. data.update({
  601. 'volume': configs['volumes']['default'],
  602. 'volume_list': volume_list
  603. })
  604. # 17 指令
  605. devInfo = self.send_mqtt({'fun_code': 17})
  606. configs2 = devInfo.get('data')
  607. data['card_curmode'] = configs2['card_curmode']
  608. data['card_refund'] = configs2['card_refund']
  609. data['card_disable'] = configs2['card_disable']
  610. # data['card_token'] = configs2['card_token']
  611. data['card_timeout'] = configs2['card_timeout']
  612. data['card_oncefee'] = round(configs2['card_oncefee'] / 100.0, 2)
  613. state = self.get_dev_consume_count()
  614. data.update(state)
  615. return data
  616. def get_dev_consume_count(self):
  617. data = {'fun_code': 8}
  618. devInfo = self.send_mqtt(data)
  619. total_card = round(devInfo.get('data', {}).get('total_card', 0) * 0.01, 2)
  620. return {'total_card': total_card}
  621. def reset_total_card(self):
  622. data = {
  623. 'fun_code': 9
  624. }
  625. self.send_mqtt(data)
  626. def set_max_watt(self, max_watt):
  627. self.send_mqtt({'fun_code': 11, 'max_watt': int(max_watt)})
  628. def lock_unlock_port(self, port, lock=True):
  629. portInfo = self.get_port_info(port)
  630. if portInfo and portInfo['status'] == Const.DEV_WORK_STATUS_WORKING and lock:
  631. raise ServiceException({'result': 2, 'description': u'端口正忙,请先关闭端口后,再禁止端口'})
  632. typeStr = not lock
  633. self.send_mqtt({'port': int(port), 'enable': typeStr, 'fun_code': 13})
  634. def check_pwd(self, pwd):
  635. if len(pwd) != 6:
  636. raise ServiceException({'result': 2, 'description': u'密码必须是6位纯数字密码'})
  637. for char in pwd:
  638. if not (char >= '0' and char <= '9'):
  639. raise ServiceException({'result': 2, 'description': u'密码必须是6位纯数字密码'})
  640. return
  641. def get_card_mode(self):
  642. devInfo = self.send_mqtt({'fun_code': 17})
  643. MAP_MODE = {
  644. 'NORMAL': 0,
  645. 'ISSUE': 1,
  646. 'MODKEY': 2,
  647. }
  648. result = {}
  649. result['card_mode'] = MAP_MODE.get(devInfo['data']['card_curmode'])
  650. return result
  651. def set_card_mode(self, setConf):
  652. cardMode = int(setConf.get('card_mode'))
  653. data = {'fun_code': 16}
  654. if cardMode == 0:
  655. data.update({'card_curmode': {'mod': 'NORMAL'}})
  656. elif cardMode == 1: # 格式化为离线卡
  657. self.check_pwd(setConf['new_pwd'])
  658. newPwd = setConf['new_pwd']
  659. if not str(setConf['balance']).isdigit():
  660. raise ServiceException({'result': 2, 'description': u'余额必须是数字'})
  661. balance = int(setConf['balance'])
  662. if not (balance >= 0 and balance <= 5000):
  663. raise ServiceException({'result': 2, 'description': u'余额必须在0和5000元之间'})
  664. card_dft_val = balance * 100 # 硬件模块记录的单位是角
  665. enObj = EncryptDate(cardKey)
  666. data.update({'card_curmode': {'mod': 'ISSUE', 'cfg': {'card_new_key': enObj.encrypt(str(newPwd)),
  667. 'card_dft_val': card_dft_val}}})
  668. elif cardMode == 2:
  669. self.check_pwd(setConf['new_pwd'])
  670. oldPwd = setConf.get('old_pwd', '')
  671. newPwd = setConf.get('new_pwd', '')
  672. enObj = EncryptDate(cardKey)
  673. data.update({'card_curmode': {'mod': 'MODKEY', 'cfg': {'card_old_key': enObj.encrypt(str(oldPwd)),
  674. 'card_new_key': enObj.encrypt(str(newPwd))}}})
  675. self.send_mqtt(data)
  676. def response_card_charge_result(self, cardNo, result):
  677. self.send_mqtt({'fun_code': 36, 'card_no': cardNo, 'result': result})
  678. def recharge_card(self, cardNo, money, orderNo=None):
  679. self.send_mqtt(
  680. {'fun_code': 36, 'result': 1, 'card_no': cardNo, 'charge': int(money * 100), 'order_id': orderNo})
  681. card = Card.objects.filter(cardNo=cardNo, dealerId=self.device.ownerId).first()
  682. balance = card.balance + money
  683. return {
  684. 'result': ErrorCode.SUCCESS,
  685. 'description': ''
  686. }, balance
  687. def response_card_balance(self, cardNo, amount, balance, result):
  688. return self.send_mqtt({'fun_code': 35, 'card_no': cardNo, 'balance': RMB.yuan_to_fen(balance),
  689. 'result': result, 'amount': RMB.yuan_to_fen(amount)})
  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('exec_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['chrmt'] == 'TIME':
  733. item['needTime'] = '{}分钟'.format(round(exec_order.get('amount_time', 0) / 60.0, 1))
  734. item['leftTime'] = round(exec_order.get('left_time', 0) / 60.0, 1)
  735. elif exec_order['chrmt'] == 'ELEC':
  736. item['needElec'] = round(exec_order.get('amount_elec', 0) * 0.000001, 2)
  737. item['leftElec'] = round(exec_order.get('left_elec', 0) * 0.000001, 4)
  738. else:
  739. pass
  740. if exec_order['order_type'] == 'apps_start':
  741. try:
  742. item['consumeMoney'] = '{}{}'.format(round(exec_order.get('money', 0) * 0.01, 2), pay_unit)
  743. item['leftMoney'] = '{}{}'.format(round(exec_order.get('left_money', 0) * 0.01, 2), pay_unit)
  744. item['order'] = {
  745. 'orderNo': exec_order['id'], # 停止按钮传订单停单用
  746. 'coin': '{}{}'.format(round(order.coin, 2), pay_unit),
  747. 'port': exec_order['port'],
  748. 'consumeType': 'mobile',
  749. }
  750. if order and order.package.get('name') == '充满自停' and order.package.get('coins') == 0 and order.package.get(
  751. 'price') == 0: # 后付费
  752. item['order'] = {
  753. 'orderNo': exec_order['id'], # 停止按钮传订单停单用
  754. 'coin': '{}{}'.format(0, pay_unit),
  755. 'port': exec_order['port'],
  756. 'consumeType': 'postpaid'
  757. }
  758. item['needTime'] = '充满自停'
  759. item.pop('needElec', None)
  760. item.pop('leftMoney', None)
  761. item.pop('leftTime', None)
  762. item.pop('leftElec', None)
  763. elif order and order.attachParas.get('vCardId'):
  764. item['order']['consumeType'] = 'mobile_vcard'
  765. elif order and order.monthlyPackageId: # 包月卡抵扣
  766. item['order']['consumeType'] = 'monthlyPackage'
  767. item.pop('consumeMoney', None)
  768. item.pop('leftMoney', None)
  769. item.pop('needTime', None)
  770. # item.pop('usedTime', None)
  771. item.pop('leftTime', None)
  772. except:
  773. pass
  774. elif exec_order['order_type'] == 'card_start':
  775. try:
  776. item['cardNo'] = exec_order['card_no']
  777. item['cardConsumeMoney'] = '{}{}'.format(round(exec_order.get('money', 0) * 0.01, 2), pay_unit)
  778. item['cardLeftMoney'] = '{}{}'.format(round(exec_order.get('left_money', 0) * 0.01, 2),
  779. pay_unit)
  780. item['order'] = {
  781. 'orderNo': exec_order['id'], # 停止按钮传订单停单用
  782. 'coin': '{}{}'.format(round(exec_order.get('amount', 0) * 0.01, 2), pay_unit),
  783. 'port': exec_order['port'],
  784. 'consumeType': 'card',
  785. }
  786. if order and order.monthlyPackageId: # 包月卡抵扣
  787. item['order']['consumeType'] = 'monthlyPackage'
  788. item.pop('cardConsumeMoney', None)
  789. item.pop('cardLeftMoney', None)
  790. item.pop('needTime', None)
  791. # item.pop('usedTime', None)
  792. item.pop('leftTime', None)
  793. except:
  794. pass
  795. else:
  796. pass
  797. elif exec_order['status'] == 'waiting':
  798. if exec_order['chrmt'] == 'TIME':
  799. item['needTime'] = '{}分钟'.format(round(exec_order.get('amount_time', 0) / 60.0, 1))
  800. item['leftTime'] = round(exec_order.get('left_time', 0) / 60.0, 1)
  801. elif exec_order['chrmt'] == 'ELEC':
  802. item['needElec'] = round(exec_order.get('amount_elec', 0) * 0.000001, 2)
  803. item['leftElec'] = round(exec_order.get('left_elec', 0) * 0.000001, 2)
  804. item['desc'] = '此订单已经下发到设备上,上一单运行完毕就会自动运行此订单'
  805. item['order'] = {
  806. 'orderNo': exec_order['id'], # 停止按钮传订单停单用
  807. 'coin': '{}币'.format(round(exec_order.get('amount', 0) * 0.01, 2)),
  808. 'port': exec_order['port'],
  809. 'consumeType': 'card' if 'cardNo' in exec_order else 'mobile',
  810. }
  811. if order and order.monthlyPackageId: # 包月卡抵扣
  812. item.pop('cardConsumeMoney', None)
  813. item.pop('cardLeftMoney', None)
  814. item.pop('needTime', None)
  815. # item.pop('usedTime', None)
  816. item.pop('leftTime', None)
  817. if 'cardNo' in exec_order:
  818. item['cardNo'] = exec_order['card_no']
  819. item.update(DeviceType.get_services_button(self.device['devType']['id']))
  820. result_list.append(item)
  821. return result_list
  822. def isHaveStopEvent(self):
  823. return True
  824. def stop_by_order(self, port, orderNo):
  825. data = {'fun_code': 4, 'order_id': orderNo, 'operator': 'user'}
  826. self.send_mqtt(data)
  827. def clear_dev_feecount(self):
  828. data = {'fun_code': 0x09, 'total_coin': True, 'total_card': True}
  829. self.send_mqtt(data)
  830. def reboot_device(self):
  831. # 重启单片机
  832. MessageSender.send(self.device, DeviceCmdCode.SET_DEVINFO, {'reset_mcu': True})
  833. # 重启模块
  834. MessageSender.send(self.device, DeviceCmdCode.SET_DEVINFO, {'restart': True})
  835. @property
  836. def show_pay_unit(self):
  837. """
  838. 前台显示付费的时候,目前有不同的客户希望 显示不同的单位 有的显示金币 有的显示元, 这个地方处理下
  839. :return:
  840. """
  841. if self.device['otherConf'].get('pay_unit'):
  842. return self.device['otherConf'].get('pay_unit')
  843. return u'币'
  844. def get_port_using_detail(self, port, ctrInfo, isLazy=False):
  845. return self.get_port_info(port)
  846. def stop(self, port=None):
  847. return self.stop_charging_port(int(port))
  848. def get_customize_score_unit(self):
  849. return u'元'
  850. def get_customize_package_unit(self):
  851. chrmt = self.device['otherConf'].get('chrmt')
  852. if not chrmt:
  853. chrmt = self.get_dev_setting().get('chrmt')
  854. # 按时间计费
  855. if chrmt == 'TIME' or chrmt == 'POWER':
  856. return u'分钟'
  857. # 按电量计费
  858. elif chrmt == 'ELEC':
  859. return u'度'
  860. return u'元'
  861. def start_customize_point(self,pointNum,openId,port):
  862. unit = self.get_customize_package_unit()
  863. package = {'name':'customizePoint','price':pointNum,'coins':pointNum,'unit':unit,'time':999}
  864. attachParas = {'chargeIndex':port,'onPoints':True}
  865. return self.start_device(package, openId, attachParas)