jndz_policy.py 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import logging
  5. import re
  6. from decimal import Decimal
  7. from typing import TYPE_CHECKING, Optional
  8. from apps.web.common.proxy import ClientConsumeModelProxy
  9. from apps.web.constant import Const, DeviceCmdCode, CONSUMETYPE
  10. from apps.web.core.adapter.base import SmartBox
  11. from apps.web.core.adapter.policy_common import PolicyCommon
  12. from apps.web.core.device_define.jndz import CMD_CODE
  13. from apps.web.core.exceptions import ServiceException
  14. from apps.web.core.networking import MessageSender
  15. from apps.web.dealer.models import Dealer
  16. from apps.web.device.models import Device, Group
  17. from taskmanager.mediator import task_caller
  18. logger = logging.getLogger(__name__)
  19. if TYPE_CHECKING:
  20. pass
  21. # from apps.web.device.models import Device, Group
  22. # from apps.web.user.models import ConsumeRecord, MyUser, Card
  23. class CmdHelper(object):
  24. @staticmethod
  25. def encode_str(data, length=2, ratio=1.0, base=16):
  26. # type:(str,int,float,int) -> str
  27. if not isinstance(data, Decimal):
  28. data = Decimal(data)
  29. if not isinstance(length, str):
  30. length = str(length)
  31. if not isinstance(ratio, Decimal):
  32. ratio = Decimal(ratio)
  33. end = 'X' if base == 16 else 'd'
  34. encodeStr = '%.' + length + end
  35. encodeStr = encodeStr % (data * ratio)
  36. return encodeStr
  37. @staticmethod
  38. def decode_str(data, ratio=1, base=16, to_num=True):
  39. # type:(str,Optional[float, int],int, bool) -> Optional[float, str]
  40. """
  41. ratio:比率单位转换
  42. """
  43. if not isinstance(data, str):
  44. data = str(data)
  45. if to_num:
  46. return int(data, base) * ratio
  47. else:
  48. return '%.10g' % (int(data, base) * ratio)
  49. @staticmethod
  50. def reverse_hex(data):
  51. # type:(str) -> str
  52. if not isinstance(data, str):
  53. raise TypeError
  54. return ''.join(list(reversed(re.findall(r'.{2}', data))))
  55. @staticmethod
  56. def split_data_to_list(split_str, data):
  57. # type:(str,str) ->Optional[list, None]
  58. """
  59. return: list
  60. """
  61. part = '({})'
  62. all_ = sum(map(lambda _: int(_) * 2, split_str))
  63. pattern = reduce(lambda a, b: a + b, map(lambda _: part.format('..' * int(_)), split_str))
  64. result = re.match(pattern, data[:all_])
  65. if result:
  66. return result.groups()
  67. def decode_long_hex_to_list(self, data, split=2, ratio=1.0, base=16):
  68. # type:(str,int,float,int) -> list
  69. """
  70. return: list
  71. """
  72. if len(data) % split != 0:
  73. raise Exception('Invalid data')
  74. pattern = r'.{%s}' % split
  75. hex_list = re.findall(pattern, data)
  76. hex_list = map(lambda x: self.decode_str(x, ratio=ratio, base=base), hex_list)
  77. return hex_list
  78. @staticmethod
  79. def check_params_range(params, minData=None, maxData=None, desc=''):
  80. # type:(str,float,float,str) -> str
  81. """
  82. 检查参数,返回字符串参数
  83. """
  84. if params is None:
  85. raise ServiceException({'result': 2, 'description': u'参数错误.'})
  86. if not isinstance(params, Decimal):
  87. params = Decimal(params)
  88. if not minData and maxData:
  89. if not isinstance(maxData, Decimal):
  90. maxData = Decimal(maxData)
  91. if params <= maxData:
  92. return '%g' % params
  93. else:
  94. raise ServiceException({'result': 2, 'description': u'%s超出可选范围,可选最大值为%g' % (desc, maxData)})
  95. if not maxData and minData:
  96. if not isinstance(minData, Decimal):
  97. minData = Decimal(minData)
  98. if minData <= params:
  99. return '%g' % params
  100. else:
  101. raise ServiceException({'result': 2, 'description': u'%s超出可选范围,可选最小值为%g' % (desc, minData)})
  102. if not minData and not maxData:
  103. return '%g' % params
  104. else:
  105. if not isinstance(minData, Decimal):
  106. minData = Decimal(minData)
  107. if not isinstance(maxData, Decimal):
  108. maxData = Decimal(maxData)
  109. if minData <= params <= maxData:
  110. return '%g' % params
  111. else:
  112. raise ServiceException(
  113. {'result': 2, 'description': u'%s参数超出可选范围,可取范围为%g-%g' % (desc, minData, maxData)})
  114. class JNDZPOLICYBox(PolicyCommon, CmdHelper):
  115. def __init__(self, device):
  116. super(JNDZPOLICYBox, self).__init__(device)
  117. def disable_app_device(self, switch=True):
  118. # type:(bool) -> None
  119. otherConf = self.device.get('otherConf', {})
  120. otherConf['disableDevice'] = switch
  121. Device.objects.filter(devNo=self.device['devNo']).update(otherConf=otherConf)
  122. Device.invalid_device_cache(self.device['devNo'])
  123. @property
  124. def device_configs(self):
  125. dev = Device.get_dev(self.device.devNo)
  126. deviceConfigs = dev.get('otherConf', {}).get('deviceConfigs', {})
  127. return deviceConfigs
  128. @property
  129. def server_configs(self):
  130. dev = Device.get_dev(self.device.devNo)
  131. serverConfigs = dev.get('otherConf', {}).get('serverConfigs', {})
  132. return serverConfigs
  133. def analyze_event_data(self, device_event):
  134. # 升级兼容. 避免重启过程中老的消息
  135. if 'data' in device_event:
  136. data = device_event['data']
  137. else:
  138. data = device_event
  139. cmdCode = data[4:6]
  140. # 4个故障的告警
  141. if cmdCode == CMD_CODE.DEVICE_FAULT_FIRE:
  142. return {"cmdCode": cmdCode, "fault": u"火灾报警"}
  143. elif cmdCode == CMD_CODE.DEVICE_FAULT_SMOKE:
  144. return {"cmdCode": cmdCode, "fault": "烟雾报警"}
  145. elif cmdCode == CMD_CODE.DEVICE_FAULT_TEMPERATURE:
  146. return {"cmdCode": cmdCode, "fault": "温度超限告警"}
  147. elif cmdCode == CMD_CODE.DEVICE_FAULT_POWER:
  148. return {"cmdCode": cmdCode, "fault": "功率超线告警"}
  149. elif cmdCode == CMD_CODE.DEVICE_FAULT_ALTER:
  150. def getBin8Str(binData):
  151. bin8Str = str(bin(binData))[2:]
  152. while len(bin8Str) < 8:
  153. bin8Str = '0' + bin8Str
  154. return bin8Str
  155. fire = int(data[8:10], 16)
  156. smoke = int(data[10:12], 16)
  157. overload = int(data[12:14], 16)
  158. overheat = int(data[14:16], 16)
  159. relayAdhesion = data[16:24]
  160. relayAdhesionDetail = (getBin8Str(int(relayAdhesion[0:2], 16)) + getBin8Str(
  161. int(relayAdhesion[2:4], 16)) + getBin8Str(int(relayAdhesion[4:6], 16)) + getBin8Str(
  162. int(relayAdhesion[6:8], 16)))[::-1]
  163. portRelayAdhesion = {}
  164. for _ in range(0, len(relayAdhesionDetail)):
  165. if int(relayAdhesionDetail[_]):
  166. portRelayAdhesion.update({str(_ + 1): int(relayAdhesionDetail[_])})
  167. result = {"cmdCode": cmdCode, "fire": bool(fire), "smoke": bool(smoke), "overload": bool(overload),
  168. "overheat": bool(overheat), "portRelayAdhesion": portRelayAdhesion}
  169. dealer = Dealer.objects(id=self.device['ownerId']).first()
  170. if dealer is None:
  171. return {}
  172. if 'supportAdhesionAlert' not in dealer.features:
  173. result.pop('portRelayAdhesion')
  174. fault = []
  175. desc = []
  176. if result.get('fire', False):
  177. fault.append("火灾报警")
  178. result["errorCode"] = "B202"
  179. if result.get('smoke', False):
  180. fault.append("烟雾报警")
  181. result["errorCode"] = "B202"
  182. if result.get('overload', False):
  183. fault.append("功率超限告警")
  184. result["errorCode"] = "B203"
  185. if result.get('overheat', False):
  186. fault.append("温度超限告警")
  187. result["errorCode"] = "B201"
  188. if result.get('portRelayAdhesion', {}):
  189. fault.append("继电器粘连告警")
  190. result["errorCode"] = "B204"
  191. for _ in portRelayAdhesion.keys():
  192. desc.append("{}号继电器粘连告警 ".format(_))
  193. if desc:
  194. desc = '--'.join(desc)
  195. result.update({"desc": desc})
  196. if fault:
  197. fault = '--'.join(fault)
  198. result.update({"fault": fault})
  199. else:
  200. result = {}
  201. return result
  202. # 2个状态量上报
  203. elif cmdCode == CMD_CODE.DEVICE_FAULT_POWER:
  204. maxPower = int(data[8:12], 16)
  205. return {"cmdCode": cmdCode, "maxPower": maxPower}
  206. elif cmdCode == CMD_CODE.DEVICE_FAULT_TEMPERATURE:
  207. maxTemperature = int(data[8:10], 16)
  208. return {"cmdCode": cmdCode, "maxTemperature": maxTemperature}
  209. elif cmdCode == CMD_CODE.DEVICE_ELEC:
  210. elec = int(data[8:12], 16)
  211. return {"cmdCode": cmdCode, "elec": elec}
  212. elif cmdCode == CMD_CODE.DEVICE_TEMPERATURE:
  213. temperature = int(data[10:12], 16)
  214. if data[8:10] == "00":
  215. temperature = temperature * -1
  216. return {"cmdCode": cmdCode, "temperature": temperature}
  217. elif cmdCode == CMD_CODE.DEVICE_REAL_TIME_REPORT_21:
  218. _result = data[6: 8]
  219. port_num = int(data[8:10], 16)
  220. port_info_urat_info = data[10:170]
  221. port_status_desc = {
  222. "01": "端口空闲",
  223. "02": "端口正在使用",
  224. "03": "端口禁用",
  225. "04": "端口故障",
  226. }
  227. port_info = {}
  228. for index in xrange(0, 16 * port_num, 16):
  229. item = port_info_urat_info[index: index + 16]
  230. one_port = {}
  231. one_port["port"] = int(item[:2], 16)
  232. one_port["portStatus"] = item[2:4]
  233. one_port["portStatusDesc"] = port_status_desc.get(item[2:4])
  234. one_port["leftTime"] = int(item[4:8], 16)
  235. one_port["power"] = int(item[8:12], 16)
  236. one_port["elec"] = int(item[12:16], 16) * 0.01
  237. port_info[str(one_port["port"])] = one_port
  238. return {"cmdCode": cmdCode, "result": _result, "sourceData": data, "portNum": port_num,
  239. "portInfo": port_info}
  240. else:
  241. logger.info("receive data <{}>, cmd is invalid".format(data))
  242. def get_default_port_nums(self):
  243. data = self.send_mqtt({'funCode': '01', 'data': '00'})
  244. portTotalNum = self.decode_str(data[8:10])
  245. theFirst = self.decode_str(data[10:12])
  246. return portTotalNum, theFirst
  247. def get_idle_port(self):
  248. data = self.send_mqtt({'funCode': '02', 'data': '00'})
  249. idlePortNum = self.decode_str(data[8:10])
  250. idlePortList = self.decode_long_hex_to_list(data[10:-2])
  251. return {'idlePortNum': idlePortNum, 'idlePortList': idlePortList}
  252. def lock_unlock_port(self, port, lock=True):
  253. lockStr = '00' if lock else '01'
  254. portStr = self.encode_str(port)
  255. data = portStr + lockStr
  256. data = self.send_mqtt({'funCode': '0C', 'data': data})
  257. if data[6:8] == '01': # 表示成功
  258. pass
  259. else:
  260. raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'})
  261. if lock:
  262. Device.update_dev_control_cache(self._device['devNo'],
  263. {str(port): {'status': Const.DEV_WORK_STATUS_FORBIDDEN}})
  264. else:
  265. Device.update_dev_control_cache(self._device['devNo'], {str(port): {'status': Const.DEV_WORK_STATUS_IDLE}})
  266. def active_deactive_port(self, port, active):
  267. if not active:
  268. self.stop_charging_port(port)
  269. devInfo = Device.get_dev_control_cache(self._device['devNo'])
  270. portCtrInfo = devInfo.get(str(port), {})
  271. portCtrInfo.update({'isStart': False, 'status': Const.DEV_WORK_STATUS_IDLE,
  272. 'endTime': datetime.datetime.now().strftime(Const.DATETIME_FMT)})
  273. newValue = {str(port): portCtrInfo}
  274. Device.update_dev_control_cache(self._device['devNo'], newValue)
  275. else:
  276. raise ServiceException({'result': 2, 'description': u'此设备不支持直接打开端口'})
  277. def get_port_status(self, force=False):
  278. if force:
  279. return self.get_port_status_from_dev()
  280. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  281. statusDict = {}
  282. if not ctrInfo.has_key('allPorts'):
  283. self.get_port_status_from_dev()
  284. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  285. allPorts = ctrInfo.get('allPorts', 10)
  286. # allPorts 有的时候会为0, 这个时候强制设置为10
  287. if allPorts == 0:
  288. allPorts = 10
  289. for ii in range(allPorts):
  290. tempDict = ctrInfo.get(str(ii + 1), {})
  291. if tempDict.has_key('status'):
  292. statusDict[str(ii + 1)] = {'status': tempDict.get('status')}
  293. elif tempDict.has_key('isStart'):
  294. if tempDict['isStart']:
  295. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_WORKING}
  296. else:
  297. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  298. else:
  299. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  300. allPorts, usedPorts, usePorts = self.get_port_static_info(statusDict)
  301. Device.update_dev_control_cache(self._device['devNo'],
  302. {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  303. return statusDict
  304. def get_port_status_from_dev(self):
  305. data = self.send_mqtt({'funCode': '0F', 'data': '00'})
  306. data = data[6:]
  307. if data[0:2] == '01': # 表示成功
  308. pass
  309. else:
  310. raise ServiceException({'result': 2, 'description': u'获取统计数据失败,请重试看能否解决'})
  311. result = {}
  312. portNum = int(data[2:4], 16)
  313. portData = data[4::]
  314. ii = 0
  315. while ii < portNum:
  316. port = int(portData[ii * 4:ii * 4 + 2], 16)
  317. statusTemp = portData[ii * 4 + 2:ii * 4 + 4]
  318. if statusTemp == '01':
  319. status = {'status': Const.DEV_WORK_STATUS_IDLE}
  320. elif statusTemp == '02':
  321. status = {'status': Const.DEV_WORK_STATUS_WORKING}
  322. elif statusTemp == '03':
  323. status = {'status': Const.DEV_WORK_STATUS_FORBIDDEN}
  324. elif statusTemp == '04':
  325. status = {'status': Const.DEV_WORK_STATUS_FAULT}
  326. else:
  327. status = {'status': Const.DEV_WORK_STATUS_FAULT}
  328. ii += 1
  329. result[str(port)] = status
  330. allPorts, usedPorts, usePorts = self.get_port_static_info(result)
  331. Device.update_dev_control_cache(self._device['devNo'],
  332. {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  333. # 这里存在多线程更新缓存的场景,可能性比较低,但是必须先取出来,然后逐个更新状态,然后再记录缓存
  334. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  335. for strPort, info in result.items():
  336. if ctrInfo.has_key(strPort):
  337. ctrInfo[strPort].update({'status': info['status']})
  338. else:
  339. ctrInfo[strPort] = info
  340. Device.update_dev_control_cache(self._device['devNo'], ctrInfo)
  341. return result
  342. def test(self, coins, port=1, time='60', elec='0'):
  343. data = self.encode_str(str(port))
  344. data += self.encode_str(coins)
  345. data += self.encode_str(time, length=4)
  346. data += self.encode_str(elec, length=4, ratio=100)
  347. devInfo = self.send_mqtt({'funCode': '14', 'data': data})
  348. return devInfo
  349. def get_dev_setting(self):
  350. """
  351. 获取设备配置参数
  352. :return:
  353. """
  354. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  355. {'IMEI': self._device['devNo'], "funCode": '1E', 'data': '00'})
  356. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  357. if devInfo['rst'] == -1:
  358. raise ServiceException(
  359. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  360. elif devInfo['rst'] == 1:
  361. raise ServiceException(
  362. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  363. data = devInfo['data'][6::]
  364. if data[0:2] == '01': # 表示成功
  365. pass
  366. else:
  367. raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'})
  368. confData = data[2:-2]
  369. coinMin = int(confData[0:4], 16)
  370. cardMin = int(confData[4:8], 16)
  371. coinElec = int(confData[8:10], 16)
  372. cardElec = int(confData[10:12], 16)
  373. cst = int(confData[12:14], 16)
  374. powerMax1 = int(confData[14:18], 16)
  375. powerMax2 = int(confData[18:22], 16)
  376. powerMax3 = int(confData[22:26], 16)
  377. powerMax4 = int(confData[26:30], 16)
  378. power2Ti = int(confData[30:32], 16)
  379. power3Ti = int(confData[32:34], 16)
  380. power4Ti = int(confData[34:36], 16)
  381. spRecMon = int(confData[36:38], 16)
  382. spFullEmpty = int(confData[38:40], 16)
  383. fullPowerMin = int(confData[40:42], 16)
  384. fullChargeTime = int(confData[42:44], 16)
  385. elecTimeFirst = int(confData[44:46], 16)
  386. try:
  387. firstCoinTime = int(confData[46:50], 16)
  388. secondCoinTime = int(confData[50:54], 16)
  389. thirdCoinTime = int(confData[54:58], 16)
  390. except Exception as e:
  391. firstCoinTime = 0
  392. secondCoinTime = 0
  393. thirdCoinTime = 0
  394. try:
  395. self.device.update_other_conf(
  396. cst=cst, coinMin=coinMin, coinElec=coinElec, cardMin=cardMin, cardElec=cardElec)
  397. except Exception as e:
  398. pass
  399. resultDict = {
  400. 'coinMin': coinMin,
  401. 'cardMin': cardMin,
  402. 'coinElec': coinElec,
  403. 'cardElec': cardElec,
  404. 'cst': cst,
  405. 'powerMax1': powerMax1,
  406. 'powerMax2': powerMax2,
  407. 'powerMax3': powerMax3,
  408. 'powerMax4': powerMax4,
  409. 'power2Ti': power2Ti,
  410. 'power3Ti': power3Ti,
  411. 'power4Ti': power4Ti,
  412. 'spRecMon': spRecMon,
  413. 'spFullEmpty': spFullEmpty,
  414. 'fullPowerMin': fullPowerMin,
  415. 'fullChargeTime': fullChargeTime,
  416. 'elecTimeFirst': elecTimeFirst,
  417. 'firstCoinTime': firstCoinTime,
  418. 'secondCoinTime': secondCoinTime,
  419. 'thirdCoinTime': thirdCoinTime
  420. }
  421. consumeInfo = self.get_dev_consume_count()
  422. resultDict.update(consumeInfo)
  423. deviceInfo = self.get_real_time_device_info()
  424. resultDict.update(deviceInfo)
  425. return resultDict
  426. @SmartBox.check_device_features(device_features=["fun_code_30_31"], no_features_to_return={"haveStatus": False})
  427. def get_real_time_device_info(self):
  428. try:
  429. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  430. {'IMEI': self._device['devNo'], 'funCode': '30', 'data': '00'}, timeout=7)
  431. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  432. if devInfo['rst'] == -1:
  433. raise ServiceException(
  434. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  435. elif devInfo['rst'] == 1:
  436. raise ServiceException(
  437. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  438. except Exception:
  439. return {"haveStatus": False}
  440. data = devInfo.get("data")
  441. maxPower = int(data[8:12], 16)
  442. elec = int(data[12:16], 16)
  443. maxTemperature = int(data[16:18], 16)
  444. base = -1 if int(data[18:20], 16) == 1 else 1
  445. temperature = int(data[20:22], 16) * base
  446. fire = int(data[22:24], 16)
  447. smoke = int(data[24:26], 16)
  448. overheat = int(data[26:28], 16)
  449. overload = int(data[28:30], 16)
  450. relayMasterSwitch = int(data[30:32], 16)
  451. return {"haveStatus": True, "maxPower": maxPower, "elec": elec, "maxTemperature": maxTemperature,
  452. "temperature": temperature,
  453. "fire": fire, "smoke": smoke, "overheat": overheat, "overload": overload,
  454. "relayMasterSwitch": relayMasterSwitch, }
  455. def get_dev_consume_count(self):
  456. data = self.send_mqtt({'funCode': '07', 'data': '00'})
  457. data = data[6:]
  458. if data[0:2] == '01': # 表示成功
  459. pass
  460. else:
  461. raise ServiceException({'result': 2, 'description': u'获取统计数据失败,请重试看能否解决'})
  462. cardFee = int(data[2:6], 16) / 10.0 # 以角为单位
  463. coinFee = int(data[6:10], 16) # 以元为单位
  464. return {'cardFee': cardFee, 'coinFee': coinFee}
  465. def set_dev_setting(self, setConf):
  466. data = self.encode_str(setConf['coinMin'], length=4)
  467. data += self.encode_str(setConf['cardMin'], length=4)
  468. data += self.encode_str(setConf['coinElec'], length=2)
  469. data += self.encode_str(setConf['cardElec'], length=2)
  470. data += self.encode_str(setConf['cst'], length=2)
  471. data += self.encode_str(setConf['powerMax1'], length=4)
  472. data += self.encode_str(setConf['powerMax2'], length=4)
  473. data += self.encode_str(setConf['powerMax3'], length=4)
  474. data += self.encode_str(setConf['powerMax4'], length=4)
  475. data += self.encode_str(setConf['power2Ti'], length=2)
  476. data += self.encode_str(setConf['power3Ti'], length=2)
  477. data += self.encode_str(setConf['power4Ti'], length=2)
  478. data += self.encode_str(setConf['spRecMon'], length=2)
  479. data += self.encode_str(setConf['spFullEmpty'], length=2)
  480. data += self.encode_str(setConf['fullPowerMin'], length=2)
  481. data += self.encode_str(setConf['fullChargeTime'], length=2)
  482. data += self.encode_str(setConf['elecTimeFirst'], length=2)
  483. self.send_mqtt({'funCode': '18', 'data': data})
  484. def set_dev_setting2(self, setConf):
  485. if 'lowPower' in setConf: # 新版本
  486. data = self.encode_str(setConf['lowPower'])
  487. else: # 老版本
  488. data = self.encode_str(setConf['stopDelay'])
  489. data += self.encode_str(setConf['maxTotalTime'])
  490. data += self.encode_str(setConf['overVoltage'], length=4)
  491. data += self.encode_str(setConf['lowVoltage'], length=4)
  492. data += self.encode_str(setConf['overTemp'])
  493. data += self.encode_str(setConf['overTempDelayTime'])
  494. data += str(setConf['timeDspOn']).replace(':', '')
  495. data += str(setConf['timeDspOff']).replace(':', '')
  496. data += self.encode_str(setConf['pricePower1'], length=4, ratio=1000.0)
  497. data += self.encode_str(setConf['pricePower2'], length=4, ratio=1000.0)
  498. data += self.encode_str(setConf['pricePower3'], length=4, ratio=1000.0)
  499. data += self.encode_str(setConf['pricePower4'], length=4, ratio=1000.0)
  500. data += self.encode_str(setConf['pricePower5'], length=4, ratio=1000.0)
  501. if 'lowPower' in setConf: # 新版本
  502. data += self.encode_str(setConf.get('lowPowerCheckTime', 30), length=4)
  503. data += self.encode_str(setConf.get('stopDelayTime', 30), length=4)
  504. else:
  505. pass
  506. if 'maxTotalPower' in setConf:
  507. data += self.encode_str(setConf['maxTotalPower'])
  508. self.send_mqtt({'funCode': '22', 'data': data})
  509. def set_device_function_param(self, request, lastSetConf):
  510. coinMin = request.POST.get('coinMin', None)
  511. cardMin = request.POST.get('cardMin', None)
  512. coinElec = request.POST.get('coinElec', None)
  513. cardElec = request.POST.get('cardElec', None)
  514. cst = request.POST.get('cst', None)
  515. powerMax1 = request.POST.get('powerMax1', None)
  516. powerMax2 = request.POST.get('powerMax2', None)
  517. powerMax3 = request.POST.get('powerMax3', None)
  518. powerMax4 = request.POST.get('powerMax4', None)
  519. power2Ti = request.POST.get('power2Ti', None)
  520. power3Ti = request.POST.get('power3Ti', None)
  521. power4Ti = request.POST.get('power4Ti', None)
  522. spRecMon = request.POST.get('spRecMon', None)
  523. spFullEmpty = request.POST.get('spFullEmpty', None)
  524. fullPowerMin = request.POST.get('fullPowerMin', None)
  525. fullChargeTime = request.POST.get('fullChargeTime', None)
  526. elecTimeFirst = request.POST.get('elecTimeFirst', None)
  527. # 这几个参数是墨小智V3特有的
  528. stWTime = request.POST.get('stWTime', None)
  529. temThre = request.POST.get('temThre', None)
  530. freeUse = request.POST.get('freeUse', None)
  531. relayMasterSwitch = request.POST.get('relayMasterSwitch', None)
  532. # 这几个参数是久恒保险丝版本特有的
  533. firstCoinTime = request.POST.get('firstCoinTime', None)
  534. secondCoinTime = request.POST.get('secondCoinTime', None)
  535. thirdCoinTime = request.POST.get('thirdCoinTime', None)
  536. # 这几个参数是申林电子8档久恒板特有的
  537. powerMax5 = request.POST.get('powerMax5', None)
  538. powerMax6 = request.POST.get('powerMax6', None)
  539. powerMax7 = request.POST.get('powerMax7', None)
  540. powerMax8 = request.POST.get('powerMax8', None)
  541. power5Ti = request.POST.get('power5Ti', None)
  542. power6Ti = request.POST.get('power6Ti', None)
  543. power7Ti = request.POST.get('power7Ti', None)
  544. power8Ti = request.POST.get('power8Ti', None)
  545. if coinMin:
  546. lastSetConf.update({'coinMin': int(coinMin)})
  547. if cardMin:
  548. lastSetConf.update({'cardMin': int(cardMin)})
  549. if coinElec:
  550. lastSetConf.update({'coinElec': int(coinElec)})
  551. if cardElec:
  552. lastSetConf.update({'cardElec': int(cardElec)})
  553. if cst:
  554. lastSetConf.update({'cst': int(cst)})
  555. if powerMax1:
  556. lastSetConf.update({'powerMax1': int(powerMax1)})
  557. if powerMax2:
  558. lastSetConf.update({'powerMax2': int(powerMax2)})
  559. if powerMax3:
  560. lastSetConf.update({'powerMax3': int(powerMax3)})
  561. if powerMax4:
  562. lastSetConf.update({'powerMax4': int(powerMax4)})
  563. if power2Ti:
  564. lastSetConf.update({'power2Ti': int(power2Ti)})
  565. if power3Ti:
  566. lastSetConf.update({'power3Ti': int(power3Ti)})
  567. if power4Ti:
  568. lastSetConf.update({'power4Ti': int(power4Ti)})
  569. if spRecMon:
  570. lastSetConf.update({'spRecMon': int(spRecMon)})
  571. if spFullEmpty:
  572. lastSetConf.update({'spFullEmpty': int(spFullEmpty)})
  573. if fullPowerMin:
  574. lastSetConf.update({'fullPowerMin': int(fullPowerMin)})
  575. if fullChargeTime:
  576. lastSetConf.update({'fullChargeTime': int(fullChargeTime)})
  577. if elecTimeFirst:
  578. lastSetConf.update({'elecTimeFirst': int(elecTimeFirst)})
  579. # 这几个参数是墨小智V3特有的
  580. if stWTime:
  581. lastSetConf.update({'stWTime': int(stWTime)})
  582. if temThre:
  583. lastSetConf.update({'temThre': int(temThre)})
  584. if freeUse:
  585. lastSetConf.update({'freeUse': int(freeUse)})
  586. # 这几个参数是久恒保险丝版本特有的
  587. if firstCoinTime:
  588. lastSetConf.update({'firstCoinTime': int(firstCoinTime)})
  589. if secondCoinTime:
  590. lastSetConf.update({'secondCoinTime': int(secondCoinTime)})
  591. if thirdCoinTime:
  592. lastSetConf.update({'thirdCoinTime': int(thirdCoinTime)})
  593. # 这几个参数是申林电子8档久恒板特有的
  594. if powerMax5:
  595. lastSetConf.update({'powerMax5': int(powerMax5)})
  596. if powerMax6:
  597. lastSetConf.update({'powerMax6': int(powerMax6)})
  598. if powerMax7:
  599. lastSetConf.update({'powerMax7': int(powerMax7)})
  600. if powerMax8:
  601. lastSetConf.update({'powerMax8': int(powerMax8)})
  602. if power5Ti:
  603. lastSetConf.update({'power5Ti': int(power5Ti)})
  604. if power6Ti:
  605. lastSetConf.update({'power6Ti': int(power6Ti)})
  606. if power7Ti:
  607. lastSetConf.update({'power7Ti': int(power7Ti)})
  608. if power8Ti:
  609. lastSetConf.update({'power8Ti': int(power8Ti)})
  610. self.set_dev_setting(lastSetConf)
  611. # 新增总继电器开关
  612. if relayMasterSwitch:
  613. try:
  614. self.relay_master_switch(relayMasterSwitch)
  615. except Exception as e:
  616. pass
  617. def handle_clear_start_error(self, port):
  618. dealer = Dealer.objects.get(id=self._device['ownerId'])
  619. if not dealer or not dealer.managerialOpenId:
  620. return
  621. group = Group.get_group(self._device['groupId'])
  622. self.lock_unlock_port(port, lock=False)
  623. notifyData = {
  624. 'title': '设备故障报警解除',
  625. 'device': u'{gNum}组-{lc}-{port}端口'.format(gNum=self._device['groupNumber'],
  626. lc=self._device['logicalCode'],
  627. port=port),
  628. 'location': u'{address}-{groupName}'.format(address=group['address'], groupName=group['groupName']),
  629. 'fault': u'当前端口已被正常启动,端口故障解除',
  630. 'notifyTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  631. }
  632. task_caller(
  633. func_name='report_to_dealer_via_wechat',
  634. openId=dealer.managerialOpenId,
  635. dealerId=str(dealer.id),
  636. templateName='device_fault',
  637. **notifyData
  638. )
  639. self.lock_unlock_port(port, lock=False)
  640. def handle_out_start_error(self, port):
  641. dealer = Dealer.objects.get(id=self._device['ownerId'])
  642. if not dealer or not dealer.managerialOpenId:
  643. return
  644. group = Group.get_group(self._device['groupId'])
  645. times = Device.get_error_start_times(self._device['devNo'], port)
  646. self.lock_unlock_port(port, lock=True)
  647. notifyData = {
  648. 'title': u'注意,您的设备可能发生故障!',
  649. 'device': u'{gNum}组-{lc}-{port}端口'.format(gNum=self._device['groupNumber'],
  650. lc=self._device['logicalCode'],
  651. port=port),
  652. 'location': u'{address}-{groupName}'.format(address=group['address'], groupName=group['groupName']),
  653. 'fault': u'当前设备端口连续启动 {times} 失败,目前该端口已经被自动禁用,请注意该端口是否发生故障'.format(times=times),
  654. 'notifyTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  655. }
  656. task_caller(
  657. func_name='report_to_dealer_via_wechat',
  658. openId=dealer.managerialOpenId,
  659. dealerId=str(dealer.id),
  660. templateName='device_fault',
  661. **notifyData
  662. )
  663. # 麦总的告警3次触发,同时锁定设备,为避免解锁设备之后 再次触发 这个地方需要将其缓存清空
  664. Device.delete_error_start_times(self._device['devNo'], port)
  665. def set_coin_ic_switch(self, coin=True, ic=True):
  666. if coin:
  667. data = '01'
  668. else:
  669. data = '00'
  670. if ic:
  671. data += '01'
  672. else:
  673. data += '00'
  674. result = self.send_mqtt({'funCode': '09', 'data': data})
  675. return result
  676. @SmartBox.check_device_features(device_features=["fun_code_30_31"])
  677. def relay_master_switch(self, relayMasterSwitch=1):
  678. if relayMasterSwitch == 1:
  679. data = "01"
  680. else:
  681. data = "00"
  682. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  683. {'IMEI': self._device['devNo'], 'funCode': '31', 'data': data}, timeout=7)
  684. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  685. if devInfo['rst'] == -1:
  686. raise ServiceException(
  687. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  688. elif devInfo['rst'] == 1:
  689. raise ServiceException(
  690. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  691. self.device.update_other_conf(relayMasterSwitch=relayMasterSwitch)
  692. def check_order_state(self, openId):
  693. """
  694. 通过 openId 以及设备来鉴别 订单
  695. :param openId:
  696. :return:
  697. """
  698. dealerId = self.device.ownerId
  699. return ClientConsumeModelProxy.get_not_finished_record(ownerId=dealerId, openId=openId,
  700. devTypeCode=self._device['devType']['code'],
  701. package__billingMethod=CONSUMETYPE.POSTPAID)
  702. def start_device_realiable(self, order):
  703. return super(JNDZPOLICYBox, self).start_device_realiable(order)
  704. def deal_order_finish(self, order, data=None):
  705. pass
  706. def set_volume(self, volume):
  707. volume = self.check_params_range(volume, minData=0.0, maxData=8.0, desc='音量')
  708. try:
  709. self.send_mqtt({'funCode': '24', 'data': self.encode_str(volume)}, timeout=2)
  710. except:
  711. pass
  712. Device.get_collection().update_one(filter={'devNo': self.device.devNo},
  713. update={'$set': {'otherConf.volume': volume, }})
  714. Device.invalid_device_cache(self.device.devNo)
  715. def get_server_setting(self):
  716. return {
  717. 'minAfterStartCoins': self.device['otherConf'].get('minAfterStartCoins', 0),
  718. 'refundProtectionTime': self.server_configs.get('refundProtectionTime', 5),
  719. 'packages': self.get_power_price_rules(),
  720. 'timeElec': self.server_configs.get('timeElec', 1),
  721. 'coinRatio': self.server_configs.get('coinRatio', 1),
  722. 'onlineCardMinStartCoins': self.server_configs.get('onlineCardMinStartCoins', 0),
  723. 'onlineCardTime': self.server_configs.get('onlineCardTime', 720),
  724. 'onlineCardELec': self.server_configs.get('onlineCardELec', 3)
  725. }
  726. def set_server_setting(self, payload):
  727. updateConf = {
  728. }
  729. packages = payload.get('packages')
  730. if packages:
  731. updateConf.update({
  732. 'otherConf.serverConfigs.power_price_rules': self.check_power_price_rules(packages)
  733. })
  734. timeElec = payload.get('timeElec')
  735. if timeElec:
  736. updateConf.update({'otherConf.serverConfigs.timeElec': round(float(timeElec), 4)})
  737. refundProtectionTime = payload.get('refundProtectionTime', None)
  738. if refundProtectionTime:
  739. updateConf.update({
  740. 'otherConf.serverConfigs.refundProtectionTime': int(refundProtectionTime)
  741. })
  742. coinRatio = payload.get('coinRatio')
  743. if coinRatio:
  744. updateConf.update({
  745. 'otherConf.serverConfigs.coinRatio': round(float(coinRatio), 2)
  746. })
  747. if 'minAfterStartCoins' in payload and payload['minAfterStartCoins']:
  748. minAfterStartCoins = self.check_params_range(
  749. payload['minAfterStartCoins'], minData=0.0, maxData=100.0, desc='设备启动最小余额')
  750. updateConf.update({
  751. 'otherConf.minAfterStartCoins': float(minAfterStartCoins)
  752. })
  753. onlineCardMinStartCoins = payload.get('onlineCardMinStartCoins')
  754. onlineCardTime = payload.get('onlineCardTime')
  755. onlineCardELec = payload.get('onlineCardELec')
  756. if onlineCardMinStartCoins and onlineCardTime and onlineCardELec:
  757. updateConf.update(
  758. {
  759. 'otherConf.serverConfigs.onlineCardMinStartCoins': round(float(onlineCardMinStartCoins), 2),
  760. 'otherConf.serverConfigs.onlineCardTime': round(float(onlineCardTime), 2),
  761. 'otherConf.serverConfigs.onlineCardELec': round(float(onlineCardELec), 4)
  762. })
  763. self.device.update_device_obj(**updateConf)