jinque.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import json
  5. import logging
  6. import re
  7. import threading
  8. import time
  9. from decimal import Decimal
  10. from mongoengine import Q
  11. from typing import TYPE_CHECKING, Optional
  12. from apilib.monetary import RMB
  13. from apilib.utils_datetime import to_datetime
  14. from apps.web.constant import Const, DeviceCmdCode, MQTT_TIMEOUT
  15. from apps.web.core.adapter.base import SmartBox
  16. from apps.web.core.exceptions import ServiceException
  17. from apps.web.core.networking import MessageSender
  18. from apps.web.device.models import Device, Group
  19. from apps.web.user.models import ConsumeRecord, MyUser, Card
  20. logger = logging.getLogger(__name__)
  21. if TYPE_CHECKING:
  22. pass
  23. # from apps.web.device.models import Device, Group
  24. # from apps.web.user.models import ConsumeRecord, MyUser, Card
  25. class CmdHelper(object):
  26. def __init__(self):
  27. pass
  28. @staticmethod
  29. def encode_str(data, length=2, ratio=1.0, base=16):
  30. # type:(str,int,float,int) -> str
  31. if not isinstance(data, Decimal):
  32. data = Decimal(data)
  33. if not isinstance(length, str):
  34. length = str(length)
  35. if not isinstance(ratio, Decimal):
  36. ratio = Decimal(ratio)
  37. end = 'X' if base == 16 else 'd'
  38. encodeStr = '%.' + length + end
  39. encodeStr = encodeStr % (data * ratio)
  40. return encodeStr
  41. @staticmethod
  42. def decode_str(data, ratio=1, base=16, to_num=True):
  43. # type:(str,Optional[float, int],int, bool) -> Optional[float, str]
  44. """
  45. ratio:比率单位转换
  46. """
  47. if not isinstance(data, str):
  48. data = str(data)
  49. if to_num:
  50. return int(data, base) * ratio
  51. else:
  52. return '%.10g' % (int(data, base) * ratio)
  53. @staticmethod
  54. def reverse_hex(data):
  55. # type:(str) -> str
  56. if not isinstance(data, str):
  57. raise TypeError
  58. return ''.join(list(reversed(re.findall(r'.{2}', data))))
  59. @staticmethod
  60. def split_data_to_list(split_str, data):
  61. # type:(str,str) ->Optional[list, None]
  62. """
  63. return: list
  64. """
  65. part = '({})'
  66. all_ = sum(map(lambda _: int(_) * 2, split_str))
  67. pattern = reduce(lambda a, b: a + b, map(lambda _: part.format('..' * int(_)), split_str))
  68. result = re.match(pattern, data[:all_])
  69. if result:
  70. return result.groups()
  71. @staticmethod
  72. def check_params_range(params, minData=None, maxData=None, desc=''):
  73. # type:(str,float,float,str) -> str
  74. """
  75. 检查参数,返回字符串参数
  76. """
  77. if params is None:
  78. raise ServiceException({'result': 2, 'description': u'参数错误.'})
  79. if not isinstance(params, Decimal):
  80. params = Decimal(params)
  81. if not minData and maxData:
  82. if not isinstance(maxData, Decimal):
  83. maxData = Decimal(maxData)
  84. if params <= maxData:
  85. return '%g' % params
  86. else:
  87. raise ServiceException({'result': 2, 'description': u'%s超出可选范围,可选最大值为%g' % (desc, maxData)})
  88. if not maxData and minData:
  89. if not isinstance(minData, Decimal):
  90. minData = Decimal(minData)
  91. if minData <= params:
  92. return '%g' % params
  93. else:
  94. raise ServiceException({'result': 2, 'description': u'%s超出可选范围,可选最小值为%g' % (desc, minData)})
  95. if not minData and not maxData:
  96. return '%g' % params
  97. else:
  98. if not isinstance(minData, Decimal):
  99. minData = Decimal(minData)
  100. if not isinstance(maxData, Decimal):
  101. maxData = Decimal(maxData)
  102. if minData <= params <= maxData:
  103. return '%g' % params
  104. else:
  105. raise ServiceException(
  106. {'result': 2, 'description': u'%s参数超出可选范围,可取范围为%g-%g' % (desc, minData, maxData)})
  107. class JinQue(SmartBox, CmdHelper):
  108. def __init__(self, device):
  109. super(JinQue, self).__init__(device)
  110. self.ctrInfo = Device.get_dev_control_cache(device.devNo)
  111. for key in self.ctrInfo.keys():
  112. if key.isdigit():
  113. if int(key) > self.port_num:
  114. self.ctrInfo.pop(key, None)
  115. allPorts, usedPorts, usePorts = self.get_port_static_info(self.ctrInfo)
  116. self.ctrInfo.update({'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  117. self.consumeMode = self.device['otherConf'].get('consumeMode')
  118. @property
  119. def port_num(self):
  120. return self.device.devTypeFeatures.get('portNum', 1)
  121. def check_dev_status(self, attachParas=None):
  122. port = int(attachParas['chargeIndex'])
  123. result = self.get_port_status(True)
  124. _info = result.get(str(port), {})
  125. if _info.get('status') != Const.DEV_WORK_STATUS_IDLE:
  126. raise ServiceException({'result': 2, 'description': u'检测到该充电枪已被他人使用, 若现场充电枪未被使用, 请松开急停按钮后重新扫码启动'})
  127. def get_dev_info(self):
  128. """
  129. 获取版本号码
  130. """
  131. payload = self.send_mqtt(data={'funCode': 'get', 'sCmd': 2502})
  132. data = payload['data'][-8:]
  133. data = self.reverse_hex(data)
  134. version = 'V' + '{}.{}'.format(data[0:2], data[2:4])
  135. return {'version': version}
  136. def test(self, coins):
  137. raise NotImplementedError(u'设备未实现 `test`')
  138. def stop(self, port=None):
  139. data = {
  140. 'funCode': 'stop',
  141. 'port': port
  142. }
  143. return self.send_mqtt(data)
  144. def calc_stop_back_coins(self, totalFee, remainderTime, totalTime):
  145. refundFee = round(totalFee * (float(remainderTime) / totalTime), 2)
  146. if refundFee > totalFee:
  147. refundFee = totalFee
  148. if refundFee <= 0.0:
  149. refundFee = 0.00
  150. return refundFee
  151. def stop_charging_port(self, port):
  152. data = {
  153. 'funCode': 'stop',
  154. 'port': port,
  155. 'rule': 'user'
  156. }
  157. return self.send_mqtt(data)
  158. # 访问设备,获取设备信息
  159. def getDevInfo(self):
  160. pass
  161. def analyze_event_data(self, data):
  162. return {}
  163. def active_deactive_port(self, port, active):
  164. if active == False:
  165. self.send_mqtt({
  166. 'funCode': 'stop',
  167. 'port': port,
  168. 'rule': 'admin'
  169. })
  170. def lock_unlock_port(self, port, lock=True):
  171. raise ServiceException({'result': 2, 'description': u'此设备不支持直接禁用、解禁端口'})
  172. def get_port_status(self, force=False):
  173. if force:
  174. self.get_port_status_from_dev()
  175. result = {}
  176. for k, v in self.ctrInfo.items():
  177. if k.isdigit():
  178. result[k] = v
  179. return result
  180. def dealer_get_port_status(self):
  181. """
  182. 远程上分的时候获取端口状态
  183. :return:
  184. """
  185. return self.get_port_status()
  186. def get_dev_setting(self):
  187. result = {}
  188. try:
  189. ver = self.send_mqtt({'funCode': 'ver'}, timeout=5).get('ver')
  190. result.update({'ver': ver})
  191. except:
  192. pass
  193. try:
  194. elec_fee = round(self.send_mqtt({'funCode': 'elec_fee'}, timeout=5).get('elec_fee', 0) * 0.01, 2)
  195. result.update({'elecFee': elec_fee})
  196. except:
  197. pass
  198. try:
  199. total_elec = round(self.send_mqtt({'funCode': 'total_elec'}, timeout=5).get('total_elec', 0) * 0.01, 2)
  200. result.update({'totalElec': total_elec})
  201. except:
  202. pass
  203. onceIdcardFee = self.device.otherConf.get('onceIdcardFee', 5)
  204. result.update({'onceIdcardFee': onceIdcardFee})
  205. return result
  206. def set_device_function(self, request, lastSetConf):
  207. if 'init' in request.POST:
  208. self.send_mqtt({'funCode': 'init'})
  209. elif 'reset' in request.POST:
  210. self.send_mqtt({'funCode': 'reset'})
  211. else:
  212. raise ServiceException({'result': 2, 'description': '设备设置失败'})
  213. def set_device_function_param(self, request, lastSetConf):
  214. if 'elecFee' in request.POST:
  215. self.send_mqtt(
  216. {'funCode': 'set', 'sCmd': 1505, 'value': int(float(request.POST.get('elecFee') or 0) * 100)},
  217. timeout=5)
  218. if 'onceIdcardFee' in request.POST:
  219. onceIdcardFee = round(float(request.POST.get('onceIdcardFee') or 0), 2)
  220. Device.get_collection().update_one({'devNo': self._device['devNo']}, {
  221. '$set': {'otherConf.onceIdcardFee': onceIdcardFee}})
  222. Device.invalid_device_cache(self.device.devNo)
  223. def set_dev_disable(self, disable):
  224. """
  225. 设备端锁定解锁设备
  226. :param disable:
  227. :return:
  228. """
  229. Device.get_collection().update_one({'devNo': self._device['devNo']}, {
  230. '$set': {'otherConf.disableDevice': disable}})
  231. Device.invalid_device_cache(self.device.devNo)
  232. def get_port_static_info(self, portDict):
  233. allPorts, usedPorts = 0, 0
  234. for k, v in portDict.items():
  235. if k.isdigit():
  236. allPorts += 1
  237. if ('isStart' in v and v['isStart']) or ('status' in v and v['status'] != Const.DEV_WORK_STATUS_IDLE):
  238. usedPorts += 1
  239. return allPorts, usedPorts, allPorts - usedPorts
  240. def get_port_status_from_dev(self):
  241. STATUS_MAP = {
  242. 'idle': 0,
  243. 'busy': 1
  244. }
  245. result = self.send_mqtt({
  246. 'funCode': 'status'
  247. })
  248. status = result.get('status', {})
  249. for strPort, status in status.items():
  250. if int(strPort) > self.port_num:
  251. self.ctrInfo.pop(strPort, None)
  252. continue
  253. if strPort in self.ctrInfo:
  254. self.ctrInfo[strPort].update({'status': STATUS_MAP.get(status, 0)})
  255. else:
  256. self.ctrInfo[strPort] = {'status': status}
  257. allPorts, usedPorts, usePorts = self.get_port_static_info(self.ctrInfo)
  258. self.ctrInfo.update({'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  259. Device.update_dev_control_cache(self._device['devNo'], self.ctrInfo)
  260. return result
  261. def get_port_info(self, port):
  262. resp = self.send_mqtt({'funCode': 'info', 'port': int(port)}, timeout=10)
  263. order_id = resp.get('order_id')
  264. result = {}
  265. if 'power' in resp:
  266. result['power'] = round(resp['power'], 1)
  267. if not order_id:
  268. return result
  269. order = ConsumeRecord.objects.filter(Q(orderNo=order_id) | Q(startKey=order_id)).first()
  270. result.update({'openId': order.openId, 'nickName': order.nickname, 'coins': str(order.coin)})
  271. if 'duration' in resp:
  272. result['usedTime'] = round(resp['duration'] / 60.0, 1)
  273. if 'cardNo' in resp:
  274. result['cardNo'] = resp['cardNo']
  275. if 'amount' in resp and 'left' in resp:
  276. amount = resp['amount']
  277. left = min(resp['left'], amount)
  278. result['leftMoney'] = '{}元'.format(round(float(order.coin) * left / (1.0 * amount), 2))
  279. result['consumeMoney'] = '{}元'.format(round(float(order.coin) * (amount - left) / (1.0 * amount), 2))
  280. return result
  281. def get_many_port_info(self, portList):
  282. data = self.send_mqtt({'funCode': 'orders'}, timeout=10)
  283. orders = data.get('orders')
  284. result = {}
  285. for _ in range(1, self.port_num+1, 1):
  286. resp = orders.get(str(_))
  287. order_id = resp.get('order_id')
  288. item = {'index': str(_)}
  289. if 'power' in resp:
  290. item['power'] = round(resp['power'], 1)
  291. if not order_id:
  292. continue
  293. item['status'] = Const.DEV_WORK_STATUS_WORKING
  294. order = ConsumeRecord.objects.filter(Q(orderNo=order_id) | Q(startKey=order_id)).first()
  295. item.update({'openId': order.openId, 'nickName': order.nickname, 'coins': str(order.coin),})
  296. if 'duration' in resp:
  297. item['usedTime'] = round(resp['duration'] / 60.0, 1)
  298. if 'cardNo' in resp:
  299. item['cardNo'] = resp['cardNo']
  300. if 'amount' in resp and 'left' in resp:
  301. amount = resp['amount']
  302. left = min(resp['left'], amount)
  303. item['leftMoney'] = '{}元'.format(round(float(order.coin) * left / (1.0 * amount), 2))
  304. item['consumeMoney'] = '{}元'.format(round(float(order.coin) * (amount - left) / (1.0 * amount), 2))
  305. result[str(_)] = item
  306. return dict(filter(lambda items: items[0] in portList, result.items()))
  307. def async_update_portinfo_from_dev(self):
  308. class Sender(threading.Thread):
  309. def __init__(self, smartBox):
  310. super(Sender, self).__init__()
  311. self._smartBox = smartBox
  312. def run(self):
  313. try:
  314. self._smartBox.get_port_status_from_dev()
  315. except Exception as e:
  316. logger.info('get port stats from dev,e=%s' % e)
  317. sender = Sender(self)
  318. sender.start()
  319. def get_port_using_detail(self, port, ctrInfo, isLazy=False):
  320. """
  321. 获取设备端口的详细信息
  322. :param port:
  323. :param ctrInfo:
  324. :return:
  325. """
  326. detailDict = ctrInfo.get(str(port), {})
  327. try:
  328. portInfo = self.get_port_info(str(port))
  329. # 有的主机报的信息leftTime错误
  330. if portInfo.has_key('leftTime') and portInfo['leftTime'] < 0:
  331. portInfo.pop('leftTime')
  332. detailDict.update(portInfo)
  333. except Exception as e:
  334. logger.exception('get port info from dev=%s err=%s' % (self.device.devNo, e))
  335. return detailDict
  336. portData = {}
  337. startTimeStr = detailDict.get('startTime', None)
  338. if startTimeStr is not None and 'usedTime' not in detailDict:
  339. startTime = to_datetime(startTimeStr)
  340. usedTime = int(round((datetime.datetime.now() - startTime).total_seconds() / 60.0))
  341. portData['usedTime'] = usedTime
  342. elif detailDict.get('usedTime'):
  343. usedTime = detailDict.get('usedTime')
  344. portData['usedTime'] = usedTime
  345. else:
  346. usedTime = None
  347. if 'cType' in detailDict:
  348. if detailDict['cType'] == 1:
  349. detailDict['leftTime'] = detailDict.get('left_value')
  350. else:
  351. detailDict['leftElec'] = round(detailDict.get('left_value') * 0.01, 2)
  352. if detailDict.has_key('leftTime') and (usedTime > 0):
  353. if detailDict['leftTime'] == 65535:
  354. portData['leftTime'] = 65535
  355. detailDict['usedTime'] = 0
  356. detailDict['actualNeedTime'] = 0
  357. else:
  358. portData['actualNeedTime'] = int(detailDict['leftTime']) + int(usedTime)
  359. if detailDict.has_key('needTime') and portData['actualNeedTime'] > detailDict['needTime']:
  360. portData['actualNeedTime'] = detailDict['needTime']
  361. portData['leftTime'] = detailDict['leftTime']
  362. if detailDict.has_key('coins'):
  363. if self.device.is_auto_refund:
  364. portData['leftMoney'] = round(
  365. float(detailDict['coins']) * int(detailDict['leftTime']) / (
  366. int(detailDict['leftTime']) + int(usedTime)), 2)
  367. portData['consumeMoney'] = round(
  368. float(detailDict['coins']) * int(portData['usedTime']) / (
  369. int(detailDict['leftTime']) + usedTime), 2)
  370. elif detailDict.has_key('leftTime'):
  371. portData['leftTime'] = detailDict['leftTime']
  372. if (not detailDict.has_key('leftTime')) and (usedTime is not None):
  373. if detailDict.has_key('needTime'):
  374. portData['leftTime'] = detailDict['needTime'] - usedTime
  375. if detailDict.has_key('coins') and float(detailDict['coins']) != 0: # 只有支持退费的设备才显示可退费数据
  376. if self.device.is_auto_refund:
  377. portData['leftMoney'] = round(
  378. float(detailDict['coins']) * portData['leftTime'] / detailDict['needTime'], 2)
  379. portData['consumeMoney'] = round(
  380. float(detailDict['coins']) * portData['usedTime'] / detailDict['needTime'], 2)
  381. if detailDict.has_key('openId'):
  382. user = MyUser.objects(openId=detailDict['openId']).first()
  383. if user:
  384. portData['nickName'] = user.nickname
  385. if detailDict.has_key('cardId'):
  386. if not detailDict.has_key('consumeType'):
  387. portData['consumeType'] = 'card'
  388. card = Card.objects.get(id=detailDict['cardId'])
  389. if card.cardName:
  390. portData['cardName'] = card.cardName
  391. portData['cardNo'] = card.cardNo
  392. # 注意,如果是IC卡,不支持余额回收,这里也不要显示出来
  393. if card.cardType == 'IC' and portData.has_key('leftMoney'):
  394. portData.pop('leftMoney')
  395. elif detailDict.has_key('openId') and (not detailDict.has_key('consumeType')):
  396. if detailDict.get('vCardId'):
  397. portData['consumeType'] = 'mobile_vcard'
  398. else:
  399. portData['consumeType'] = 'mobile'
  400. elif detailDict.has_key('consumeType') and detailDict['consumeType'] == 'coin':
  401. portData['consumeType'] = 'coin' # 硬币的都无法退费
  402. if portData.has_key('leftMoney'):
  403. portData.pop('leftMoney')
  404. # 做个特殊处理
  405. if portData.has_key('needTime'):
  406. if portData['needTime'] == '999' or portData['needTime'] == '充满自停':
  407. portData['needTime'] = u'充满自停'
  408. else:
  409. portData['needTime'] = u'%s分钟' % portData['needTime']
  410. # 如果剩余时间为65535,表示未接插头
  411. if portData.has_key('leftTime') and portData['leftTime'] == 65535:
  412. portData['leftTime'] = u'(线路空载)'
  413. portData['usedTime'] = 0
  414. portData['needTime'] = 0
  415. detailDict.update(portData)
  416. for k, v in detailDict.items():
  417. if v < 0:
  418. detailDict.pop(k)
  419. # 因为前台显示的开始时间如果带年,就显示不下,这里做个切割
  420. if detailDict.has_key('startTime') and detailDict['startTime'].count('-') == 2:
  421. detailDict['startTime'] = to_datetime(detailDict['startTime']).strftime('%m-%d %H:%M:%S')
  422. return detailDict
  423. @property
  424. def isHaveStopEvent(self):
  425. return True
  426. def start_device_realiable(self, order):
  427. # type:(ConsumeRecord)->dict
  428. if order.orderNo[:10] == self.device.ownerId[-10:]: # 此时为远程上分 先停一次
  429. self.stop_charging_port(order.used_port)
  430. # 等待串口相应
  431. time.sleep(1)
  432. attachParas = order.attachParas
  433. package = order.package
  434. price = package['price']
  435. port = int(attachParas['chargeIndex'])
  436. data = {
  437. 'funCode': 'start',
  438. 'order_id': order.orderNo,
  439. 'amount': int(price * 100),
  440. 'session_id': int(time.time() % 255),
  441. 'port': port,
  442. 'attach_paras': {},
  443. }
  444. # 订单中记录一份下发收到源数据
  445. uart_source = []
  446. uart_source.append(
  447. {'write_start': json.dumps(data, indent=4), 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
  448. result = MessageSender.send(device=self.device,
  449. cmd=DeviceCmdCode.OPERATE_DEV_SYNC,
  450. payload=data,
  451. timeout=120)
  452. if result['rst'] == 1:
  453. raise ServiceException({'result': 2, 'description': '设备串口故障或设备处于急停模式'})
  454. elif result['rst'] == 2:
  455. raise ServiceException({'result': 2, 'description': '设备拒绝启动(否认应答)'})
  456. elif result['rst'] == 3:
  457. raise ServiceException({'result': 2, 'description': '充电枪端口号缺失'})
  458. elif result['rst'] == 4:
  459. raise ServiceException({'result': 2, 'description': '充电枪端口正在工作中'})
  460. uart_source.append(
  461. {'rece_start': json.dumps(result, indent=4), 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
  462. # 兼容后台启动
  463. try:
  464. order.update(uart_source=uart_source, servicedInfo={'chargeIndex': port, })
  465. except:
  466. pass
  467. return result
  468. def calc_elec_fee(self, spend_elec):
  469. group = Group.objects.get(id=self.device['groupId'])
  470. return float(group.otherConf.get('elecFee', 0)) * spend_elec
  471. @property
  472. def support_monthly_package(self):
  473. return False
  474. def stop_by_order(self, port, orderNo):
  475. return self.stop_charging_port(port)
  476. def isHaveFaultHandle(self):
  477. return True
  478. def faultHandle(self, **kw):
  479. pass
  480. def send_mqtt(self, data=None, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, timeout=MQTT_TIMEOUT.NORMAL):
  481. """
  482. 发送mqtt 指令默认210 返回data
  483. """
  484. if 'cmd' not in data:
  485. data.update({'cmd': cmd})
  486. if 'IMEI' not in data:
  487. data.update({'IMEI': self.device.devNo})
  488. result = MessageSender.send(self.device, cmd, data, timeout=timeout)
  489. if 'rst' in result and result['rst'] != 0:
  490. if result['rst'] == -1:
  491. raise ServiceException(
  492. {'result': 2, 'description': u'该设备正在玩命找网络,请您稍候再试', 'rst': -1})
  493. elif result['rst'] == 1:
  494. raise ServiceException(
  495. {'result': 2, 'description': u'该设备忙,无响应,请您稍候再试。也可能是急停按钮被按下, 请重开重试', 'rst': 1})
  496. else:
  497. if cmd in [DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE]:
  498. return
  499. return result
  500. def support_device_package(self):
  501. return True
  502. def format_device_package(self, isTemp=False, **kw):
  503. def __generate_id(ids):
  504. i = 1
  505. while True:
  506. if str(i) in ids:
  507. i += 1
  508. else:
  509. return str(i)
  510. def __formart_ruleList():
  511. packageList = kw['serviceData']
  512. ids = [str(rule['id']) for rule in packageList if 'id' in rule]
  513. washConfig = {}
  514. for i, rule in enumerate(packageList):
  515. ruleId = str(rule.get('id', ''))
  516. if not ruleId:
  517. ruleId = __generate_id(ids)
  518. ids.append(ruleId)
  519. washConfig[ruleId] = {}
  520. if 'switch' in rule:
  521. washConfig[ruleId].update({'switch': rule['switch']})
  522. if 'billingMethod' in rule:
  523. washConfig[ruleId].update({'billingMethod': rule['billingMethod']})
  524. if 'price' in rule:
  525. washConfig[ruleId].update({'price': round(float(rule['price']), 2) or 0, 'coins': round(float(rule['price']), 2) or 0})
  526. # if 'time' in rule:
  527. # washConfig[ruleId].update({'time': rule['time'] or 0})
  528. if 'name' in rule:
  529. washConfig[ruleId].update({'name': rule['name'] or '套餐{}'.format(i + 1)})
  530. if 'unit' in rule:
  531. washConfig[ruleId].update({'unit': rule['unit']})
  532. if 'sn' in rule:
  533. washConfig[ruleId].update({'sn': rule['sn']})
  534. if 'autoStop' in rule:
  535. washConfig[ruleId]['autoStop'] = rule['autoStop']
  536. if 'autoRefund' in rule:
  537. washConfig[ruleId]['autoRefund'] = rule['autoRefund']
  538. if 'minAfterStartCoins' in rule:
  539. washConfig[ruleId]['minAfterStartCoins'] = rule['minAfterStartCoins']
  540. if 'minFee' in rule:
  541. washConfig[ruleId]['minFee'] = rule['minFee']
  542. if isTemp:
  543. pass
  544. # 检验部分
  545. if RMB(rule.get('price') or 0) > self.device.owner.maxPackagePrice:
  546. raise ServiceException(
  547. {'result': 0, 'description': '套餐( {} )金额超限'.format(rule['name']), 'payload': {}})
  548. return washConfig
  549. def __formart_displaySwitchs():
  550. return {'displayCoinsSwitch': False,
  551. 'displayTimeSwitch': False,
  552. 'displayPriceSwitch': True,
  553. 'setPulseAble': False,
  554. 'setBasePriceAble': False}
  555. washConfig = __formart_ruleList()
  556. displaySwitchs = __formart_displaySwitchs()
  557. return washConfig, displaySwitchs
  558. def dealer_show_package(self, isTemp=False, **kw):
  559. def get_rule_list():
  560. if isTemp:
  561. config = self.device.get('tempWashConfig', {})
  562. else:
  563. config = self.device['washConfig']
  564. ruleList = []
  565. for packageId, rule in config.items():
  566. item = {
  567. 'id': packageId
  568. }
  569. if 'switch' in rule:
  570. item['switch'] = rule['switch']
  571. if 'name' in rule:
  572. item['name'] = rule['name']
  573. if 'coins' in rule:
  574. item['coins'] = rule['coins']
  575. if 'price' in rule:
  576. item['price'] = rule['price']
  577. if 'time' in rule:
  578. item['time'] = rule['time']
  579. if 'description' in rule:
  580. item['description'] = rule['description']
  581. if 'unit' in rule:
  582. item['unit'] = rule['unit']
  583. if 'imgList' in rule:
  584. item['imgList'] = rule['imgList']
  585. if 'sn' in rule:
  586. item['sn'] = rule['sn']
  587. if 'autoStop' in rule:
  588. item['autoStop'] = rule['autoStop']
  589. if 'minAfterStartCoins' in rule:
  590. item['minAfterStartCoins'] = rule['minAfterStartCoins']
  591. if 'minFee' in rule:
  592. item['minFee'] = rule['minFee']
  593. ruleList.append(item)
  594. return sorted(ruleList, key=lambda x: (x.get('sn'), x.get('id')))
  595. def get_display_switchs():
  596. if "displaySwitchs" in self.device["otherConf"]:
  597. displaySwitchs = self.device["otherConf"].get('displaySwitchs')
  598. else:
  599. displaySwitchs = {'displayCoinsSwitch': False,
  600. 'displayTimeSwitch': False,
  601. 'displayPriceSwitch': True,
  602. "setPulseAble": False,
  603. "setBasePriceAble": False}
  604. return displaySwitchs
  605. ruleList = get_rule_list()
  606. displaySwitchs = get_display_switchs()
  607. return {"ruleList": ruleList, 'displaySwitchs': displaySwitchs}