gaoborui_second.py 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129
  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. from decimal import Decimal
  9. from typing import TYPE_CHECKING, Optional
  10. from apilib.utils_datetime import to_datetime
  11. from apps.web.constant import Const, DeviceCmdCode, MQTT_TIMEOUT
  12. from apps.web.core.adapter.base import SmartBox
  13. from apps.web.core.exceptions import ServiceException
  14. from apps.web.core.networking import MessageSender
  15. from apps.web.device.models import Device, Group
  16. from apps.web.user.models import ConsumeRecord, MyUser, Card
  17. logger = logging.getLogger(__name__)
  18. if TYPE_CHECKING:
  19. pass
  20. # from apps.web.device.models import Device, Group
  21. # from apps.web.user.models import ConsumeRecord, MyUser, Card
  22. class CmdHelper(object):
  23. def __init__(self):
  24. pass
  25. @staticmethod
  26. def encode_str(data, length=2, ratio=1.0, base=16):
  27. # type:(str,int,float,int) -> str
  28. if not isinstance(data, Decimal):
  29. data = Decimal(data)
  30. if not isinstance(length, str):
  31. length = str(length)
  32. if not isinstance(ratio, Decimal):
  33. ratio = Decimal(ratio)
  34. end = 'X' if base == 16 else 'd'
  35. encodeStr = '%.' + length + end
  36. encodeStr = encodeStr % (data * ratio)
  37. return encodeStr
  38. @staticmethod
  39. def decode_str(data, ratio=1, base=16, to_num=True):
  40. # type:(str,Optional[float, int],int, bool) -> Optional[float, str]
  41. """
  42. ratio:比率单位转换
  43. """
  44. if not isinstance(data, str):
  45. data = str(data)
  46. if to_num:
  47. return int(data, base) * ratio
  48. else:
  49. return '%.10g' % (int(data, base) * ratio)
  50. @staticmethod
  51. def reverse_hex(data):
  52. # type:(str) -> str
  53. if not isinstance(data, str):
  54. raise TypeError
  55. return ''.join(list(reversed(re.findall(r'.{2}', data))))
  56. @staticmethod
  57. def split_data_to_list(split_str, data):
  58. # type:(str,str) ->Optional[list, None]
  59. """
  60. return: list
  61. """
  62. part = '({})'
  63. all_ = sum(map(lambda _: int(_) * 2, split_str))
  64. pattern = reduce(lambda a, b: a + b, map(lambda _: part.format('..' * int(_)), split_str))
  65. result = re.match(pattern, data[:all_])
  66. if result:
  67. return result.groups()
  68. @staticmethod
  69. def check_params_range(params, minData=None, maxData=None, desc=''):
  70. # type:(str,float,float,str) -> str
  71. """
  72. 检查参数,返回字符串参数
  73. """
  74. if params is None:
  75. raise ServiceException({'result': 2, 'description': u'参数错误.'})
  76. if not isinstance(params, Decimal):
  77. params = Decimal(params)
  78. if not minData and maxData:
  79. if not isinstance(maxData, Decimal):
  80. maxData = Decimal(maxData)
  81. if params <= maxData:
  82. return '%g' % params
  83. else:
  84. raise ServiceException({'result': 2, 'description': u'%s超出可选范围,可选最大值为%g' % (desc, maxData)})
  85. if not maxData and minData:
  86. if not isinstance(minData, Decimal):
  87. minData = Decimal(minData)
  88. if minData <= params:
  89. return '%g' % params
  90. else:
  91. raise ServiceException({'result': 2, 'description': u'%s超出可选范围,可选最小值为%g' % (desc, minData)})
  92. if not minData and not maxData:
  93. return '%g' % params
  94. else:
  95. if not isinstance(minData, Decimal):
  96. minData = Decimal(minData)
  97. if not isinstance(maxData, Decimal):
  98. maxData = Decimal(maxData)
  99. if minData <= params <= maxData:
  100. return '%g' % params
  101. else:
  102. raise ServiceException(
  103. {'result': 2, 'description': u'%s参数超出可选范围,可取范围为%g-%g' % (desc, minData, maxData)})
  104. def _port_info_01(self, encode=None, decode=None):
  105. """
  106. 查询端口状态
  107. :param encode:
  108. :param decode:
  109. :return:
  110. """
  111. if decode:
  112. data = decode.get('data')
  113. port = self.decode_str(data[10:12])
  114. status = self.decode_str(data[12:14])
  115. left_value = self.decode_str(data[14:18])
  116. power = round(self.decode_str(data[18:22], ratio=0.1), 2)
  117. return {'port': port, 'status': status, 'left_value': left_value, 'power': power, }
  118. else:
  119. port = encode['port']
  120. cmd = '01'
  121. port = self.encode_str(port)
  122. return {'funCode': 52, 'data': cmd + port}
  123. def _start_port_02(self, encode=None, decode=None):
  124. """
  125. 开启端口
  126. :param encode:
  127. :param decode:
  128. :return:
  129. """
  130. if decode:
  131. data = decode.get('data')
  132. port = self.decode_str(data[2:4], )
  133. status = self.decode_str(data[4:6], )
  134. power = self.decode_str(data[6:10], ratio=0.1, )
  135. return {'port': port, 'status': status, 'power': power, }
  136. else:
  137. port, start_type, value = encode['port'], encode['start_type'], encode['value']
  138. cmd = '02'
  139. port = self.encode_str(port)
  140. start_type = self.encode_str(start_type)
  141. value = self.encode_str(value, length=4)
  142. return {'funCode': '5202', 'data': cmd + port + start_type + value}
  143. def _port_stop_03(self, encode=None, decode=None):
  144. """
  145. 停止端口
  146. :param encode:
  147. :param decode:
  148. :return:
  149. """
  150. if decode:
  151. data = decode.get('data')
  152. port = self.decode_str(data[10:12], )
  153. status = self.decode_str(data[12:14], )
  154. used_elec = self.decode_str(data[14:18], ratio=0.01) # 度
  155. left_value = self.decode_str(data[18:22])
  156. power = self.decode_str(data[22:26], ratio=0.1) # 瓦
  157. return {'port': port, 'status': status, 'used_elec': used_elec, 'left_value': left_value, 'power': power}
  158. else:
  159. port = encode['port']
  160. cmd = '03'
  161. port = self.encode_str(port)
  162. return {'funCode': 52, 'data': cmd + port}
  163. def _get_gear_param_04(self, decode=None):
  164. """
  165. 读取分档
  166. :param decode:
  167. :return:
  168. """
  169. if decode:
  170. data = decode.get('data', '')
  171. all_data = self.split_data_to_list('2121212121', data[10:])
  172. power1 = self.decode_str(all_data[0])
  173. power1ratio = self.decode_str(all_data[1])
  174. power2 = self.decode_str(all_data[2])
  175. power2ratio = self.decode_str(all_data[3])
  176. power3 = self.decode_str(all_data[4])
  177. power3ratio = self.decode_str(all_data[5])
  178. power4 = self.decode_str(all_data[6])
  179. power4ratio = self.decode_str(all_data[7])
  180. power5 = self.decode_str(all_data[8])
  181. power5ratio = self.decode_str(all_data[9])
  182. return {
  183. 'power1': power1, 'power1ratio': power1ratio,
  184. 'power2': power2, 'power2ratio': power2ratio,
  185. 'power3': power3, 'power3ratio': power3ratio,
  186. 'power4': power4, 'power4ratio': power4ratio,
  187. 'power5': power5, 'power5ratio': power5ratio,
  188. }
  189. else:
  190. cmd = '04'
  191. return {'funCode': 52, 'data': cmd}
  192. def _set_gear_param_05(self, encode=None, decode=None):
  193. """
  194. 设置分档
  195. :param encode:
  196. :param decode:
  197. :return:
  198. """
  199. if decode:
  200. data = decode.get('data')
  201. status = self.decode_str(data[10:12], )
  202. return {'status': status}
  203. else:
  204. kw = encode.get('gear_param')
  205. cmd = '05'
  206. data = ''
  207. data += self.encode_str(kw.get('power1'), length=4)
  208. data += self.encode_str(kw.get('power1ratio', ))
  209. data += self.encode_str(kw.get('power2'), length=4)
  210. data += self.encode_str(kw.get('power2ratio', ))
  211. data += self.encode_str(kw.get('power3'), length=4)
  212. data += self.encode_str(kw.get('power3ratio', ))
  213. data += self.encode_str(kw.get('power4'), length=4)
  214. data += self.encode_str(kw.get('power4ratio', ))
  215. data += self.encode_str(kw.get('power5'), length=4)
  216. data += self.encode_str(kw.get('power5ratio', ))
  217. return {'funCode': 52, 'data': cmd + data}
  218. def _get_dev_param_06(self, decode=None):
  219. """
  220. 读取设备参数
  221. :param decode:
  222. :return:
  223. """
  224. if decode:
  225. data = decode.get('data', '')
  226. all_data = self.split_data_to_list('22222111111122111114111', data[10:])
  227. port_max_power = self.decode_str(all_data[0])
  228. dev_max_power = self.decode_str(all_data[1])
  229. coin_quote = self.decode_str(all_data[2])
  230. card_quote = self.decode_str(all_data[3])
  231. free_time = self.decode_str(all_data[4])
  232. remove_load_time = self.decode_str(all_data[5])
  233. noload_wait_time = self.decode_str(all_data[6])
  234. free_mode_en = self.decode_str(all_data[7])
  235. refund_mode_en = self.decode_str(all_data[8])
  236. full_stop_en = self.decode_str(all_data[9])
  237. coin_en = self.decode_str(all_data[10])
  238. card_en = self.decode_str(all_data[11])
  239. float_power = self.decode_str(all_data[12])
  240. float_time = self.decode_str(all_data[13])
  241. onec_card_fee = self.decode_str(all_data[14])
  242. volume = self.decode_str(all_data[15])
  243. consumeModule = self.decode_str(all_data[16])
  244. temp_max_limit = self.decode_str(all_data[17])
  245. smork_max_limit = self.decode_str(all_data[18])
  246. password = self.decode_str(all_data[19])
  247. power_report_interval = self.decode_str(all_data[20])
  248. smork_temp_report_interval = self.decode_str(all_data[21])
  249. port_status_interval = self.decode_str(all_data[22])
  250. return {
  251. 'port_max_power': port_max_power,
  252. 'dev_max_power': dev_max_power,
  253. 'coin_quote': coin_quote,
  254. 'card_quote': card_quote,
  255. 'free_time': free_time,
  256. 'remove_load_time': remove_load_time,
  257. 'noload_wait_time': noload_wait_time,
  258. 'free_mode_en': free_mode_en,
  259. 'refund_mode_en': refund_mode_en,
  260. 'full_stop_en': full_stop_en,
  261. 'coin_en': coin_en,
  262. 'card_en': card_en,
  263. 'float_power': float_power,
  264. 'float_time': float_time,
  265. 'onec_card_fee': onec_card_fee,
  266. 'volume': volume,
  267. 'consumeModule': consumeModule,
  268. 'temp_max_limit': temp_max_limit,
  269. 'smork_max_limit': smork_max_limit,
  270. 'password': password,
  271. 'power_report_interval': power_report_interval,
  272. 'smork_temp_report_interval': smork_temp_report_interval,
  273. 'port_status_interval': port_status_interval,
  274. }
  275. else:
  276. cmd = '06'
  277. return {'funCode': 52, 'data': cmd}
  278. def _set_dev_param_07(self, encode=None, decode=None):
  279. """
  280. 设置设备参数
  281. :param encode:
  282. :param decode:
  283. :return:
  284. """
  285. if decode:
  286. data = decode.get('data', '')
  287. status = self.decode_str(data[10:12])
  288. return {'status': status}
  289. else:
  290. kw = encode.get('dev_param')
  291. cmd = '07'
  292. data = ''
  293. data += self.encode_str(kw.get('port_max_power'), length=4)
  294. data += self.encode_str(kw.get('dev_max_power'), length=4)
  295. data += self.encode_str(kw.get('coin_quote'), length=4)
  296. data += self.encode_str(kw.get('card_quote'), length=4)
  297. data += self.encode_str(kw.get('free_time'), length=4)
  298. data += self.encode_str(kw.get('remove_load_time'))
  299. data += self.encode_str(kw.get('noload_wait_time'))
  300. data += self.encode_str(kw.get('free_mode_en'))
  301. data += self.encode_str(kw.get('refund_mode_en'))
  302. data += self.encode_str(kw.get('full_stop_en'))
  303. data += self.encode_str(kw.get('coin_en'))
  304. data += self.encode_str(kw.get('card_en'))
  305. data += self.encode_str(kw.get('float_power'), length=4)
  306. data += self.encode_str(kw.get('float_time'), length=4)
  307. data += self.encode_str(kw.get('onec_card_fee'))
  308. data += self.encode_str(kw.get('volume'))
  309. data += self.encode_str(kw.get('consumeModule'))
  310. data += self.encode_str(kw.get('temp_max_limit'))
  311. data += self.encode_str(kw.get('smork_max_limit'))
  312. data += self.encode_str(kw.get('password'), length=8)
  313. data += self.encode_str(kw.get('power_report_interval'))
  314. data += self.encode_str(kw.get('smork_temp_report_interval'))
  315. data += self.encode_str(kw.get('port_status_interval'))
  316. return {'funCode': 52, 'data': cmd + data}
  317. def _get_card_param_08(self, decode=None):
  318. """
  319. 读取读卡参数
  320. :param decode:
  321. :return:
  322. """
  323. if decode:
  324. data = decode.get('data', '')
  325. cardPassword = self.decode_str(data[10:22])
  326. sector = self.decode_str(data[22:24])
  327. return {'cardPassword': cardPassword, 'sector': sector}
  328. else:
  329. cmd = '08'
  330. return {'funCode': 52, 'data': cmd}
  331. def _set_card_param_09(self, encode=None, decode=None):
  332. """
  333. 设置读卡参数
  334. :param encode:
  335. :param decode:
  336. :return:
  337. """
  338. if decode:
  339. data = decode.get('data', '')
  340. status = self.decode_str(data[10:12])
  341. return {'status': status}
  342. else:
  343. kw = encode.get('card_param')
  344. cmd = '09'
  345. data = ''
  346. data += self.encode_str(kw.get('cardPassword'), length=12)
  347. data += self.encode_str(kw.get('sector'))
  348. return {'funCode': 52, 'data': cmd + data}
  349. def _set_port_enable_0A(self, encode=None, decode=None):
  350. """
  351. 设置端口禁用/启用
  352. :param encode:
  353. :param decode:
  354. :return:
  355. """
  356. if decode:
  357. data = decode.get('data', '')
  358. status = self.decode_str(data[10:12])
  359. return {'status': status}
  360. else:
  361. port, enable = encode['port'], encode['enable']
  362. cmd = '0A'
  363. data = ''
  364. data += self.encode_str(port)
  365. if enable == True:
  366. data += '01'
  367. else:
  368. data += '00'
  369. return {'funCode': 52, 'data': cmd + data}
  370. def _set_dev_power_status_0B(self, encode=None, decode=None):
  371. """
  372. 设置设备电源状态 开机/待机
  373. :param encode:
  374. :param decode:
  375. :return:
  376. """
  377. if decode:
  378. data = decode.get('data', '')
  379. status = self.decode_str(data[10:12])
  380. return {'status': status}
  381. else:
  382. power_on = encode['power_on']
  383. cmd = '0B'
  384. data = ''
  385. if power_on == True:
  386. data += '01'
  387. else:
  388. data += '00'
  389. return {'funCode': 52, 'data': cmd + data}
  390. def _get_dev_consume_total_0C(self, decode=None):
  391. """
  392. 获取设备投币/刷卡总量
  393. :param decode:
  394. :return:
  395. """
  396. if decode:
  397. data = decode.get('data', '')
  398. coin_total = round(self.decode_str(data[10:14]))
  399. card_total = round(self.decode_str(data[14:18], ratio=0.1), 1)
  400. return {'coin_total': coin_total, 'card_total': card_total}
  401. else:
  402. cmd = '0C'
  403. return {'funCode': 52, 'data': cmd}
  404. def _set_dev_fualt_recovery_0D(self, decode=None):
  405. """
  406. 设备故障状态清除 未做
  407. :param decode:
  408. :return:
  409. """
  410. if decode:
  411. data = decode.get('data', '')
  412. status = self.decode_str(data[2:4])
  413. return {'status': status}
  414. else:
  415. cmd = '0D'
  416. return {'funCode': 52, 'data': cmd}
  417. def _get_dev_version_0E(self, decode=None):
  418. """
  419. 获取设备版本信息
  420. :param decode:
  421. :return:
  422. """
  423. if decode:
  424. data = decode.get('data', '')
  425. data = data[8:]
  426. hw_ver_len = int(data[2:4], 16)
  427. hw_ver = data[4: 4 + hw_ver_len * 2]
  428. soft_ver_len = int(data[4 + hw_ver_len * 2: 6 + hw_ver_len * 2], 16)
  429. soft_ver = data[6 + hw_ver_len * 2: 6 + soft_ver_len * 2]
  430. return {'hw_ver': hw_ver, 'soft_ver': soft_ver}
  431. else:
  432. cmd = '0E'
  433. return {'funCode': 52, 'data': cmd}
  434. def _get_dev_all_status_0F(self, decode=None):
  435. """
  436. 获取设备全部状态
  437. :param decode:
  438. :return:
  439. """
  440. if decode:
  441. data = decode.get('data', '')
  442. powers = map(lambda _: self.decode_str(_, ratio=1.0), self.split_data_to_list('2' * 10, data[10:50]))
  443. left_values = map(lambda _: self.decode_str(_), self.split_data_to_list('2' * 10, data[50:90]))
  444. result = {}
  445. for i in xrange(len(powers)):
  446. result['{}'.format(i + 1)] = {
  447. 'port': i + 1,
  448. 'power': powers[i],
  449. 'left_value': left_values[i],
  450. 'status': Const.DEV_WORK_STATUS_IDLE if left_values[i] == 0 and powers[
  451. i] == 0 else Const.DEV_WORK_STATUS_WORKING
  452. }
  453. return result
  454. else:
  455. cmd = '0F'
  456. return {'funCode': 52, 'data': cmd}
  457. class GaoBoRuiChargingBox(SmartBox, CmdHelper):
  458. def __init__(self, device):
  459. super(GaoBoRuiChargingBox, self).__init__(device)
  460. self.ctrInfo = Device.get_dev_control_cache(device.devNo)
  461. self.consumeMode = self.device['otherConf'].get('consumeMode')
  462. def check_dev_status(self, attachParas=None):
  463. if attachParas is None:
  464. raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路、电池类型信息'})
  465. if not attachParas.has_key('chargeIndex'):
  466. raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路'})
  467. port = attachParas['chargeIndex']
  468. openId = attachParas['openId']
  469. result = self.send_mqtt(data={'funCode': '88', 'data': port})
  470. if 'uid' in result:
  471. if openId != result['uid']:
  472. raise ServiceException({'result': 2, 'description': u'当前端口繁忙'})
  473. def get_dev_info(self):
  474. return self._get_dev_version_0E(decode=self.send_mqtt(self._get_dev_version_0E()))
  475. def test(self, coins):
  476. raise NotImplementedError(u'设备未实现 `test`')
  477. def start_device(self, package, openId, attachParas):
  478. raise NotImplementedError('cannot call `start_device` of base class SmartBox')
  479. def start(self, packageId, openId=None, attachParas=None):
  480. if not attachParas:
  481. attachParas = {}
  482. washConfig = self.device.get('washConfig', dict())
  483. package = washConfig.get(packageId)
  484. if not package:
  485. raise ServiceException({'result': 2, 'description': u'未找到套餐,请联系经销商!'}) # new
  486. return self.start_device(package, openId, attachParas)
  487. def stop(self, port=None):
  488. encode = {'port': port}
  489. return self._port_stop_03(decode=self.send_mqtt(self._port_stop_03(encode=encode)))
  490. def calc_stop_back_coins(self, totalFee, remainderTime, totalTime):
  491. refundFee = round(totalFee * (float(remainderTime) / totalTime), 2)
  492. if refundFee > totalFee:
  493. refundFee = totalFee
  494. if refundFee <= 0.0:
  495. refundFee = 0.00
  496. return refundFee
  497. def stop_charging_port(self, port):
  498. return self.send_mqtt({'port': int(port), 'funCode': 'stopPort'})
  499. # 访问设备,获取设备信息
  500. def getDevInfo(self):
  501. pass
  502. def analyze_event_data(self, data):
  503. cmdCode = data[2:4]
  504. if cmdCode == '53' or cmdCode == '4C':
  505. subCmd = data[8:10]
  506. result = {'cmdCode': cmdCode, 'subCmd': subCmd}
  507. if subCmd == '36': # 在线卡请求余额
  508. result['session'] = data[6:8]
  509. result['card_no'] = data[10:18]
  510. result['fee'] = data[18:20]
  511. elif subCmd == '35': # 离线卡上报开始
  512. result['session'] = data[6:8]
  513. result['port'] = int(data[10:12], 16)
  514. result['card_no'] = data[12:20]
  515. result['fee'] = round(int(data[20:22], 16) * 0.1)
  516. result['balance'] = round(int(data[22:26], 16) * 0.1) # 元
  517. if data[26:28] == '01':
  518. result['billingType'] = 'time'
  519. result['needTime'] = int(data[28:32], 16)
  520. else:
  521. result['billingType'] = 'elec'
  522. result['needElec'] = round(int(data[28:32], 16) * 0.01, 2)
  523. result['power'] = round(int(data[32:36], 16) * 0.1, 2)
  524. elif subCmd == '3B':
  525. result['port'] = int(data[10:12], 16)
  526. result['card_no'] = data[12:20]
  527. result['fee'] = round(int(data[20:22], 16) * 0.1, 2)
  528. result['balance'] = round(int(data[22:26], 16) * 0.1, 2)
  529. elif subCmd == '32':
  530. upload = data[10:-2]
  531. points = re.findall('..' * 3, upload)
  532. powers = {}
  533. for item in points:
  534. port = self.decode_str(item[:2], to_num=False)
  535. power = int(self.decode_str(item[2:], ratio=0.1))
  536. powers[port] = power
  537. result['powers'] = powers
  538. elif subCmd == '3E':
  539. result = {}
  540. decode = {'data': data[10:60]}
  541. gear_params = self._get_gear_param_04(decode=decode)
  542. result.update(gear_params)
  543. decode = {'data': data[60:]}
  544. dev_params = self._get_dev_param_06(decode=decode)
  545. result.update(dev_params)
  546. return result
  547. elif subCmd == '3F':
  548. FAULT_CODE = {
  549. '1': '键盘故障',
  550. '2': '显示器故障',
  551. '3': '刷卡板故障',
  552. '4': '子网故障',
  553. '5': '存储故障',
  554. '6': '过温故障',
  555. '7': '烟雾故障',
  556. '8': '漏电',
  557. '9': '短路',
  558. 'A': '过压',
  559. 'B': '欠压',
  560. 'C': '整机过流',
  561. 'D': '恶意操作',
  562. 'E': '继电器粘连',
  563. 'F': '其它故障',
  564. }
  565. errCode = data[12:14][-1]
  566. statusInfo = FAULT_CODE.get(errCode)
  567. if errCode == '6': # 过温(1 字节)
  568. temp = int(data[14:16], 16)
  569. statusInfo = statusInfo + '(过温,当前{}度)'.format(temp)
  570. result.update({'statusInfo': statusInfo, 'FaultCode': errCode,
  571. 'uart': data})
  572. elif errCode == 'A': # 过压(2 字节)
  573. overVoltage = int(data[14:16], 16)
  574. statusInfo = statusInfo + '(过压,当前{}V)'.format(overVoltage)
  575. result.update({'statusInfo': statusInfo,
  576. 'FaultCode': errCode,
  577. 'uart': data})
  578. elif errCode == 'B': # 欠压(1 字节)
  579. overVoltage = int(data[14:16], 16)
  580. statusInfo = statusInfo + '(欠压,当前{}V)'.format(overVoltage)
  581. result.update({'statusInfo': statusInfo,
  582. 'FaultCode': errCode,
  583. 'uart': data})
  584. elif errCode == 'C': # 过流(2 字节)
  585. overCurrent = int(data[14:16], 16)
  586. statusInfo = statusInfo + '(过流,当前{}A)'.format(overCurrent)
  587. result.update({'statusInfo': statusInfo, 'cmdCode': cmdCode,
  588. 'FaultCode': errCode,
  589. 'uart': data})
  590. else: # 通道号(1字节)
  591. port = int(data[14:16], 16)
  592. if 0 < port < 11:
  593. result.update({'statusInfo': statusInfo, 'cmdCode': cmdCode, 'FaultCode': errCode,
  594. 'uart': data})
  595. else:
  596. result.update({'statusInfo': statusInfo, 'cmdCode': cmdCode, 'port': port, 'FaultCode': errCode,
  597. 'uart': data})
  598. return result
  599. def active_deactive_port(self, port, active):
  600. if active == False:
  601. self.stop_charging_port(port)
  602. def lock_unlock_port(self, port, lock=True):
  603. encode = {'port': port, 'enable': not lock}
  604. result = self._set_port_enable_0A(decode=self.send_mqtt(self._set_port_enable_0A(encode=encode)))
  605. if result.get('status') == 1:
  606. pass
  607. else:
  608. raise ServiceException({'result': 2, 'description': '设备端口( {} ) {}失败'.format(port, '禁用' if lock else '启用')})
  609. # 端口是否可用,如果canAdd为True,表示可以追加钱
  610. def is_port_can_use(self, port, canAdd=False):
  611. port = str(port)
  612. try:
  613. portDict = self.get_port_status()
  614. except ServiceException, e:
  615. return False, e.result.get('description')
  616. except Exception, e:
  617. logger.error('error = %s' % e)
  618. return False, u'获取端口状态失败'
  619. if port in portDict:
  620. if portDict[port]['status'] == Const.DEV_WORK_STATUS_IDLE:
  621. return True, ''
  622. elif portDict[port]['status'] == Const.DEV_WORK_STATUS_FAULT:
  623. return False, u'该线路故障,暂时不能使用,请您使用其他线路'
  624. elif portDict[port]['status'] == Const.DEV_WORK_STATUS_WORKING:
  625. if canAdd:
  626. return True, ''
  627. return False, u'该线路正在工作,暂时不能继续使用,请您使用其他线路,或者等待该线路工作完毕'
  628. elif portDict[port]['status'] == Const.DEV_WORK_STATUS_FORBIDDEN:
  629. return False, u'该线路已被禁止使用,请您使用其他线路'
  630. elif portDict[port]['status'] == Const.DEV_WORK_STATUS_CONNECTED:
  631. return True, u''
  632. else:
  633. return False, u'线路未知状态,暂时不能使用'
  634. return False, u'未知端口,无法使用'
  635. def get_port_status(self, force=False):
  636. if force:
  637. self._get_dev_all_status_0F(decode=self.send_mqtt(self._get_dev_all_status_0F()))
  638. result = {}
  639. for k,v in self.ctrInfo.items():
  640. if k.isdigit():
  641. result[k] = v
  642. return result
  643. def dealer_get_port_status(self):
  644. """
  645. 远程上分的时候获取端口状态
  646. :return:
  647. """
  648. return self.get_port_status()
  649. def get_dev_setting(self):
  650. result = {}
  651. params = self._get_dev_param_06(decode=self.send_mqtt(self._get_dev_param_06()))
  652. result.update(params)
  653. gear_params = self._get_gear_param_04(decode=self.send_mqtt(self._get_gear_param_04()))
  654. result.update(gear_params)
  655. card_params = self._get_card_param_08(decode=self.send_mqtt(self._get_card_param_08()))
  656. result.update(card_params)
  657. consumeTotal = self._get_dev_consume_total_0C(decode=self.send_mqtt(self._get_dev_consume_total_0C()))
  658. result.update(consumeTotal)
  659. devVersion = self._get_dev_version_0E(decode=self.send_mqtt(self._get_dev_version_0E()))
  660. result.update(devVersion)
  661. disableDevice = self.device['otherConf'].get('disableDevice', False)
  662. result.update({'disableDevice': disableDevice})
  663. refundProtectionTime = self.device.get('otherConf', {}).get('refundProtectionTime', 5)
  664. result.update({'refundProtectionTime': refundProtectionTime})
  665. return result
  666. def set_device_function(self, request, lastSetConf):
  667. if 'disable' in request.POST:
  668. result = self._set_dev_param_07(decode=self.send_mqtt(self._set_dev_param_07()))
  669. if result.get('status') == 1:
  670. pass
  671. else:
  672. raise ServiceException({'result': 2, 'description': '设备设置失败'})
  673. def set_device_function_param(self, request, lastSetConf):
  674. def check_update(name_list):
  675. updata_dict = {}
  676. update = False
  677. for _ in name_list:
  678. if request.POST.get(_) != lastSetConf.get(_):
  679. update = True
  680. updata_dict[_] = request.POST.get(_)
  681. else:
  682. updata_dict[_] = lastSetConf.get(_)
  683. return update, updata_dict
  684. gear_param_name = ['power1', 'power1ratio', 'power2', 'power2ratio', 'power3', 'power3ratio', 'power4',
  685. 'power4ratio', 'power5', 'power5ratio', ]
  686. update, gear_param = check_update(gear_param_name)
  687. if update:
  688. encode = {'gear_param': gear_param}
  689. result = self._set_gear_param_05(decode=self.send_mqtt(self._set_gear_param_05(encode=encode)))
  690. if result.get('status') == 1:
  691. pass
  692. else:
  693. raise ServiceException({'result': 2, 'description': '设备分档参数设置失败'})
  694. card_param_name = ['cardPassword', 'sector', ]
  695. update, gear_param = check_update(card_param_name)
  696. if update:
  697. encode = {'card_param': gear_param}
  698. result = self._set_card_param_09(decode=self.send_mqtt(self._set_card_param_09(encode=encode)))
  699. if result.get('status') == 1:
  700. pass
  701. else:
  702. raise ServiceException({'result': 2, 'description': '卡参数设置失败'})
  703. dev_param_name = ['port_max_power', 'dev_max_power', 'coin_quote', 'card_quote', 'free_time',
  704. 'remove_load_time', 'noload_wait_time', 'free_mode_en', 'refund_mode_en', 'full_stop_en',
  705. 'coin_en', 'card_en', 'float_power', 'float_time', 'onec_card_fee', 'volume', 'consumeModule',
  706. 'temp_max_limit', 'smork_max_limit', 'password', 'power_report_interval',
  707. 'smork_temp_report_interval', 'port_status_interval', ]
  708. update, dev_param = check_update(dev_param_name)
  709. if update:
  710. encode = {'dev_param': dev_param}
  711. result = self._set_dev_param_07(decode=self.send_mqtt(self._set_dev_param_07(encode=encode)))
  712. if result.get('status') == 1:
  713. pass
  714. else:
  715. raise ServiceException({'result': 2, 'description': '设备设备参数设置失败'})
  716. if 'disableDevice' in request.POST:
  717. disableDevice = self.device['otherConf'].get('disableDevice', False)
  718. if disableDevice == request.POST['disableDevice']:
  719. pass
  720. else:
  721. disableDevice = request.POST['disableDevice']
  722. self.set_dev_disable(disableDevice)
  723. if 'refundProtectionTime' in request.POST:
  724. refundProtectionTime = self.device['otherConf'].get('refundProtectionTime', False)
  725. if refundProtectionTime == request.POST['refundProtectionTime']:
  726. pass
  727. else:
  728. refundProtectionTime = request.POST['refundProtectionTime']
  729. Device.get_collection().update_one({'devNo': self._device['devNo']}, {
  730. '$set': {'otherConf.refundProtectionTime': refundProtectionTime}})
  731. Device.invalid_device_cache(self.device.devNo)
  732. def set_dev_disable(self, disable):
  733. """
  734. 设备端锁定解锁设备
  735. :param disable:
  736. :return:
  737. """
  738. if disable:
  739. encode = {'power_on': False}
  740. else:
  741. encode = {'power_on': True}
  742. result = self._set_dev_power_status_0B(decode=self.send_mqtt(self._set_dev_power_status_0B(encode=encode)))
  743. if result.get('status') == 1:
  744. Device.get_collection().update_one({'devNo': self._device['devNo']}, {
  745. '$set': {'otherConf.disableDevice': disable}})
  746. Device.invalid_device_cache(self.device.devNo)
  747. else:
  748. raise ServiceException({'result': 2, 'description': '设备待机/开机设置失败'})
  749. def get_port_static_info(self, portDict):
  750. allPorts, usedPorts = 0, 0
  751. for k, v in portDict.items():
  752. if k.isdigit():
  753. allPorts += 1
  754. if ('isStart' in v and v['isStart']) or ('status' in v and v['status'] != Const.DEV_WORK_STATUS_IDLE):
  755. usedPorts += 1
  756. return allPorts, usedPorts, allPorts - usedPorts
  757. def get_port_status_from_dev(self):
  758. data = self._get_dev_all_status_0F()
  759. result = self.send_mqtt(data)
  760. result = self._get_dev_all_status_0F(decode=result)
  761. for strPort, info in result.items():
  762. if strPort in self.ctrInfo:
  763. self.ctrInfo[strPort].update({'status': info['status']})
  764. else:
  765. self.ctrInfo[strPort] = info
  766. allPorts, usedPorts, usePorts = self.get_port_static_info(self.ctrInfo)
  767. self.ctrInfo.update({'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  768. Device.update_dev_control_cache(self._device['devNo'], self.ctrInfo)
  769. return result
  770. def get_port_info(self, port):
  771. encode = {'port': port}
  772. result = self._port_info_01(decode=self.send_mqtt(self._port_info_01(encode=encode)))
  773. return result
  774. def async_update_portinfo_from_dev(self):
  775. class Sender(threading.Thread):
  776. def __init__(self, smartBox):
  777. super(Sender, self).__init__()
  778. self._smartBox = smartBox
  779. def run(self):
  780. try:
  781. self._smartBox.get_port_status_from_dev()
  782. except Exception as e:
  783. logger.info('get port stats from dev,e=%s' % e)
  784. sender = Sender(self)
  785. sender.start()
  786. def get_port_using_detail(self, port, ctrInfo, isLazy=False):
  787. """
  788. 获取设备端口的详细信息
  789. :param port:
  790. :param ctrInfo:
  791. :return:
  792. """
  793. detailDict = ctrInfo.get(str(port), {})
  794. try:
  795. portInfo = self.get_port_info(str(port))
  796. # 有的主机报的信息leftTime错误
  797. if portInfo.has_key('leftTime') and portInfo['leftTime'] < 0:
  798. portInfo.pop('leftTime')
  799. detailDict.update(portInfo)
  800. except Exception, e:
  801. logger.exception('get port info from dev=%s err=%s' % (self.device.devNo, e))
  802. return detailDict
  803. portData = {}
  804. startTimeStr = detailDict.get('startTime', None)
  805. if startTimeStr is not None and 'usedTime' not in detailDict:
  806. startTime = to_datetime(startTimeStr)
  807. usedTime = int(round((datetime.datetime.now() - startTime).total_seconds() / 60.0))
  808. portData['usedTime'] = usedTime
  809. elif detailDict.get('usedTime'):
  810. usedTime = detailDict.get('usedTime')
  811. portData['usedTime'] = usedTime
  812. else:
  813. usedTime = None
  814. if 'cType' in detailDict:
  815. if detailDict['cType'] == 1:
  816. detailDict['leftTime'] = detailDict.get('left_value')
  817. else:
  818. detailDict['leftElec'] = round(detailDict.get('left_value') * 0.01, 2)
  819. if detailDict.has_key('leftTime') and (usedTime > 0):
  820. if detailDict['leftTime'] == 65535:
  821. portData['leftTime'] = 65535
  822. detailDict['usedTime'] = 0
  823. detailDict['actualNeedTime'] = 0
  824. else:
  825. portData['actualNeedTime'] = int(detailDict['leftTime']) + int(usedTime)
  826. if detailDict.has_key('needTime') and portData['actualNeedTime'] > detailDict['needTime']:
  827. portData['actualNeedTime'] = detailDict['needTime']
  828. portData['leftTime'] = detailDict['leftTime']
  829. if detailDict.has_key('coins'):
  830. if self.device.is_auto_refund:
  831. portData['leftMoney'] = round(
  832. float(detailDict['coins']) * int(detailDict['leftTime']) / (
  833. int(detailDict['leftTime']) + int(usedTime)), 2)
  834. portData['consumeMoney'] = round(
  835. float(detailDict['coins']) * int(portData['usedTime']) / (
  836. int(detailDict['leftTime']) + usedTime), 2)
  837. elif detailDict.has_key('leftTime'):
  838. portData['leftTime'] = detailDict['leftTime']
  839. if (not detailDict.has_key('leftTime')) and (usedTime is not None):
  840. if detailDict.has_key('needTime'):
  841. portData['leftTime'] = detailDict['needTime'] - usedTime
  842. if detailDict.has_key('coins') and float(detailDict['coins']) != 0: # 只有支持退费的设备才显示可退费数据
  843. if self.device.is_auto_refund:
  844. portData['leftMoney'] = round(
  845. float(detailDict['coins']) * portData['leftTime'] / detailDict['needTime'], 2)
  846. portData['consumeMoney'] = round(
  847. float(detailDict['coins']) * portData['usedTime'] / detailDict['needTime'], 2)
  848. if detailDict.has_key('openId'):
  849. user = MyUser.objects(openId=detailDict['openId']).first()
  850. if user:
  851. portData['nickName'] = user.nickname
  852. if detailDict.has_key('cardId'):
  853. if not detailDict.has_key('consumeType'):
  854. portData['consumeType'] = 'card'
  855. card = Card.objects.get(id=detailDict['cardId'])
  856. if card.cardName:
  857. portData['cardName'] = card.cardName
  858. portData['cardNo'] = card.cardNo
  859. # 注意,如果是IC卡,不支持余额回收,这里也不要显示出来
  860. if card.cardType == 'IC' and portData.has_key('leftMoney'):
  861. portData.pop('leftMoney')
  862. elif detailDict.has_key('openId') and (not detailDict.has_key('consumeType')):
  863. if detailDict.get('vCardId'):
  864. portData['consumeType'] = 'mobile_vcard'
  865. else:
  866. portData['consumeType'] = 'mobile'
  867. elif detailDict.has_key('consumeType') and detailDict['consumeType'] == 'coin':
  868. portData['consumeType'] = 'coin' # 硬币的都无法退费
  869. if portData.has_key('leftMoney'):
  870. portData.pop('leftMoney')
  871. # 做个特殊处理
  872. if portData.has_key('needTime'):
  873. if portData['needTime'] == '999' or portData['needTime'] == '充满自停':
  874. portData['needTime'] = u'充满自停'
  875. else:
  876. portData['needTime'] = u'%s分钟' % portData['needTime']
  877. # 如果剩余时间为65535,表示未接插头
  878. if portData.has_key('leftTime') and portData['leftTime'] == 65535:
  879. portData['leftTime'] = u'(线路空载)'
  880. portData['usedTime'] = 0
  881. portData['needTime'] = 0
  882. detailDict.update(portData)
  883. for k, v in detailDict.items():
  884. if v < 0:
  885. detailDict.pop(k)
  886. # 因为前台显示的开始时间如果带年,就显示不下,这里做个切割
  887. if detailDict.has_key('startTime') and detailDict['startTime'].count('-') == 2:
  888. detailDict['startTime'] = to_datetime(detailDict['startTime']).strftime('%m-%d %H:%M:%S')
  889. return detailDict
  890. @property
  891. def isHaveStopEvent(self):
  892. return True
  893. def get_many_port_info(self, portList):
  894. return None
  895. def start_device_realiable(self, order):
  896. # type:(ConsumeRecord)->dict
  897. if order.orderNo[:10] == self.device.ownerId[-10:]: # 此时为远程上分 先停一次
  898. self.stop_charging_port(order.used_port)
  899. attachParas = order.attachParas
  900. package = order.package
  901. unit = package.get('unit')
  902. if unit == '分钟':
  903. cType = 1
  904. cValue = int(package.get('time'))
  905. elif unit == '度':
  906. cType = 2
  907. cValue = int(package.get('time') * 100)
  908. else:
  909. raise ServiceException({'result': 2, 'description': '设备单位配置错误'})
  910. port = int(attachParas['chargeIndex'])
  911. data = {
  912. 'funCode': 'startPort',
  913. 'port': port,
  914. 'cType': cType, # 1 按时间 2 按电量
  915. 'cValue': cValue,
  916. 'order_id': order.orderNo,
  917. 'uid': order.openId
  918. }
  919. # 订单中记录一份下发收到源数据
  920. uart_source = []
  921. uart_source.append(
  922. {'write_start': json.dumps(data, indent=4), 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
  923. result = MessageSender.send(device=self.device,
  924. cmd=DeviceCmdCode.OPERATE_DEV_SYNC,
  925. payload=data,
  926. timeout=120)
  927. if result['rst'] == 9:
  928. raise ServiceException({'result': 2, 'description': '启动失败,当前端口已被其他人使用'})
  929. uart_source.append(
  930. {'rece_start': json.dumps(result, indent=4), 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
  931. order.update(uart_source=uart_source)
  932. return result
  933. def calc_elec_fee(self, spend_elec):
  934. group = Group.objects.get(id=self.device['groupId'])
  935. return float(group.otherConf.get('elecFee', 0)) * spend_elec
  936. @property
  937. def support_monthly_package(self):
  938. return False
  939. def stop_by_order(self, port, orderNo):
  940. return self.stop_charging_port(port)
  941. def isHaveFaultHandle(self):
  942. return True
  943. def faultHandle(self, **kw):
  944. self._set_dev_fualt_recovery_0D(decode=self.send_mqtt(self._set_dev_fualt_recovery_0D(), timeout=5))
  945. def send_mqtt(self, data=None, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, timeout=MQTT_TIMEOUT.NORMAL):
  946. """
  947. 发送mqtt 指令默认210 返回data
  948. """
  949. if 'cmd' not in data: data.update({'cmd': cmd})
  950. if 'IMEI' not in data:
  951. data.update({'IMEI': self.device.devNo})
  952. result = MessageSender.send(self.device, cmd, data, timeout=timeout)
  953. if 'rst' in result and result['rst'] != 0:
  954. if result['rst'] == -1:
  955. raise ServiceException(
  956. {'result': 2, 'description': u'该设备正在玩命找网络,请您稍候再试', 'rst': -1})
  957. elif result['rst'] == 1:
  958. raise ServiceException(
  959. {'result': 2, 'description': u'该设备忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能', 'rst': 1})
  960. elif result['rst'] == 8:
  961. raise ServiceException(
  962. {'result': 2, 'description': u'停止指令串口通讯已完成,设备未响应', 'rst': 1})
  963. else:
  964. if cmd in [DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE]:
  965. return
  966. return result