aiyaxin5.py 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import logging
  5. import time
  6. from decimal import Decimal
  7. from typing import Optional
  8. from apilib.monetary import RMB
  9. from apilib.utils_datetime import timestamp_to_dt
  10. from apps.web.constant import DeviceCmdCode, MQTT_TIMEOUT, Const, ErrorCode
  11. from apps.web.core.adapter.base import SmartBox, fill_2_hexByte, make_six_bytes_session_id
  12. from apps.web.core.exceptions import ServiceException
  13. from apps.web.core.networking import MessageSender
  14. from apps.web.device.models import Device
  15. logger = logging.getLogger(__name__)
  16. class Aiyaxin5Box(SmartBox):
  17. def __init__(self, device):
  18. super(Aiyaxin5Box, self).__init__(device)
  19. def translate_funcode(self, funCode):
  20. funCodeDict = {
  21. '01': u'获取端口数量',
  22. '02': u'移动支付',
  23. '0C': u'获取设备设置',
  24. '08': u'设置设备参数',
  25. '06': u'获取设备端口详情',
  26. '07': u'获取刷卡投币统计数据',
  27. '09': u'设置刷卡投币使能',
  28. '0A': u'端口使能',
  29. '0B': u'端口关闭',
  30. '16': u'设置设备参数',
  31. '20': u'设备重启',
  32. '13': u'设置卡充满退费',
  33. '15': u'获取功率费率配置',
  34. '14': u'设置功率费率配置',
  35. '12': u'充值',
  36. }
  37. return funCodeDict.get(funCode, '')
  38. def translate_event_cmdcode(self, cmdCode):
  39. cmdDict = {
  40. '03': u'投币上报',
  41. '04': u'刷卡上报',
  42. '05': u'充电结束',
  43. '16': u'充电结束',
  44. '10': u'刷卡上报',
  45. '0D': u'故障',
  46. '20': u'启动设备',
  47. '11': u'刷卡使用',
  48. '12': u'卡充值',
  49. '21': u'电流上报',
  50. '22': u'温度上报',
  51. '23': u'烟感上报',
  52. }
  53. return cmdDict.get(cmdCode, '')
  54. def test(self, coins):
  55. hexPort = fill_2_hexByte(hex(int(1)), 2)
  56. hexTime = fill_2_hexByte(hex(5))
  57. devInfo = MessageSender.send(device = self.device,
  58. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  59. payload = {
  60. 'IMEI': self._device['devNo'],
  61. "funCode": '02',
  62. 'data': hexPort + '0000' + hexTime
  63. })
  64. return devInfo
  65. def start_device(self, package, openId, attachParas):
  66. if attachParas is None:
  67. raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路、电池类型信息'})
  68. if not attachParas.has_key('chargeIndex'):
  69. raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路'})
  70. port = hex(int(attachParas['chargeIndex']))
  71. hexPort = fill_2_hexByte(port, 2)
  72. needTime = int(package['time'])
  73. unit = package.get('unit', u'分钟')
  74. if unit == u'小时':
  75. needTime = int(package['time']) * 60
  76. elif unit == u'天':
  77. needTime = int(package['time']) * 1440
  78. coins = float(package['coins'])
  79. hexTime = fill_2_hexByte(hex(needTime))
  80. devInfo = MessageSender.send(device = self.device,
  81. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  82. payload = {
  83. 'IMEI': self._device['devNo'],
  84. "funCode": '02',
  85. 'data': hexPort + '0000' + hexTime
  86. }, timeout = MQTT_TIMEOUT.START_DEVICE)
  87. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  88. if devInfo['rst'] == -1:
  89. raise ServiceException({'result': 2, 'description': u'充电桩正在玩命找网络,您的金币还在,重试不需要重新付款,建议您试试旁边其他设备,或者稍后再试哦'})
  90. elif devInfo['rst'] == 1:
  91. raise ServiceException({'result': 2, 'description': u'充电桩正在忙,无响应,您的金币还在,请试试其他线路,或者请稍后再试哦'})
  92. data = devInfo['data'][16::]
  93. usePort = int(attachParas['chargeIndex'])
  94. result = data[4:6]
  95. if result == '01': # 成功
  96. pass
  97. elif result == '02':
  98. newValue = {str(usePort): {'status': Const.DEV_WORK_STATUS_FAULT, 'statusInfo': u'充电站故障'}}
  99. Device.update_dev_control_cache(self._device['devNo'], newValue)
  100. raise ServiceException({'result': 2, 'description': u'充电站故障'})
  101. elif result == '03':
  102. newValue = {str(usePort): {'status': Const.DEV_WORK_STATUS_WORKING, 'statusInfo': u''}}
  103. Device.update_dev_control_cache(self._device['devNo'], newValue)
  104. raise ServiceException({'result': 2, 'description': u'该端口正在使用中'})
  105. start_timestamp = int(time.time())
  106. devInfo['finishedTime'] = start_timestamp + needTime * 60
  107. Device.update_dev_control_cache(
  108. self._device['devNo'],
  109. {
  110. str(usePort): {
  111. 'startTime': timestamp_to_dt(start_timestamp).strftime('%Y-%m-%d %H:%M:%S'),
  112. 'status': Const.DEV_WORK_STATUS_WORKING,
  113. 'finishedTime': devInfo['finishedTime'],
  114. 'coins': coins,
  115. 'needTime': needTime,
  116. 'isStart': True,
  117. 'openId': openId,
  118. 'refunded': False,
  119. 'vCardId': self._vcard_id
  120. }
  121. })
  122. return devInfo
  123. def analyze_event_data(self, data):
  124. cmdCode = data[4:6]
  125. if cmdCode in ['05']:
  126. port = int(data[18:20], 16)
  127. leftTime = int(data[20:24], 16)
  128. reason = data[24:26]
  129. if reason == '00':
  130. desc = u'您购买的充电时间用完了。'
  131. elif reason == '01':
  132. desc = u'可能是插头被拔掉,或者电瓶已经充满。系统判断为异常断电,由于电瓶车充电器种类繁多,可能存在误差。如有问题,请您及时联系商家协助解决问题并恢复充电。'
  133. elif reason == '02':
  134. desc = u'恭喜您!电池已经充满电!'
  135. elif reason == '03':
  136. desc = u'设备或端口出现问题,为了安全起见,被迫停止工作。建议您根据已经充电的时间评估是否需要到现场换到其他端口充电。'
  137. elif reason == '04':
  138. desc = u'警告!您的电池功率超过本机最大限制,已经停止充电,为了公共安全,不建议您在该充电桩充电!提醒您,为了安全大功率的电池不要放入楼道、室内等位置充电哦'
  139. elif reason == '05':
  140. desc = u'刷卡退费结束了哦'
  141. elif reason == '06':
  142. desc = u'已经开始充电,但是没有检测到电池,建议检查插座是否连接正常'
  143. else:
  144. desc = u''
  145. return {'status': Const.DEV_WORK_STATUS_IDLE, 'cmdCode': cmdCode, 'port': port, 'leftTime': leftTime,
  146. 'reason': desc, 'isStart': False}
  147. elif cmdCode == '0D':
  148. port = int(data[16:18], 16)
  149. errCode = int(data[18:20], 16)
  150. if errCode == '01':
  151. return {'status': Const.DEV_WORK_STATUS_FAULT, 'statusInfo': u'端口输出故障', 'cmdCode': cmdCode,
  152. 'port': port, 'FaultCode': errCode}
  153. elif errCode == '02':
  154. return {'status': Const.DEV_WORK_STATUS_FAULT, 'statusInfo': u'机器整体充电功率过大', 'cmdCode': cmdCode,
  155. 'port': port, 'FaultCode': errCode}
  156. elif errCode == '03':
  157. return {'status': Const.DEV_WORK_STATUS_FAULT, 'statusInfo': u'电源故障', 'cmdCode': cmdCode, 'port': port,
  158. 'FaultCode': errCode}
  159. elif cmdCode == '03':
  160. coin = int(data[18:20], 16)
  161. return {'cmdCode': cmdCode, 'coins': coin}
  162. elif cmdCode == '11':
  163. cardNo = data[18:26]
  164. cardNo = str(int(cardNo, 16))
  165. money = int(data[26:28], 16) / 10.0
  166. balance = int(data[28:32], 16) / 10.0
  167. port = int(data[36:38], 16)
  168. status = data[38:40]
  169. return {'cmdCode': cmdCode, 'cardNo': cardNo, 'coins': money, 'balance': balance, 'status': status,
  170. 'port': port}
  171. elif cmdCode == '12':
  172. cardNo = data[18:26]
  173. cardNo = str(int(cardNo, 16))
  174. balance = int(data[26:30], 16) / 10.0
  175. cardType = data[30:34]
  176. return {'cmdCode': cmdCode, 'balance': balance, 'cardType': cardType, 'cardNo': cardNo}
  177. elif cmdCode == '04': # 旧版本的刷卡报文
  178. money = int(data[18:20], 16)
  179. return {'cmdCode': cmdCode, 'money': money}
  180. elif cmdCode == '30': # 宇泽家的,主动上报电流
  181. portData = data[18:48]
  182. port1 = int(portData[2:4])
  183. port2 = int(portData[4:6])
  184. port3 = int(portData[6:8])
  185. port4 = int(portData[8:10])
  186. port5 = int(portData[10:12])
  187. port6 = int(portData[12:14])
  188. port7 = int(portData[14:16])
  189. port8 = int(portData[16:18])
  190. port9 = int(portData[18:20])
  191. port10 = int(portData[20:22])
  192. device = int(portData[22:24])
  193. return {
  194. 'cmdCode': cmdCode,
  195. 'port1': port1,
  196. 'port2': port2,
  197. 'port3': port3,
  198. 'port4': port4,
  199. 'port5': port5,
  200. 'port6': port6,
  201. 'port7': port7,
  202. 'port8': port8,
  203. 'port9': port9,
  204. 'port10': port10,
  205. 'device': device,
  206. }
  207. elif cmdCode == '31': # 宇泽家的,主动上报温度
  208. portData = data[18:48]
  209. port1 = int(portData[2:4])
  210. port2 = int(portData[4:6])
  211. port3 = int(portData[6:8])
  212. port4 = int(portData[8:10])
  213. port5 = int(portData[10:12])
  214. port6 = int(portData[12:14])
  215. port7 = int(portData[14:16])
  216. port8 = int(portData[16:18])
  217. port9 = int(portData[18:20])
  218. port10 = int(portData[20:22])
  219. device = int(portData[22:24])
  220. return {
  221. 'cmdCode': cmdCode,
  222. 'port1': port1,
  223. 'port2': port2,
  224. 'port3': port3,
  225. 'port4': port4,
  226. 'port5': port5,
  227. 'port6': port6,
  228. 'port7': port7,
  229. 'port8': port8,
  230. 'port9': port9,
  231. 'port10': port10,
  232. 'device': device,
  233. }
  234. elif cmdCode == '32': # 宇泽家的,主动上报温度
  235. if data[18:20] == '01':
  236. return {'isYangan': True, 'cmdCode': cmdCode}
  237. else:
  238. return {'isYangan': False, 'cmdCode': cmdCode}
  239. def get_dev_consume_count(self):
  240. devInfo = MessageSender.send(device = self.device,
  241. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  242. payload = {
  243. 'IMEI': self._device['devNo'], "funCode": '07', 'data': '00'
  244. })
  245. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  246. if devInfo['rst'] == -1:
  247. raise ServiceException(
  248. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  249. elif devInfo['rst'] == 1:
  250. raise ServiceException(
  251. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  252. data = devInfo['data'][16::]
  253. cardFee = int(data[2:6], 16) / 10.0 # 以角为单位
  254. coinFee = int(data[6:10], 16) # 以元为单位
  255. return {'cardFee': cardFee, 'coinFee': coinFee}
  256. def get_port_info(self, line):
  257. data = fill_2_hexByte(hex(int(line)), 2)
  258. devInfo = MessageSender.send(device = self.device,
  259. cmd = self.make_random_cmdcode(),
  260. payload = {
  261. 'IMEI': self._device['devNo'], "funCode": '06', 'data': data
  262. })
  263. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  264. if devInfo['rst'] == -1:
  265. raise ServiceException(
  266. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  267. elif devInfo['rst'] == 1:
  268. raise ServiceException(
  269. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  270. data = devInfo['data'][16::]
  271. leftTime = int(data[4:8], 16)
  272. if data[8:12] == 'FFFF':
  273. power = 0
  274. else:
  275. power = int(data[8:12], 16) / 10.0
  276. return {'port': line, 'leftTime': leftTime, 'power': power}
  277. # 访问设备电流
  278. def get_port_elec_from_dev(self):
  279. devInfo = MessageSender.send(device = self.device,
  280. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  281. payload = {
  282. 'IMEI': self._device['devNo'], "funCode": '21', 'data': '00'
  283. })
  284. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  285. if devInfo['rst'] == -1:
  286. raise ServiceException(
  287. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  288. elif devInfo['rst'] == 1:
  289. raise ServiceException(
  290. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  291. portData = devInfo['data'][18::]
  292. port1 = int(portData[2:4], 16)
  293. port2 = int(portData[4:6], 16)
  294. port3 = int(portData[6:8], 16)
  295. port4 = int(portData[8:10], 16)
  296. port5 = int(portData[10:12], 16)
  297. port6 = int(portData[12:14], 16)
  298. port7 = int(portData[14:16], 16)
  299. port8 = int(portData[16:18], 16)
  300. port9 = int(portData[18:20], 16)
  301. port10 = int(portData[20:22], 16)
  302. device = int(portData[22:24], 16)
  303. return {
  304. 'port1': port1,
  305. 'port2': port2,
  306. 'port3': port3,
  307. 'port4': port4,
  308. 'port5': port5,
  309. 'port6': port6,
  310. 'port7': port7,
  311. 'port8': port8,
  312. 'port9': port9,
  313. 'port10': port10,
  314. 'device': device,
  315. }
  316. # 访问设备
  317. def get_port_temperature_from_dev(self):
  318. devInfo = MessageSender.send(device = self.device,
  319. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  320. payload = {
  321. 'IMEI': self._device['devNo'], "funCode": '22', 'data': '00'
  322. })
  323. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  324. if devInfo['rst'] == -1:
  325. raise ServiceException(
  326. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  327. elif devInfo['rst'] == 1:
  328. raise ServiceException(
  329. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  330. portData = devInfo['data'][18::]
  331. port1 = int(portData[2:4], 16)
  332. port2 = int(portData[4:6], 16)
  333. port3 = int(portData[6:8], 16)
  334. port4 = int(portData[8:10], 16)
  335. port5 = int(portData[10:12], 16)
  336. port6 = int(portData[12:14], 16)
  337. port7 = int(portData[14:16], 16)
  338. port8 = int(portData[16:18], 16)
  339. port9 = int(portData[18:20], 16)
  340. port10 = int(portData[20:22], 16)
  341. device = int(portData[22:24], 16)
  342. return {
  343. 'port1': port1,
  344. 'port2': port2,
  345. 'port3': port3,
  346. 'port4': port4,
  347. 'port5': port5,
  348. 'port6': port6,
  349. 'port7': port7,
  350. 'port8': port8,
  351. 'port9': port9,
  352. 'port10': port10,
  353. 'device': device,
  354. }
  355. # 访问设备,获取设备端口信息
  356. def get_port_status_from_dev(self):
  357. devInfo = MessageSender.send(device = self.device,
  358. cmd = self.make_random_cmdcode(),
  359. payload = {
  360. 'IMEI': self._device['devNo'], "funCode": '01', 'data': '00'
  361. })
  362. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  363. if devInfo['rst'] == -1:
  364. raise ServiceException(
  365. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  366. elif devInfo['rst'] == 1:
  367. raise ServiceException(
  368. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  369. data = devInfo['data'][16::]
  370. result = {}
  371. portNum = int(data[2:4], 16)
  372. portData = data[4::]
  373. ii = 0
  374. while ii < portNum:
  375. statusTemp = portData[ii * 2:ii * 2 + 2]
  376. if statusTemp == '01':
  377. status = {'status': Const.DEV_WORK_STATUS_IDLE}
  378. elif statusTemp == '02':
  379. status = {'status': Const.DEV_WORK_STATUS_WORKING}
  380. elif statusTemp == '03':
  381. status = {'status': Const.DEV_WORK_STATUS_FORBIDDEN}
  382. elif statusTemp == '04':
  383. status = {'status': Const.DEV_WORK_STATUS_FAULT}
  384. ii += 1
  385. result[str(ii)] = status
  386. allPorts, usedPorts, usePorts = self.get_port_static_info(result)
  387. Device.update_dev_control_cache(self._device['devNo'],
  388. {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  389. # 这里存在多线程更新缓存的场景,可能性比较低,但是必须先取出来,然后逐个更新状态,然后再记录缓存
  390. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  391. for strPort, info in result.items():
  392. if ctrInfo.has_key(strPort):
  393. ctrInfo[strPort].update({'status': info['status']})
  394. else:
  395. ctrInfo[strPort] = info
  396. Device.update_dev_control_cache(self._device['devNo'], ctrInfo)
  397. return result
  398. def get_port_status(self, force = False):
  399. if force:
  400. return self.get_port_status_from_dev()
  401. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  402. if not ctrInfo.has_key('allPorts'):
  403. self.get_port_status_from_dev()
  404. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  405. allPorts = ctrInfo.get('allPorts', 10)
  406. statusDict = {}
  407. for ii in range(allPorts):
  408. tempDict = ctrInfo.get(str(ii + 1), {})
  409. if tempDict.has_key('status'):
  410. statusDict[str(ii + 1)] = {'status': tempDict.get('status')}
  411. elif tempDict.has_key('isStart'):
  412. if tempDict['isStart']:
  413. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_WORKING}
  414. else:
  415. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  416. else:
  417. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  418. allPorts, usedPorts, usePorts = self.get_port_static_info(statusDict)
  419. Device.update_dev_control_cache(self._device['devNo'],
  420. {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  421. return statusDict
  422. def lock_unlock_port(self, port, lock = True):
  423. lockStr = '00' if lock else '01'
  424. portStr = fill_2_hexByte(hex(int(port)), 2)
  425. devInfo = MessageSender.send(device = self.device,
  426. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  427. payload = {
  428. 'IMEI': self._device['devNo'],
  429. "funCode": '0A',
  430. 'data': portStr + lockStr
  431. })
  432. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  433. if devInfo['rst'] == -1:
  434. raise ServiceException(
  435. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  436. elif devInfo['rst'] == 1:
  437. raise ServiceException(
  438. {'result': 2, 'description': u'端口禁用功能只有带保险丝版本的才有。可能是您的设备版本过低,暂时不支持此功能,也可能是设备繁忙无响应。'})
  439. data = devInfo['data'][18::]
  440. if data[0:2] == '01': # 表示成功
  441. pass
  442. else:
  443. raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'})
  444. if lock:
  445. Device.update_dev_control_cache(self._device['devNo'],
  446. {str(port): {'status': Const.DEV_WORK_STATUS_FORBIDDEN}})
  447. else:
  448. Device.update_dev_control_cache(self._device['devNo'], {str(port): {'status': Const.DEV_WORK_STATUS_IDLE}})
  449. def active_deactive_port(self, port, active):
  450. if active:
  451. raise ServiceException({'result': 2, 'description': u'该设备不支持直接打开端口'})
  452. self.stop_charging_port(port)
  453. devInfo = Device.get_dev_control_cache(self._device['devNo'])
  454. portCtrInfo = devInfo.get(str(port), {})
  455. portCtrInfo.update({'isStart': False, 'status': Const.DEV_WORK_STATUS_IDLE, 'needTime': 0, 'leftTime': 0,
  456. 'endTime': datetime.datetime.now().strftime(Const.DATETIME_FMT)})
  457. newValue = {str(port): portCtrInfo}
  458. Device.update_dev_control_cache(self._device['devNo'], newValue)
  459. def stop_charging_port(self, port):
  460. portStr = fill_2_hexByte(hex(int(port)), 2)
  461. devInfo = MessageSender.send(device = self.device,
  462. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  463. payload = {
  464. 'IMEI': self._device['devNo'], "funCode": '0B', 'data': portStr
  465. })
  466. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  467. if devInfo['rst'] == -1:
  468. raise ServiceException(
  469. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  470. elif devInfo['rst'] == 1:
  471. raise ServiceException(
  472. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  473. data = devInfo['data'][16::]
  474. port = int(data[2:4], 16)
  475. leftTime = int(data[4:8], 16)
  476. return {'port': port, 'leftTime': leftTime}
  477. # 获取IC卡、投币、最大功率设置
  478. def get_IC_coin_power_config(self):
  479. devInfo = MessageSender.send(device = self.device,
  480. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  481. payload = {
  482. 'IMEI': self._device['devNo'], "funCode": '0C', 'data': '00'
  483. })
  484. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  485. if devInfo['rst'] == -1:
  486. raise ServiceException(
  487. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  488. elif devInfo['rst'] == 1:
  489. raise ServiceException(
  490. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  491. confData = devInfo['data'][18::]
  492. maxPower = int(confData[0:4], 16)
  493. icMoney = int(confData[4:6], 16)
  494. if len(confData) > 6:
  495. time1 = int(confData[6:10], 16)
  496. time2 = int(confData[10:14], 16)
  497. time3 = int(confData[14:18], 16)
  498. else:
  499. time1, time2, time3 = 0, 0, 0
  500. return {'maxPower': maxPower, 'icMoney': icMoney, 'time1': time1,
  501. 'time2': time2, 'time3': time3}
  502. def set_IC_coin_power_config(self, infoDict):
  503. data = ''
  504. data += fill_2_hexByte(hex(int(infoDict['maxPower'])), 4)
  505. data += fill_2_hexByte(hex(int(infoDict['icMoney'])), 2)
  506. data += fill_2_hexByte(hex(int(infoDict['time1'])), 4)
  507. data += fill_2_hexByte(hex(int(infoDict['time2'])), 4)
  508. data += fill_2_hexByte(hex(int(infoDict['time3'])), 4)
  509. devInfo = MessageSender.send(device = self.device,
  510. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  511. payload = {
  512. 'IMEI': self._device['devNo'], "funCode": '08', 'data': data
  513. })
  514. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  515. if devInfo['rst'] == -1:
  516. raise ServiceException(
  517. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  518. elif devInfo['rst'] == 1:
  519. raise ServiceException(
  520. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试'})
  521. def get_coin_card_enable(self):
  522. devs = Device.get_collection().find({'devNo': self._device['devNo']})
  523. if devs.count == 0:
  524. raise ServiceException(
  525. {'result': 2, 'description': u'没有找到设备哦'})
  526. return {'putCoins': devs[0].get('otherConf', {}).get('putCoins', False),
  527. 'icCard': devs[0].get('otherConf', {}).get('icCard', False)}
  528. def set_coin_card_enable(self, infoDict):
  529. data = ''
  530. if infoDict['putCoins']:
  531. data += '01'
  532. else:
  533. data += '00'
  534. if infoDict['icCard']:
  535. data += '01'
  536. else:
  537. data += '00'
  538. devInfo = MessageSender.send(device = self.device,
  539. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  540. payload = {
  541. 'IMEI': self._device['devNo'], "funCode": '09', 'data': data
  542. })
  543. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  544. if devInfo['rst'] == -1:
  545. raise ServiceException(
  546. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  547. elif devInfo['rst'] == 1:
  548. raise ServiceException(
  549. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  550. try:
  551. conf = Device.objects.get(devNo = self._device['devNo']).otherConf
  552. conf.update({'putCoins': infoDict['putCoins'], 'icCard': infoDict['icCard']})
  553. Device.get_collection().update({'devNo': self._device['devNo']}, {'$set': {'otherConf': conf}})
  554. except Exception, e:
  555. logger.error('update dev=%s coin enable ic enable e=%s' % (self._device['devNo'], e))
  556. # 获取设备配置参数
  557. def get_dev_setting(self):
  558. resultDict = self.get_IC_coin_power_config()
  559. tempDict = self.get_coin_card_enable()
  560. resultDict.update(tempDict)
  561. tempDict = self.get_gear_conf()
  562. resultDict.update(tempDict)
  563. tempDict = self.get_fullstop_cardrefund()
  564. resultDict.update(tempDict)
  565. # tempDict = self.get_freemode_volume_andsoon_config()
  566. # resultDict.update(tempDict)
  567. # tempDict = self.get_mcu_version()
  568. # resultDict.update(tempDict)
  569. return resultDict
  570. # 获取设备配置参数
  571. def set_dev_setting(self, setConf):
  572. keys = setConf.keys()
  573. if 'putCoins' in keys or 'icCard' in keys:
  574. self.set_coin_card_enable(setConf)
  575. if 'maxPower' in keys or 'icMoney' in keys or 'time1' in keys or 'time2' in keys or 'time3' in keys:
  576. self.set_IC_coin_power_config(setConf)
  577. def get_fullstop_cardrefund(self):
  578. try:
  579. dev = Device.objects.get(devNo = self._device['devNo'])
  580. except Exception, e:
  581. logger.error('get dev error = %s' % e)
  582. return {'autoStop': False, 'cardRefund': False}
  583. return {'autoStop': dev['otherConf'].get('autoStop', False),
  584. 'cardRefund': dev['otherConf'].get('cardRefund', False)}
  585. def set_fullstop_cardrefund(self, infoDict):
  586. data = ''
  587. if infoDict['autoStop']:
  588. data += '01'
  589. else:
  590. data += '00'
  591. if infoDict['cardRefund']:
  592. data += '01'
  593. else:
  594. data += '00'
  595. devInfo = MessageSender.send(device = self.device,
  596. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  597. payload = {
  598. 'IMEI': self._device['devNo'], "funCode": '13', 'data': data
  599. })
  600. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  601. if devInfo['rst'] == -1:
  602. raise ServiceException(
  603. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  604. elif devInfo['rst'] == 1:
  605. raise ServiceException(
  606. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  607. try:
  608. conf = Device.objects.get(devNo = self._device['devNo']).otherConf
  609. conf.update({'autoStop': infoDict['autoStop'], 'cardRefund': infoDict['cardRefund']})
  610. Device.get_collection().update({'devNo': self._device['devNo']}, {'$set': {'otherConf': conf}})
  611. except Exception, e:
  612. logger.error('update dev=%s coin enable ic enable e=%s' % (self._device['devNo'], e))
  613. def get_gear_conf(self):
  614. resultDict = {'power1': 0, 'power1ratio': 0, 'power2': 0,
  615. 'power2ratio': 0, 'power3': 0, 'power3ratio': 0}
  616. devInfo = MessageSender.send(device = self.device,
  617. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  618. payload = {
  619. 'IMEI': self._device['devNo'], "funCode": '15', 'data': '00'
  620. })
  621. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  622. if devInfo['rst'] == -1:
  623. raise ServiceException(
  624. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  625. elif devInfo['rst'] == 1:
  626. return resultDict
  627. confData = devInfo['data'][18::]
  628. power1 = int(confData[0:4], 16)
  629. power1ratio = int(confData[4:6], 16) * 10
  630. power2 = int(confData[6:10], 16)
  631. power2ratio = int(confData[10:12], 16) * 10
  632. power3 = int(confData[12:16], 16)
  633. power3ratio = int(confData[16:18], 16) * 10
  634. result = {
  635. 'power1': power1, 'power1ratio': power1ratio, 'power2': power2,
  636. 'power2ratio': power2ratio, 'power3': power3, 'power3ratio': power3ratio
  637. }
  638. if len(confData) > 31:
  639. power4 = int(confData[18:22], 16)
  640. power4ratio = int(confData[22:24], 16) * 10
  641. power5 = int(confData[24:28], 16)
  642. power5ratio = int(confData[28:30], 16) * 10
  643. result.update({'power4': power4, 'power4ratio': power4ratio,
  644. 'power5': power5, 'power5ratio': power5ratio})
  645. return result
  646. def set_gear_conf(self, infoDict):
  647. data = ''
  648. data += fill_2_hexByte(hex(int(infoDict['power1'])), 4)
  649. data += fill_2_hexByte(hex(int(infoDict['power1ratio']) / 10), 2)
  650. data += fill_2_hexByte(hex(int(infoDict['power2'])), 4)
  651. data += fill_2_hexByte(hex(int(infoDict['power2ratio']) / 10), 2)
  652. data += fill_2_hexByte(hex(int(infoDict['power3'])), 4)
  653. data += fill_2_hexByte(hex(int(infoDict['power3ratio']) / 10), 2)
  654. if infoDict.has_key('power4'):
  655. data += fill_2_hexByte(hex(int(infoDict['power4'])), 4)
  656. data += fill_2_hexByte(hex(int(infoDict['power4ratio']) / 10), 2)
  657. data += fill_2_hexByte(hex(int(infoDict['power5'])), 4)
  658. data += fill_2_hexByte(hex(int(infoDict['power5ratio']) / 10), 2)
  659. devInfo = MessageSender.send(device = self.device,
  660. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  661. payload = {
  662. 'IMEI': self._device['devNo'], "funCode": '14', 'data': data
  663. })
  664. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  665. if devInfo['rst'] == -1:
  666. raise ServiceException(
  667. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  668. elif devInfo['rst'] == 1:
  669. raise ServiceException(
  670. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  671. # 给实体卡充值
  672. def recharge_card(self, cardNo, money, orderNo = None):
  673. # type:(str,RMB,Optional[str])->(dict, RMB)
  674. try:
  675. data = 'EE1012{}'.format(make_six_bytes_session_id())
  676. cardNo = fill_2_hexByte(hex(int(cardNo)), 8)
  677. data += cardNo + fill_2_hexByte(hex(int(money * 10)), 4) + '0001'
  678. devInfo = MessageSender.send(device = self.device,
  679. cmd = DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_SYNC,
  680. payload = {
  681. 'IMEI': self._device['devNo'], 'data': data, 'funCode': '12'
  682. }, timeout = MQTT_TIMEOUT.LONGEST)
  683. if devInfo['rst'] != 0:
  684. if devInfo['rst'] == ErrorCode.DEVICE_CONN_FAIL:
  685. # 离线无法判断是否成功, 认为充值成功, 走售后解决
  686. return {
  687. 'result': ErrorCode.DEVICE_CONN_FAIL,
  688. 'description': u'当前充电桩正在玩命找网络,请您稍候再试'
  689. }, None
  690. elif devInfo['rst'] == ErrorCode.BOARD_UART_TIMEOUT:
  691. return {
  692. 'result': ErrorCode.BOARD_UART_TIMEOUT,
  693. 'description': u'当前充电桩忙,无响应,请您稍候再试'
  694. }, None
  695. else:
  696. return {
  697. 'result': devInfo['rst'],
  698. 'description': u'系统异常'
  699. }, None
  700. resultData = devInfo['data']
  701. if resultData[4:6] != '16':
  702. return {
  703. 'result': ErrorCode.PARAMETER_ERROR_TO_BOX,
  704. 'description': u'充值返回报文命令码不为16'
  705. }, None
  706. balance = RMB(int(resultData[26:30], 16)) * Decimal('0.1')
  707. result = True if resultData[34:36] == '01' else False
  708. if result:
  709. return {
  710. 'result': ErrorCode.SUCCESS,
  711. 'description': ''
  712. }, balance
  713. else:
  714. return {
  715. 'result': ErrorCode.IC_RECHARGE_FAIL,
  716. 'description': u'充值失败'
  717. }, balance
  718. except Exception as e:
  719. logger.exception(e)
  720. return {
  721. 'result': ErrorCode.EXCEPTION,
  722. 'description': e.message
  723. }, None
  724. # 获取IC卡、投币、最大功率设置
  725. def get_freemode_volume_andsoon_config(self):
  726. devInfo = MessageSender.send(device = self.device,
  727. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  728. payload = {
  729. 'IMEI': self._device['devNo'], "funCode": '2A', 'data': '00'
  730. })
  731. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  732. if devInfo['rst'] == -1:
  733. raise ServiceException(
  734. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  735. elif devInfo['rst'] == 1:
  736. raise ServiceException(
  737. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  738. confData = devInfo['data'][18::]
  739. card1Time = int(confData[0:4], 16)
  740. card2Time = int(confData[4:8], 16)
  741. card3Time = int(confData[8:12], 16)
  742. chargeFree = True if confData[12:14] == '01' else False
  743. volume = int(confData[14:16], 16)
  744. FuchongPower = int(confData[16:20], 16)
  745. FuchongTime = int(confData[20:24], 16)
  746. return {'card1Time': card1Time, 'card2Time': card2Time, 'card3Time': card3Time,
  747. 'chargeFree': chargeFree, 'volume': volume, 'FuchongPower': FuchongPower,
  748. 'FuchongTime': FuchongTime}
  749. def get_mcu_version(self):
  750. devInfo = MessageSender.send(device = self.device,
  751. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  752. payload = {
  753. 'IMEI': self._device['devNo'], "funCode": '25', 'data': '00'
  754. })
  755. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  756. if devInfo['rst'] == -1:
  757. raise ServiceException(
  758. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  759. elif devInfo['rst'] == 1:
  760. raise ServiceException(
  761. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  762. confData = devInfo['data'][18::]
  763. mcuVersion = int(confData[0:4], 16)
  764. return {'mcuVersion': mcuVersion}
  765. def set_freemode_volume_config(self, infoDict):
  766. data = '01' if infoDict['chargeFree'] else '00'
  767. data += fill_2_hexByte(hex(int(infoDict.get('volume', 5))), 2)
  768. devInfo = MessageSender.send(device = self.device,
  769. cmd = self.make_random_cmdcode(),
  770. payload = {
  771. 'IMEI': self._device['devNo'], "funCode": '27', 'data': data
  772. })
  773. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  774. if devInfo['rst'] == -1:
  775. raise ServiceException(
  776. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  777. elif devInfo['rst'] == 1:
  778. raise ServiceException(
  779. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试'})
  780. def set_fuchong_config(self, infoDict):
  781. data = fill_2_hexByte(hex(int(infoDict['fuchongPower'])), 4)
  782. data += fill_2_hexByte(hex(int(infoDict['fuchongTime'])), 4)
  783. devInfo = MessageSender.send(device = self.device,
  784. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  785. payload = {
  786. 'IMEI': self._device['devNo'], "funCode": '28', 'data': data
  787. })
  788. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  789. if devInfo['rst'] == -1:
  790. raise ServiceException(
  791. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  792. elif devInfo['rst'] == 1:
  793. raise ServiceException(
  794. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试'})
  795. def set_card_time_config(self, infoDict):
  796. data = fill_2_hexByte(hex(int(infoDict['card1Time'])), 4)
  797. data += fill_2_hexByte(hex(int(infoDict['card2Time'])), 4)
  798. data += fill_2_hexByte(hex(int(infoDict['card3Time'])), 4)
  799. devInfo = MessageSender.send(device = self.device,
  800. cmd = self.make_random_cmdcode(),
  801. payload = {
  802. 'IMEI': self._device['devNo'], "funCode": '29', 'data': data
  803. })
  804. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  805. if devInfo['rst'] == -1:
  806. raise ServiceException(
  807. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  808. elif devInfo['rst'] == 1:
  809. raise ServiceException(
  810. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试'})
  811. def set_device_function(self, request, lastSetConf):
  812. if request.POST.has_key('putCoins'):
  813. # putCoins = True if request.POST.get('putCoins') == 'true' else False
  814. putCoins = request.POST.get("putCoins", False)
  815. lastSetConf.update({'putCoins': putCoins})
  816. self.set_coin_card_enable(lastSetConf)
  817. if request.POST.has_key('icCard'):
  818. # icCard = True if request.POST.get('icCard') == 'true' else False
  819. icCard = request.POST.get("icCard")
  820. lastSetConf.update({'icCard': icCard})
  821. self.set_coin_card_enable(lastSetConf)
  822. if request.POST.has_key('autoStop'):
  823. # autoStop = True if request.POST.get('autoStop') == 'true' else False
  824. autoStop = request.POST.get("autoStop", False)
  825. lastSetConf.update({'autoStop': autoStop})
  826. self.set_fullstop_cardrefund(lastSetConf)
  827. if request.POST.has_key('cardRefund'):
  828. # cardRefund = True if request.POST.get('cardRefund') == 'true' else False
  829. cardRefund = request.POST.get("cardRefund", False)
  830. lastSetConf.update({'cardRefund': cardRefund})
  831. self.set_fullstop_cardrefund(lastSetConf)
  832. if request.POST.has_key('chargeFree'):
  833. # chargeFree = True if request.POST.get('chargeFree') == 'true' else False
  834. chargeFree = request.POST.get("chargeFree", False)
  835. lastSetConf.update({'chargeFree': chargeFree})
  836. self.set_freemode_volume_config(lastSetConf)
  837. def set_device_function_param(self, request, lastSetConf):
  838. if request.POST.has_key('maxPower') and request.POST.has_key('icMoney'):
  839. lastSetConf.update({'maxPower': int(request.POST.get('maxPower', 0))})
  840. lastSetConf.update({'icMoney': int(request.POST.get('icMoney', 0))})
  841. self.set_IC_coin_power_config(lastSetConf)
  842. if request.POST.has_key('time1') and request.POST.has_key('time2') and request.POST.has_key('time3'):
  843. lastSetConf.update({'time1': int(request.POST.get('time1', 0))})
  844. lastSetConf.update({'time2': int(request.POST.get('time2', 0))})
  845. lastSetConf.update({'time3': int(request.POST.get('time3', 0))})
  846. self.set_IC_coin_power_config(lastSetConf)
  847. if request.POST.has_key('power1'):
  848. lastSetConf.update({'power1': int(request.POST.get('power1', 0))})
  849. lastSetConf.update({'power2': int(request.POST.get('power2', 0))})
  850. lastSetConf.update({'power3': int(request.POST.get('power3', 0))})
  851. lastSetConf.update({'power4': int(request.POST.get('power4', 0))})
  852. lastSetConf.update({'power5': int(request.POST.get('power5', 0))})
  853. self.set_gear_conf(lastSetConf)
  854. if request.POST.has_key('power1ratio'):
  855. lastSetConf.update({'power1ratio': int(request.POST.get('power1ratio', 0))})
  856. lastSetConf.update({'power2ratio': int(request.POST.get('power2ratio', 0))})
  857. lastSetConf.update({'power3ratio': int(request.POST.get('power3ratio', 0))})
  858. lastSetConf.update({'power4ratio': int(request.POST.get('power4ratio', 0))})
  859. lastSetConf.update({'power5ratio': int(request.POST.get('power5ratio', 0))})
  860. self.set_gear_conf(lastSetConf)
  861. if request.POST.has_key('card1Time') and request.POST.has_key('card2Time') and request.POST.has_key(
  862. 'card3Time'):
  863. lastSetConf.update({'card1Time': int(request.POST.get('card1Time', 0))})
  864. lastSetConf.update({'card2Time': int(request.POST.get('card2Time', 0))})
  865. lastSetConf.update({'card3Time': int(request.POST.get('card3Time', 0))})
  866. self.set_card_time_config(lastSetConf)
  867. if request.POST.has_key('fuchongPower') and request.POST.has_key('fuChongTime'):
  868. lastSetConf.update({'fuchongPower': int(request.POST.get('fuchongPower', 0))})
  869. lastSetConf.update({'fuChongTime': int(request.POST.get('fuChongTime', 0))})
  870. self.set_fuchong_config(lastSetConf)