xkd_policy.py 37 KB

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