anxin.py 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
  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 django.core.cache import caches
  8. from apilib.utils_datetime import timestamp_to_dt
  9. from apps.web.agent.models import Agent
  10. from apps.web.constant import MQTT_TIMEOUT, DeviceCmdCode, Const, ErrorCode
  11. from apps.web.core.adapter.base import SmartBox, fill_2_hexByte
  12. from apps.web.core.exceptions import ServiceException
  13. from apps.web.core.networking import MessageSender
  14. from apps.web.dealer.models import Dealer
  15. from apps.web.device.models import Device, Group, DeviceType
  16. from apilib.monetary import RMB
  17. from apps.web.core.device_define.jndz import CMD_CODE
  18. logger = logging.getLogger(__name__)
  19. class ChargingAnxinBox(SmartBox):
  20. def __init__(self, device):
  21. super(ChargingAnxinBox, self).__init__(device)
  22. def translate_funcode(self, funCode):
  23. funCodeDict = {
  24. '01': u'获取端口数量',
  25. '02': u'获取端口数据',
  26. '14': u'移动支付',
  27. '07': u'获取刷卡投币统计数据',
  28. '15': u'获取设备端口详情',
  29. '0F': u'获取端口状态',
  30. '0C': u'端口锁操作',
  31. '0D': u'端口开关',
  32. '1E': u'获取设备设置',
  33. '18': u'设置设备参数',
  34. '10': u'回复卡余额',
  35. }
  36. return funCodeDict.get(funCode, '')
  37. def translate_event_cmdcode(self, cmdCode):
  38. cmdDict = {
  39. '06': u'充电结束',
  40. '16': u'充电结束',
  41. '0A': u'故障',
  42. '10': u'刷卡上报',
  43. '20': u'启动设备',
  44. }
  45. return cmdDict.get(cmdCode, '')
  46. def test(self, coins):
  47. hexPort = fill_2_hexByte(1, 2)
  48. hexCoins = fill_2_hexByte(hex(1))
  49. hexTime = fill_2_hexByte(hex(60))
  50. devInfo = MessageSender.send(device = self.device,
  51. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  52. payload = {
  53. 'IMEI': self._device['devNo'],
  54. "funCode": '14',
  55. 'data': hexPort + hexCoins + hexTime
  56. })
  57. return devInfo
  58. def stop(self, port = None):
  59. infoDict = self.stop_charging_port(port)
  60. infoDict['remainder_time'] = infoDict['leftTime']
  61. return infoDict
  62. def start_device(self, package, openId, attachParas):
  63. if attachParas is None:
  64. raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路、电池类型信息'})
  65. if not attachParas.has_key('chargeIndex'):
  66. raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路'})
  67. devConf = caches['devmgr'].get('settingConf_%s' % (self._device['devNo']))
  68. if devConf is None:
  69. conf = self.get_dev_setting()
  70. caches['devmgr'].set('settingConf_%s' % (self._device['devNo']), conf, 24 * 3600)
  71. coinElec = conf['coinElec']
  72. refundProtection = conf.get('refundProtection', 0)
  73. else:
  74. coinElec = devConf['coinElec']
  75. refundProtection = devConf.get('refundProtection', 0)
  76. price = float(package['price'])
  77. port = hex(int(attachParas['chargeIndex']))
  78. hexPort = fill_2_hexByte(port, 2)
  79. coins = float(package['coins'])
  80. hexCoins = fill_2_hexByte(hex(int(coins * 10))) # 注意单位是角
  81. needElec = round((coins * coinElec) / 10.0, 2) # 单位是0.01度电(配置中每次投币的最大用电量单位却是0.1度电,这里要乘以10)
  82. hexElec = fill_2_hexByte(hex(int(needElec * 100)), 4)
  83. unit = package.get('unit', u'分钟')
  84. needTime = int(package['time'])
  85. if unit in [u'分钟', u'小时', u'天']:
  86. billingType = 'time'
  87. if unit == u'小时':
  88. needTime = int(package['time']) * 60
  89. elif unit == u'天':
  90. needTime = int(package['time']) * 1440
  91. hexTime = fill_2_hexByte(hex(needTime))
  92. elif unit == u'度':
  93. billingType = 'elec'
  94. needElec = round((package['time']), 2)
  95. hexElec = fill_2_hexByte(hex(int(needElec * 100)), 4)
  96. hexTime = fill_2_hexByte(hex(12 * 60))
  97. else:
  98. billingType = 'elec'
  99. devObj = Device.objects.get(devNo = self._device['devNo'])
  100. elecFee = float(devObj.otherConf.get('elecFee', 0.9))
  101. needElec = round(min(float(coins / elecFee), needElec), 2)
  102. hexElec = fill_2_hexByte(hex(int(needElec * 100)), 4)
  103. hexTime = fill_2_hexByte(hex(12 * 60))
  104. # 在启动设备前,如果可能是续充,需要获取下设备端口状态,便于后面核实。防止结束报文丢包导致数据不准确
  105. # 重新把设备上的状态取回来,可以保证needTime数据不出错,正在服务显示也不会有问题
  106. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  107. lastPortInfo = ctrInfo.get(str(attachParas['chargeIndex']), None)
  108. if (lastPortInfo is not None) and lastPortInfo.get('status', '') == Const.DEV_WORK_STATUS_WORKING:
  109. self.get_port_status_from_dev()
  110. devInfo = MessageSender.send(device = self.device,
  111. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  112. payload = {
  113. 'IMEI': self._device['devNo'],
  114. "funCode": '14',
  115. 'data': hexPort + hexCoins + hexTime + hexElec
  116. }, timeout = MQTT_TIMEOUT.START_DEVICE)
  117. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  118. if devInfo['rst'] == -1:
  119. raise ServiceException(
  120. {
  121. 'result': 2,
  122. 'description': u'充电桩正在玩命找网络,您的金币还在,重试不需要重新付款,建议您试试旁边其他设备,或者稍后再试哦'
  123. }
  124. )
  125. elif devInfo['rst'] == 1:
  126. raise ServiceException(
  127. {
  128. 'result': 2,
  129. 'description': u'充电桩正在忙,无响应,您的金币还在,请试试其他线路,或者请稍后再试哦'
  130. }
  131. )
  132. data = devInfo['data'][6::]
  133. if data[0:2] == '01': # 表示成功
  134. pass
  135. else:
  136. raise ServiceException({'result': 2, 'description': u'获取端口数据失败,请重试看能否解决'})
  137. usePort = int(attachParas['chargeIndex'])
  138. result = data[4:6]
  139. if result == '01': # 成功
  140. pass
  141. elif result == '0B':
  142. newValue = {str(usePort): {'status': Const.DEV_WORK_STATUS_FAULT, 'statusInfo': u'充电站故障'}}
  143. Device.update_dev_control_cache(self._device['devNo'], newValue)
  144. raise ServiceException({'result': 2, 'description': u'充电站故障'})
  145. elif result == '0C':
  146. newValue = {str(usePort): {'status': Const.DEV_WORK_STATUS_WORKING, 'statusInfo': u''}}
  147. Device.update_dev_control_cache(self._device['devNo'], newValue)
  148. raise ServiceException({'result': 2, 'description': u'该端口正在使用中'})
  149. start_ts = int(time.time())
  150. portDict = {
  151. 'startTime': timestamp_to_dt(start_ts).strftime('%Y-%m-%d %H:%M:%S'),
  152. 'status': Const.DEV_WORK_STATUS_WORKING,
  153. 'coins': float(coins),
  154. 'isStart': True,
  155. 'price': price,
  156. 'openId': openId,
  157. 'refunded': False,
  158. 'billingType': billingType,
  159. 'refundProtection': refundProtection,
  160. 'vCardId': self._vcard_id
  161. }
  162. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  163. lastPortInfo = ctrInfo.get(str(usePort), None)
  164. if lastPortInfo is not None and lastPortInfo.get('status', '') == Const.DEV_WORK_STATUS_WORKING:
  165. if lastPortInfo.has_key('coins'):
  166. portDict['coins'] = float(coins) + lastPortInfo['coins']
  167. if lastPortInfo.has_key('price'):
  168. portDict['price'] = price + lastPortInfo['price']
  169. if unit in [u'分钟', u'小时', u'天']:
  170. portDict.update({'needTime': needTime, 'needElec': needElec})
  171. if (lastPortInfo is not None) and lastPortInfo.get('status', '') == Const.DEV_WORK_STATUS_WORKING:
  172. if lastPortInfo.has_key('needTime'):
  173. portDict['needTime'] = needTime + lastPortInfo['needTime']
  174. if lastPortInfo.has_key('needElec'):
  175. portDict['needElec'] = needElec + lastPortInfo['needElec']
  176. finishedTime = start_ts + portDict['needTime'] * 60
  177. else:
  178. portDict.update({'needElec': needElec})
  179. if (lastPortInfo is not None) and lastPortInfo.has_key('needElec') and lastPortInfo.get('status','') == Const.DEV_WORK_STATUS_WORKING:
  180. portDict['needElec'] = needElec + lastPortInfo['needElec']
  181. finishedTime = start_ts + 12 * 60 * 60
  182. portDict.update({'finishedTime': finishedTime})
  183. if 'orderNo' in attachParas:
  184. portDict.update({'orderNo': attachParas['orderNo']})
  185. Device.update_dev_control_cache(self._device['devNo'], {str(usePort): portDict})
  186. devInfo['finishedTime'] = finishedTime
  187. return devInfo
  188. def get_card_charge_result_from_data(self, data):
  189. if data[4:6] != '17':
  190. logger.info('receive wrong card charge result data = %s' % data)
  191. return False, RMB(0)
  192. balance = RMB(1) * (int(data[16:20], 16) / 10.0)
  193. result = True if data[6:8] == '01' and data[22:24] == '01' else False
  194. return result, balance
  195. def get_elec_meter(self):
  196. devInfo = MessageSender.send(device = self.device,
  197. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  198. payload = {
  199. 'IMEI': self._device['devNo'], "funCode": '22', 'data': '00'
  200. })
  201. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  202. if devInfo['rst'] == -1:
  203. raise ServiceException(
  204. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  205. elif devInfo['rst'] == 1:
  206. raise ServiceException(
  207. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  208. data = devInfo['data'][6::]
  209. meter = int(data[2:10], 16) / 100.0
  210. return {'meter': meter}
  211. def analyze_event_data(self, data):
  212. cmdCode = data[4:6]
  213. if cmdCode == CMD_CODE.DEVICE_SUBMIT_CHARGE_FINISHED_06:
  214. port = int(data[8:10], 16)
  215. leftTime = int(data[10:14], 16)
  216. reasonCode = data[14:16]
  217. desc_map = {
  218. '00': u'购买的充电时间或电量用完了。',
  219. '01': u'可能是插头被拔掉,或者电瓶已经充满。系统判断为异常断电,由于电瓶车充电器种类繁多,可能存在误差。如有问题,请您及时联系商家协助解决问题并恢复充电。',
  220. '02': u'恭喜您!电池已经充满电!',
  221. '0B': u'设备或端口出现问题,为了安全起见,被迫停止工作。建议您根据已经充电的时间评估是否需要到现场换到其他端口充电。'
  222. }
  223. return {
  224. 'status': Const.DEV_WORK_STATUS_IDLE,
  225. 'cmdCode': cmdCode,
  226. 'port': port,
  227. 'leftTime': leftTime,
  228. 'reason': desc_map[reasonCode],
  229. 'reasonCode': reasonCode
  230. }
  231. #: (高版本的) 提交充电结束状态 (16)
  232. elif cmdCode == CMD_CODE.DEVICE_SUBMIT_CHARGE_FINISHED_v2_16:
  233. port = int(data[8:10], 16)
  234. leftTime = int(data[10:14], 16)
  235. elec = int(data[14:18], 16) / 100.0
  236. reasonCode = data[18:20]
  237. desc_map = {
  238. '00': u'购买的充电时间或电量用完了。',
  239. '01': u'可能是插头被拔掉,或者电瓶已经充满。系统判断为异常断电,由于电瓶车充电器种类繁多,可能存在误差。如有问题,请您及时联系商家协助解决问题并恢复充电。',
  240. '02': u'恭喜您!电池已经充满电!',
  241. '03': u'警告!您的电池超功率,已经停止充电,为了公共安全,不建议您在该充电桩充电!提醒您,为了安全大功率的电池不要放入楼道、室内等位置充电哦',
  242. '04': u'远程断电。',
  243. '0B': u'设备或端口出现问题,为了安全起见,被迫停止工作。建议您根据已经充电的时间评估是否需要到现场换到其他端口充电。'
  244. }
  245. desc = desc_map.get(reasonCode, u'电池没有充满!原因未知。')
  246. return {
  247. 'status': Const.DEV_WORK_STATUS_IDLE,
  248. 'cmdCode': cmdCode,
  249. 'port': port,
  250. 'leftTime': leftTime,
  251. 'elec': elec,
  252. 'reason': desc,
  253. 'reasonCode': reasonCode
  254. }
  255. #: 上传设备故障
  256. elif cmdCode == CMD_CODE.DEVICE_SUBMIT_DEVICE_FAULT_0A:
  257. port = int(data[8:10], 16)
  258. errCode = int(data[10:14], 16)
  259. return {
  260. 'status': Const.DEV_WORK_STATUS_FAULT,
  261. 'statusInfo': u'设备故障',
  262. 'cmdCode': cmdCode,
  263. 'port': port,
  264. 'FaultCode': errCode
  265. }
  266. # 用户刷卡上报的信息
  267. #: 在线卡上传卡号,预扣费
  268. #: 示例:
  269. #: CARD_ID CARD_CST CARD_OPE
  270. #: 0x00000000 0x00 0x00
  271. #: 参数
  272. #: CARD_ID : IC 卡卡号
  273. #: CARD_SURP: 卡片需要改变的金额信息(以角为单位)
  274. #: CARD_OPE: 0x00 是扣费(减少),0x01 是充值(增加)。
  275. #: 回复
  276. #: RES: 0x00,表示扣费成功,0x01 表示余额不足,0x02 表示非法卡。
  277. #: CARD_SURP: 表示卡余额(以角为单位)。
  278. elif cmdCode == CMD_CODE.SWIPE_CARD_10:
  279. group = Group.get_group(self._device['groupId'])
  280. dealer = Dealer.get_dealer(group['ownerId'])
  281. agent = Agent.objects(id=dealer['agentId']).first()
  282. device = Device.objects(devNo=self._device['devNo']).first()
  283. devType = DeviceType.objects(id=device['devType']['id']).first()
  284. cardNo = str(int(data[8:16], 16))
  285. # 兼容以前的老代码, 先走以前的老代码, 如果有新代码再走新的.
  286. if agent is not None and 'cardNoReverse' in agent.features:
  287. cardData = data[14:16] + data[12:14] + data[10:12] + data[8:10]
  288. cardNo = str(int(cardData, 16))
  289. if 'cardNoReverse' in devType.features:
  290. if devType.features['cardNoReverse'] is True:
  291. cardData = data[14:16] + data[12:14] + data[10:12] + data[8:10]
  292. cardNo = str(int(cardData, 16))
  293. else:
  294. cardNo = str(int(data[8:16], 16))
  295. else:
  296. pass
  297. #: 卡里的余额,以角为单位
  298. preFee = int(data[16:18], 16) / 10.0
  299. #: 操作符 00(增加) | 01(减少)
  300. oper = data[18:20]
  301. return {'cardNo': cardNo, 'preFee': preFee, 'cmdCode': cmdCode, 'oper': oper}
  302. #: 上报投币打开的信息
  303. elif cmdCode == CMD_CODE.DEVICE_SUBMIT_OFFLINE_COINS_20: # 启动设备
  304. port = int(data[8:10], 16)
  305. needTime = int(data[10:14], 16)
  306. elec = int(data[14:18], 16) / 100.0
  307. consumeTypeTemp = data[18:20]
  308. if consumeTypeTemp == '00':
  309. consumeType = 'coin'
  310. elif consumeTypeTemp == '01':
  311. consumeType = 'card'
  312. elif consumeTypeTemp == '03':
  313. consumeType = 'server'
  314. else:
  315. consumeType = 'other'
  316. money = int(data[20:22], 16) / 10.0
  317. return {
  318. 'cmdCode': cmdCode,
  319. 'port': port,
  320. 'needTime': needTime,
  321. 'elec': elec,
  322. 'consumeType': consumeType,
  323. 'coins': money
  324. }
  325. elif cmdCode == '11': # IC卡扣费、退费上报事件
  326. cardNo = str(int(data[8:16], 16))
  327. preFee = int(data[16:18], 16) / 10.0
  328. balance = int(data[18:22], 16) / 10.0
  329. port = int(data[26:28], 16)
  330. oper = data[28:30]
  331. return {'cardNo': cardNo, 'preFee': preFee, 'cmdCode': cmdCode, 'balance': balance, 'cardType': 'IC',
  332. 'port': port, 'oper': oper}
  333. elif cmdCode == '12': # IC卡圈存
  334. cardNo = str(int(data[8:16], 16))
  335. balance = int(data[16:20], 16) * 10.0
  336. return {'cmdCode': cmdCode, 'balance': balance, 'cardType': 'IC', 'cardNo': cardNo}
  337. return super(ChargingAnxinBox, self).analyze_event_data(data)
  338. def get_dev_consume_count(self):
  339. devInfo = MessageSender.send(device = self.device,
  340. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  341. payload = {
  342. 'IMEI': self._device['devNo'], "funCode": '07', 'data': '00'
  343. })
  344. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  345. if devInfo['rst'] == -1:
  346. raise ServiceException(
  347. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  348. elif devInfo['rst'] == 1:
  349. raise ServiceException(
  350. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  351. data = devInfo['data'][6::]
  352. if data[0:2] == '01': # 表示成功
  353. pass
  354. else:
  355. raise ServiceException({'result': 2, 'description': u'获取统计数据失败,请重试看能否解决'})
  356. cardFee = int(data[2:6], 16) / 10.0 # 以角为单位
  357. coinFee = int(data[6:10], 16) # 以元为单位
  358. return {'cardFee': cardFee, 'coinFee': coinFee}
  359. def get_port_info(self, line):
  360. data = fill_2_hexByte(hex(int(line)), 2)
  361. devInfo = MessageSender.send(device = self.device,
  362. cmd = self.make_random_cmdcode(),
  363. payload = {
  364. 'IMEI': self._device['devNo'], "funCode": '15', 'data': data
  365. })
  366. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  367. if devInfo['rst'] == -1:
  368. raise ServiceException(
  369. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  370. elif devInfo['rst'] == 1:
  371. raise ServiceException(
  372. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  373. data = devInfo['data'][6::]
  374. if data[0:2] == '01': # 表示成功
  375. pass
  376. else:
  377. raise ServiceException({'result': 2, 'description': u'获取统计数据失败,请重试看能否解决'})
  378. leftTime = int(data[4:8], 16)
  379. if data[8:12] == 'FFFF':
  380. power = 0
  381. else:
  382. power = int(data[8:12], 16)
  383. if data[12:16] == 'FFFF':
  384. elec = 0
  385. else:
  386. elec = int(data[12:16], 16)
  387. if data[16:20] == 'FFFF':
  388. surp = 0
  389. else:
  390. surp = int(data[16:20], 16)
  391. return {'port': line, 'leftTime': leftTime, 'power': power, 'surp': surp}
  392. def get_port_status(self, force = False):
  393. if force:
  394. return self.get_port_status_from_dev()
  395. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  396. statusDict = {}
  397. if not ctrInfo.has_key('allPorts'):
  398. self.get_port_status_from_dev()
  399. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  400. allPorts = ctrInfo.get('allPorts', 10)
  401. for ii in range(allPorts):
  402. tempDict = ctrInfo.get(str(ii + 1), {})
  403. if tempDict.has_key('status'):
  404. statusDict[str(ii + 1)] = {'status': tempDict.get('status')}
  405. elif tempDict.has_key('isStart'):
  406. if tempDict['isStart']:
  407. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_WORKING}
  408. else:
  409. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  410. else:
  411. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  412. allPorts, usedPorts, usePorts = self.get_port_static_info(statusDict)
  413. Device.update_dev_control_cache(self._device['devNo'],
  414. {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  415. return statusDict
  416. def get_port_status_from_dev(self):
  417. devInfo = MessageSender.send(device = self.device,
  418. cmd = self.make_random_cmdcode(),
  419. payload = {
  420. 'IMEI': self._device['devNo'], "funCode": '0F', 'data': '00'
  421. })
  422. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  423. if devInfo['rst'] == -1:
  424. raise ServiceException(
  425. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  426. elif devInfo['rst'] == 1:
  427. raise ServiceException(
  428. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  429. data = devInfo['data'][6::]
  430. if data[0:2] == '01': # 表示成功
  431. pass
  432. else:
  433. raise ServiceException({'result': 2, 'description': u'获取统计数据失败,请重试看能否解决'})
  434. result = {}
  435. portNum = int(data[2:4], 16)
  436. portData = data[4::]
  437. ii = 0
  438. while ii < portNum:
  439. port = int(portData[ii * 4:ii * 4 + 2], 16)
  440. statusTemp = portData[ii * 4 + 2:ii * 4 + 4]
  441. if statusTemp == '01':
  442. status = {'status': Const.DEV_WORK_STATUS_IDLE}
  443. elif statusTemp == '02':
  444. status = {'status': Const.DEV_WORK_STATUS_WORKING}
  445. elif statusTemp == '03':
  446. status = {'status': Const.DEV_WORK_STATUS_FORBIDDEN}
  447. elif statusTemp == '04':
  448. status = {'status': Const.DEV_WORK_STATUS_FAULT}
  449. ii += 1
  450. result[str(port)] = status
  451. allPorts, usedPorts, usePorts = self.get_port_static_info(result)
  452. Device.update_dev_control_cache(self._device['devNo'],
  453. {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  454. # 这里存在多线程更新缓存的场景,可能性比较低,但是必须先取出来,然后逐个更新状态,然后再记录缓存
  455. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  456. for strPort, info in result.items():
  457. if ctrInfo.has_key(strPort):
  458. ctrInfo[strPort].update({'status': info['status']})
  459. else:
  460. ctrInfo[strPort] = info
  461. Device.update_dev_control_cache(self._device['devNo'], ctrInfo)
  462. return result
  463. def lock_unlock_port(self, port, lock = True):
  464. lockStr = '00' if lock else '01'
  465. portStr = fill_2_hexByte(hex(int(port)), 2)
  466. devInfo = MessageSender.send(device = self.device,
  467. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  468. payload = {
  469. 'IMEI': self._device['devNo'],
  470. "funCode": '0C',
  471. 'data': portStr + lockStr
  472. })
  473. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  474. if devInfo['rst'] == -1:
  475. raise ServiceException(
  476. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  477. elif devInfo['rst'] == 1:
  478. raise ServiceException(
  479. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  480. data = devInfo['data'][6::]
  481. if data[0:2] == '01': # 表示成功
  482. pass
  483. else:
  484. raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'})
  485. if lock:
  486. Device.update_dev_control_cache(self._device['devNo'],
  487. {str(port): {'status': Const.DEV_WORK_STATUS_FORBIDDEN}})
  488. else:
  489. Device.update_dev_control_cache(self._device['devNo'], {str(port): {'status': Const.DEV_WORK_STATUS_IDLE}})
  490. def active_deactive_port(self, port, active):
  491. if not active:
  492. self.stop_charging_port(port)
  493. devInfo = Device.get_dev_control_cache(self._device['devNo'])
  494. portCtrInfo = devInfo.get(str(port), {})
  495. portCtrInfo.update({'isStart': False, 'status': Const.DEV_WORK_STATUS_IDLE,
  496. 'endTime': datetime.datetime.now().strftime(Const.DATETIME_FMT)})
  497. newValue = {str(port): portCtrInfo}
  498. Device.update_dev_control_cache(self._device['devNo'], newValue)
  499. else:
  500. raise ServiceException({'result': 2, 'description': u'此设备不支持直接打开端口'})
  501. def stop_charging_port(self, port):
  502. portStr = fill_2_hexByte(hex(int(port)), 2)
  503. devInfo = MessageSender.send(device = self.device,
  504. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  505. payload = {
  506. 'IMEI': self._device['devNo'], "funCode": '0D', 'data': portStr + '00'
  507. })
  508. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  509. if devInfo['rst'] == -1:
  510. raise ServiceException(
  511. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  512. elif devInfo['rst'] == 1:
  513. raise ServiceException(
  514. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  515. data = devInfo['data'][6::]
  516. if data[0:2] == '01': # 表示成功
  517. pass
  518. else:
  519. raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'})
  520. port = int(data[2:4], 16)
  521. leftTime = int(data[4:8], 16)
  522. return {'port': port, 'leftTime': leftTime}
  523. def get_dev_setting(self):
  524. devInfo = MessageSender.send(device = self.device,
  525. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  526. payload = {
  527. 'IMEI': self._device['devNo'], "funCode": '1E', 'data': '00'
  528. })
  529. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  530. if devInfo['rst'] == -1:
  531. raise ServiceException(
  532. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  533. elif devInfo['rst'] == 1:
  534. raise ServiceException(
  535. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  536. data = devInfo['data'][6::]
  537. if data[0:2] == '01': # 表示成功
  538. pass
  539. else:
  540. raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'})
  541. confData = data[2:-2]
  542. coinMin = int(confData[0:4], 16)
  543. cardMin = int(confData[4:8], 16)
  544. coinElec = int(confData[8:10], 16)
  545. cardElec = int(confData[10:12], 16)
  546. cst = int(confData[12:14], 16)
  547. powerMax1 = int(confData[14:18], 16)
  548. powerMax2 = int(confData[18:22], 16)
  549. powerMax3 = int(confData[22:26], 16)
  550. powerMax4 = int(confData[26:30], 16)
  551. power2Ti = int(confData[30:32], 16)
  552. power3Ti = int(confData[32:34], 16)
  553. power4Ti = int(confData[34:36], 16)
  554. spRecMon = int(confData[36:38], 16)
  555. spFullEmpty = int(confData[38:40], 16)
  556. fullPowerMin = int(confData[40:42], 16)
  557. fullChargeTime = int(confData[42:44], 16)
  558. elecTimeFirst = int(confData[44:46], 16)
  559. dev = Device.objects.get(devNo = self._device['devNo'])
  560. billingType = dev.otherConf.get('billingType', 'time')
  561. elecFee = dev.otherConf.get('elecFee', 0.9)
  562. refundProtection = dev.otherConf.get('refundProtection', 0)
  563. resultDict = {
  564. 'coinMin': coinMin,
  565. 'cardMin': cardMin,
  566. 'coinElec': coinElec,
  567. 'cardElec': cardElec,
  568. 'cst': cst,
  569. 'powerMax1': powerMax1,
  570. 'powerMax2': powerMax2,
  571. 'powerMax3': powerMax3,
  572. 'powerMax4': powerMax4,
  573. 'power2Ti': power2Ti,
  574. 'power3Ti': power3Ti,
  575. 'power4Ti': power4Ti,
  576. 'spRecMon': spRecMon,
  577. 'spFullEmpty': spFullEmpty,
  578. 'fullPowerMin': fullPowerMin,
  579. 'fullChargeTime': fullChargeTime,
  580. 'elecTimeFirst': elecTimeFirst,
  581. 'billingType': billingType,
  582. 'elecFee': elecFee,
  583. 'refundProtection': refundProtection
  584. }
  585. consumeInfo = self.get_dev_consume_count()
  586. resultDict.update(consumeInfo)
  587. return resultDict
  588. # 设置设备配置参数
  589. def set_dev_setting(self, setConf):
  590. data = ''
  591. data += fill_2_hexByte(hex(int(setConf['coinMin'])), 4)
  592. data += fill_2_hexByte(hex(int(setConf['cardMin'])), 4)
  593. data += fill_2_hexByte(hex(int(setConf['coinElec'])), 2)
  594. data += fill_2_hexByte(hex(int(setConf['cardElec'])), 2)
  595. data += fill_2_hexByte(hex(int(setConf['cst'])), 2)
  596. data += fill_2_hexByte(hex(int(setConf['powerMax1'])), 4)
  597. data += fill_2_hexByte(hex(int(setConf['powerMax2'])), 4)
  598. data += fill_2_hexByte(hex(int(setConf['powerMax3'])), 4)
  599. data += fill_2_hexByte(hex(int(setConf['powerMax4'])), 4)
  600. data += fill_2_hexByte(hex(int(setConf['power2Ti'])), 2)
  601. data += fill_2_hexByte(hex(int(setConf['power3Ti'])), 2)
  602. data += fill_2_hexByte(hex(int(setConf['power4Ti'])), 2)
  603. data += fill_2_hexByte(hex(int(setConf['spRecMon'])), 2)
  604. data += fill_2_hexByte(hex(int(setConf['spFullEmpty'])), 2)
  605. data += fill_2_hexByte(hex(int(setConf['fullPowerMin'])), 2)
  606. data += fill_2_hexByte(hex(int(setConf['fullChargeTime'])), 2)
  607. data += fill_2_hexByte(hex(int(setConf['elecTimeFirst'])), 2)
  608. devInfo = MessageSender.send(device = self.device,
  609. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  610. payload = {
  611. 'IMEI': self._device['devNo'], "funCode": '18', 'data': data
  612. })
  613. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  614. if devInfo['rst'] == -1:
  615. raise ServiceException(
  616. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  617. elif devInfo['rst'] == 1:
  618. raise ServiceException(
  619. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  620. data = devInfo['data'][6::]
  621. if data[0:2] == '01': # 表示成功
  622. dev = Device.objects.get(devNo = self._device['devNo'])
  623. dev.otherConf.update({'billingType': setConf['billingType']})
  624. dev.otherConf.update({'elecFee': setConf['elecFee']})
  625. dev.otherConf.update({'refundProtection': setConf['refundProtection']})
  626. dev.save()
  627. else:
  628. raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'})
  629. def response_use_card(self, res, balance):
  630. data = '55061001'
  631. data = data + res + fill_2_hexByte(hex(int(balance * 10)), 4)
  632. devInfo = MessageSender.send(device = self.device,
  633. cmd = DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE,
  634. payload = {
  635. 'IMEI': self._device['devNo'], "funCode": '10', 'data': data
  636. })
  637. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  638. if devInfo['rst'] == -1:
  639. raise ServiceException(
  640. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  641. elif devInfo['rst'] == 1: # 等于1的时候,说明服务器和远程模块通讯OK,响应已经收到,不需要异常处理
  642. pass
  643. def set_device_function_param(self, request, lastSetConf):
  644. coinMin = request.POST.get('coinMin', None)
  645. cardMin = request.POST.get('cardMin', None)
  646. coinElec = request.POST.get('coinElec', None)
  647. cardElec = request.POST.get('cardElec', None)
  648. cst = request.POST.get('cst', None)
  649. powerMax1 = request.POST.get('powerMax1', None)
  650. powerMax2 = request.POST.get('powerMax2', None)
  651. powerMax3 = request.POST.get('powerMax3', None)
  652. powerMax4 = request.POST.get('powerMax4', None)
  653. power2Ti = request.POST.get('power2Ti', None)
  654. power3Ti = request.POST.get('power3Ti', None)
  655. power4Ti = request.POST.get('power4Ti', None)
  656. spRecMon = request.POST.get('spRecMon', None)
  657. spFullEmpty = request.POST.get('spFullEmpty', None)
  658. fullPowerMin = request.POST.get('fullPowerMin', None)
  659. fullChargeTime = request.POST.get('fullChargeTime', None)
  660. elecTimeFirst = request.POST.get('elecTimeFirst', None)
  661. billingType = request.POST.get('billingType', 'time')
  662. elecFee = request.POST.get('elecFee', 0.9)
  663. refundProtection = request.POST.get('refundProtection', None)
  664. # 这几个参数是墨小智V3特有的
  665. stWTime = request.POST.get('stWTime', None)
  666. temThre = request.POST.get('temThre', None)
  667. freeUse = request.POST.get('freeUse', None)
  668. if coinMin:
  669. lastSetConf.update({'coinMin': int(coinMin)})
  670. if cardMin:
  671. lastSetConf.update({'cardMin': int(cardMin)})
  672. if coinElec:
  673. lastSetConf.update({'coinElec': int(coinElec)})
  674. if cardElec:
  675. lastSetConf.update({'cardElec': int(cardElec)})
  676. if cst:
  677. lastSetConf.update({'cst': int(cst)})
  678. if powerMax1:
  679. lastSetConf.update({'powerMax1': int(powerMax1)})
  680. if powerMax2:
  681. lastSetConf.update({'powerMax2': int(powerMax2)})
  682. if powerMax3:
  683. lastSetConf.update({'powerMax3': int(powerMax3)})
  684. if powerMax4:
  685. lastSetConf.update({'powerMax4': int(powerMax4)})
  686. if power2Ti:
  687. lastSetConf.update({'power2Ti': int(power2Ti)})
  688. if power3Ti:
  689. lastSetConf.update({'power3Ti': int(power3Ti)})
  690. if power4Ti:
  691. lastSetConf.update({'power4Ti': int(power4Ti)})
  692. if spRecMon:
  693. lastSetConf.update({'spRecMon': int(spRecMon)})
  694. if spFullEmpty:
  695. lastSetConf.update({'spFullEmpty': int(spFullEmpty)})
  696. if fullPowerMin:
  697. lastSetConf.update({'fullPowerMin': int(fullPowerMin)})
  698. if fullChargeTime:
  699. lastSetConf.update({'fullChargeTime': int(fullChargeTime)})
  700. if elecTimeFirst:
  701. lastSetConf.update({'elecTimeFirst': int(elecTimeFirst)})
  702. if billingType:
  703. lastSetConf.update({'billingType': billingType})
  704. if elecFee:
  705. lastSetConf.update({'elecFee': elecFee})
  706. if refundProtection:
  707. lastSetConf.update({'refundProtection': int(refundProtection)})
  708. # 这几个参数是墨小智V3特有的
  709. if stWTime:
  710. lastSetConf.update({'stWTime': int(stWTime)})
  711. if temThre:
  712. lastSetConf.update({'temThre': int(temThre)})
  713. if freeUse:
  714. lastSetConf.update({'freeUse': int(freeUse)})
  715. self.set_dev_setting(lastSetConf)
  716. # 给实体卡充值
  717. def recharge_card(self, cardNo, money, orderNo = None):
  718. try:
  719. data = '550B1201'
  720. cardNo = fill_2_hexByte(hex(int(cardNo)), 8)
  721. data = data + cardNo + fill_2_hexByte(hex(int(money * 10)), 4) + '00CC'
  722. devInfo = MessageSender.send(device = self.device,
  723. cmd = DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_SYNC,
  724. payload = {
  725. 'IMEI': self._device['devNo'], 'data': data, 'funCode': '12'
  726. },
  727. timeout = MQTT_TIMEOUT.LONGEST)
  728. if devInfo['rst'] != 0:
  729. if devInfo['rst'] == ErrorCode.DEVICE_CONN_FAIL:
  730. # 离线无法判断是否成功, 认为充值成功, 走售后解决
  731. return {
  732. 'result': ErrorCode.DEVICE_CONN_FAIL,
  733. 'description': u'当前充电桩正在玩命找网络,请您稍候再试'
  734. }, None
  735. elif devInfo['rst'] == ErrorCode.BOARD_UART_TIMEOUT:
  736. return {
  737. 'result': ErrorCode.BOARD_UART_TIMEOUT,
  738. 'description': u'当前充电桩忙,无响应,请您稍候再试'
  739. }, None
  740. else:
  741. return {
  742. 'result': devInfo['rst'],
  743. 'description': u'系统异常'
  744. }, None
  745. resultData = devInfo['data']
  746. if resultData[4:6] != '17':
  747. return {
  748. 'result': ErrorCode.PARAMETER_ERROR_TO_BOX,
  749. 'description': u'充值返回报文命令码不为17'
  750. }, None
  751. balance = RMB(int(resultData[16:20], 16)) * Decimal('0.1')
  752. if resultData[6:8] == '01' and resultData[22:24] == '01':
  753. return {
  754. 'result': ErrorCode.SUCCESS,
  755. 'description': ''
  756. }, balance
  757. else:
  758. return {
  759. 'result': ErrorCode.IC_RECHARGE_FAIL,
  760. 'description': u'充值失败'
  761. }, None
  762. except Exception as e:
  763. logger.exception(e)
  764. return {
  765. 'result': ErrorCode.EXCEPTION,
  766. 'description': e.message
  767. }, None
  768. def apiGetPortStatusFromAx(self, record):
  769. return self.get_port_status_from_dev()
  770. def apiStopAxPort(self, record):
  771. portStr = record['chargeIndex']
  772. return self.stop_charging_port(portStr)
  773. @property
  774. def isHaveStopEvent(self):
  775. return True