jiuheng_baoxiansi.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. # -*- coding: utf-8 -*-
  2. #!/usr/bin/env python
  3. import datetime
  4. import logging
  5. import time
  6. from django.core.cache import caches
  7. from apilib.utils_datetime import timestamp_to_dt
  8. from apps.web.constant import DeviceCmdCode, Const, MQTT_TIMEOUT
  9. from apps.web.core.adapter.base import SmartBox, fill_2_hexByte
  10. from apps.web.core.exceptions import ServiceException
  11. from apps.web.core.networking import MessageSender
  12. from apps.web.device.models import Device
  13. logger = logging.getLogger(__name__)
  14. class ChargingJNDZBox(SmartBox):
  15. def __init__(self, device):
  16. super(ChargingJNDZBox, self).__init__(device)
  17. def translate_funcode(self,funCode):
  18. funCodeDict = {
  19. '01':u'获取端口数量',
  20. '02':u'获取端口数据',
  21. '07':u'获取刷卡投币统计数据',
  22. '0F':u'获取端口状态',
  23. '0C':u'端口锁操作',
  24. '0D':u'端口开关',
  25. }
  26. return funCodeDict.get(funCode,'')
  27. def translate_event_cmdcode(self,cmdCode):
  28. cmdDict = {
  29. '06':u'充电结束',
  30. }
  31. return cmdDict.get(cmdCode,'')
  32. def test(self, coins):
  33. hexPort = fill_2_hexByte(1,2)
  34. hexCoins = fill_2_hexByte(hex(1))
  35. hexTime = fill_2_hexByte(hex(60))
  36. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  37. {'IMEI': self._device['devNo'], "funCode": '04',
  38. 'data': hexPort + hexCoins + hexTime})
  39. return devInfo
  40. def stop(self,port):
  41. infoDict = self.stop_charging_port(port)
  42. infoDict['remainder_time'] = infoDict['leftTime']
  43. return infoDict
  44. def start_device(self, package, openId, attachParas):
  45. if attachParas is None:
  46. raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路、电池类型信息'})
  47. if not attachParas.has_key('chargeIndex'):
  48. raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路'})
  49. devConf = caches['devmgr'].get('settingConf_%s' % (self._device['devNo']))
  50. if devConf:
  51. refundProtection = devConf.get('refundProtection', 0)
  52. else:
  53. refundProtection = 0
  54. price = float(package['price'])
  55. port = hex(int(attachParas['chargeIndex']))
  56. hexPort = fill_2_hexByte(port,2)
  57. coins = int(package['coins'])
  58. hexCoins = fill_2_hexByte(hex(coins*10))#注意单位是角
  59. unit = package.get('unit',u'分钟')
  60. if unit in [u'分钟',u'小时',u'天']:
  61. needTime = int(package['time'])
  62. if unit == u'小时':
  63. needTime = int(package['time'])*60
  64. elif unit == u'天':
  65. needTime = int(package['time'])*1440
  66. hexTime = fill_2_hexByte(hex(needTime))
  67. else:
  68. raise ServiceException({'result': 2, 'description': u'套餐必须包含时间信息'})
  69. #在启动设备前,如果可能是续充,需要获取下设备端口状态,便于后面核实。防止结束报文丢包导致数据不准确
  70. #重新把设备上的状态取回来,可以保证needTime数据不出错,正在服务显示也不会有问题
  71. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  72. lastPortInfo = ctrInfo.get(str(attachParas['chargeIndex']), None)
  73. if (lastPortInfo is not None) and lastPortInfo.get('status','') == Const.DEV_WORK_STATUS_WORKING:
  74. self.get_port_status_from_dev()
  75. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  76. {'IMEI': self._device['devNo'], "funCode": '04',
  77. 'data': hexPort + hexCoins + hexTime}, timeout = MQTT_TIMEOUT.START_DEVICE)
  78. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  79. if devInfo['rst'] == -1:
  80. raise ServiceException({'result': 2, 'description': u'充电桩正在玩命找网络,您的金币还在,重试不需要重新付款,建议您试试旁边其他设备,或者稍后再试哦'})
  81. elif devInfo['rst'] == 1:
  82. raise ServiceException({'result': 2, 'description': u'充电桩正在忙,无响应,您的金币还在,请试试其他线路,或者请稍后再试哦'})
  83. data = devInfo['data'][6::]
  84. if data[0:2] == '01':#表示成功
  85. pass
  86. else:
  87. raise ServiceException({'result': 2, 'description': u'获取端口数据失败,请重试看能否解决'})
  88. usePort = int(data[2:4],16)
  89. result = data[4:6]
  90. if result == '01':#成功
  91. pass
  92. elif result == '0B':
  93. newValue = {str(usePort):{'status':Const.DEV_WORK_STATUS_FAULT,'statusInfo':u'充电站故障'}}
  94. Device.update_dev_control_cache(self._device['devNo'], newValue)
  95. raise ServiceException({'result': 2, 'description': u'充电站故障'})
  96. elif result == '0C':
  97. newValue = {str(usePort):{'status':Const.DEV_WORK_STATUS_WORKING,'statusInfo':u''}}
  98. Device.update_dev_control_cache(self._device['devNo'], newValue)
  99. raise ServiceException({'result': 2, 'description': u'该端口正在使用中'})
  100. start_timestamp = int(time.time())
  101. portDict = {
  102. 'startTime': timestamp_to_dt(start_timestamp).strftime('%Y-%m-%d %H:%M:%S'),
  103. 'status': Const.DEV_WORK_STATUS_WORKING,
  104. 'coins': float(coins),
  105. 'isStart': True, 'price': price,
  106. 'openId': openId,
  107. 'refunded': False,
  108. 'refundProtection': refundProtection,
  109. 'vCardId': self._vcard_id
  110. }
  111. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  112. lastPortInfo = ctrInfo.get(str(usePort),None)
  113. if lastPortInfo is not None and lastPortInfo.get('status','') == Const.DEV_WORK_STATUS_WORKING:
  114. if lastPortInfo.has_key('coins'):
  115. portDict['coins'] = float(coins) + lastPortInfo['coins']
  116. if lastPortInfo.has_key('price'):
  117. portDict['price'] = price + lastPortInfo['price']
  118. portDict.update({'needTime':needTime})
  119. if (lastPortInfo is not None) and lastPortInfo.has_key('needTime') and lastPortInfo.get('status','') == Const.DEV_WORK_STATUS_WORKING:
  120. portDict['needTime'] = needTime + lastPortInfo['needTime']
  121. if attachParas.has_key('orderNo'):
  122. portDict.update({'orderNo':attachParas['orderNo']})
  123. devInfo['finishedTime'] = start_timestamp + needTime * 60
  124. portDict.update({'finishedTime': devInfo['finishedTime']})
  125. Device.update_dev_control_cache(self._device['devNo'],{str(usePort):portDict})
  126. return devInfo
  127. def analyze_event_data(self, data):
  128. cmdCode = data[4:6]
  129. if cmdCode in ['06']:
  130. port = int(data[8:10],16)
  131. leftTime = int(data[10:14],16)
  132. reason = data[14:16]
  133. if reason == '00':
  134. desc = u'购买的充电时间用完了。'
  135. elif reason == '01':
  136. desc = u'可能是插头被拔掉,或者电瓶已经充满。系统判断为异常断电,由于电瓶车充电器种类繁多,可能存在误差。如有问题,请您及时联系商家协助解决问题并恢复充电。'
  137. elif reason == '02':
  138. desc = u'恭喜您!电池已经充满电!'
  139. elif reason == '0B':
  140. desc = u'设备或端口出现问题,为了安全起见,被迫停止工作。建议您根据已经充电的时间评估是否需要到现场换到其他端口充电。'
  141. return {'status':Const.DEV_WORK_STATUS_IDLE,'cmdCode':cmdCode,'port':port,'leftTime':leftTime,'reason':desc,'reasonCode':reason}
  142. def get_dev_consume_count(self):
  143. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  144. {'IMEI': self._device['devNo'], "funCode": '07', 'data': '00'})
  145. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  146. if devInfo['rst'] == -1:
  147. raise ServiceException(
  148. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  149. elif devInfo['rst'] == 1:
  150. raise ServiceException(
  151. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  152. data = devInfo['data'][6::]
  153. if data[0:2] == '01':#表示成功
  154. pass
  155. else:
  156. raise ServiceException({'result': 2, 'description': u'获取统计数据失败,请重试看能否解决'})
  157. cardFee = int(data[2:6],16)/10.0#以角为单位
  158. coinFee = int(data[6:10],16)#以元为单位
  159. return {'cardFee':cardFee,'coinFee':coinFee}
  160. def get_port_info(self,line):
  161. return {}
  162. def get_port_status(self, force = False):
  163. if force:
  164. return self.get_port_status_from_dev()
  165. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  166. statusDict = {}
  167. if not ctrInfo.has_key('allPorts'):
  168. self.get_port_status_from_dev()
  169. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  170. allPorts = ctrInfo.get('allPorts',10)
  171. for ii in range(allPorts):
  172. tempDict = ctrInfo.get(str(ii+1),{})
  173. if tempDict.has_key('status'):
  174. statusDict[str(ii+1)] = {'status':tempDict.get('status')}
  175. elif tempDict.has_key('isStart'):
  176. if tempDict['isStart']:
  177. statusDict[str(ii+1)] = {'status':Const.DEV_WORK_STATUS_WORKING}
  178. else:
  179. statusDict[str(ii+1)] = {'status':Const.DEV_WORK_STATUS_IDLE}
  180. else:
  181. statusDict[str(ii+1)] = {'status':Const.DEV_WORK_STATUS_IDLE}
  182. allPorts,usedPorts,usePorts = self.get_port_static_info(statusDict)
  183. Device.update_dev_control_cache(self._device['devNo'], {'allPorts':allPorts,'usedPorts':usedPorts,'usePorts':usePorts})
  184. return statusDict
  185. def get_port_status_from_dev(self):
  186. devInfo = MessageSender.send(self.device, self.make_random_cmdcode(),
  187. {'IMEI': self._device['devNo'], "funCode": '0F', 'data': '00'})
  188. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  189. if devInfo['rst'] == -1:
  190. raise ServiceException(
  191. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  192. elif devInfo['rst'] == 1:
  193. raise ServiceException(
  194. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  195. data = devInfo['data'][6::]
  196. if data[0:2] == '01':#表示成功
  197. pass
  198. else:
  199. raise ServiceException({'result': 2, 'description': u'获取统计数据失败,请重试看能否解决'})
  200. result = {}
  201. portNum = int(data[2:4],16)
  202. portData = data[4::]
  203. ii = 0
  204. while ii < portNum:
  205. port = int(portData[ii*4:ii*4+2],16)
  206. statusTemp = portData[ii*4+2:ii*4+4]
  207. if statusTemp == '01':
  208. status = {'status':Const.DEV_WORK_STATUS_IDLE}
  209. elif statusTemp == '02':
  210. status = {'status':Const.DEV_WORK_STATUS_WORKING}
  211. elif statusTemp == '03':
  212. status = {'status':Const.DEV_WORK_STATUS_FORBIDDEN}
  213. elif statusTemp == '04':
  214. status = {'status':Const.DEV_WORK_STATUS_FAULT}
  215. ii += 1
  216. result[str(port)] = status
  217. allPorts,usedPorts,usePorts = self.get_port_static_info(result)
  218. Device.update_dev_control_cache(self._device['devNo'], {'allPorts':allPorts,'usedPorts':usedPorts,'usePorts':usePorts})
  219. #这里存在多线程更新缓存的场景,可能性比较低,但是必须先取出来,然后逐个更新状态,然后再记录缓存
  220. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  221. for strPort,info in result.items():
  222. if ctrInfo.has_key(strPort):
  223. ctrInfo[strPort].update({'status':info['status']})
  224. else:
  225. ctrInfo[strPort] = info
  226. Device.update_dev_control_cache(self._device['devNo'],ctrInfo)
  227. return result
  228. def lock_unlock_port(self,port,lock=True):
  229. lockStr = '00' if lock else '01'
  230. portStr = fill_2_hexByte(hex(int(port)), 2)
  231. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  232. {'IMEI': self._device['devNo'], "funCode": '0C', 'data': portStr + lockStr})
  233. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  234. if devInfo['rst'] == -1:
  235. raise ServiceException(
  236. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  237. elif devInfo['rst'] == 1:
  238. raise ServiceException(
  239. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  240. data = devInfo['data'][6::]
  241. if data[0:2] == '01':#表示成功
  242. pass
  243. else:
  244. raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'})
  245. if lock:
  246. Device.update_dev_control_cache(self._device['devNo'], {str(port):{'status':Const.DEV_WORK_STATUS_FORBIDDEN}})
  247. else:
  248. Device.update_dev_control_cache(self._device['devNo'], {str(port):{'status':Const.DEV_WORK_STATUS_IDLE}})
  249. def active_deactive_port(self,port,active):
  250. if not active:
  251. self.stop_charging_port(port)
  252. devInfo = Device.get_dev_control_cache(self._device['devNo'])
  253. portCtrInfo = devInfo.get(str(port),{})
  254. portCtrInfo.update({'isStart':False,'status':Const.DEV_WORK_STATUS_IDLE,'endTime':datetime.datetime.now().strftime(Const.DATETIME_FMT)})
  255. newValue = {str(port):portCtrInfo}
  256. Device.update_dev_control_cache(self._device['devNo'], newValue)
  257. else:
  258. raise ServiceException({'result': 2, 'description': u'此设备不支持直接打开端口'})
  259. def stop_charging_port(self,port):
  260. portStr = fill_2_hexByte(hex(int(port)), 2)
  261. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  262. {'IMEI': self._device['devNo'], "funCode": '0D', 'data': portStr + '00'})
  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'][6::]
  271. if data[0:2] == '01':#表示成功
  272. pass
  273. else:
  274. raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'})
  275. port = int(data[2:4],16)
  276. leftTime = int(data[4:8],16)
  277. return {'port':port,'leftTime':leftTime}
  278. # #获取设备配置参数
  279. def get_dev_setting(self):
  280. dev = Device.objects.get(devNo=self._device['devNo'])
  281. resultDict={}
  282. resultDict = self.get_coin_card_enable()
  283. refundProtection = dev.otherConf.get('refundProtection', 0)
  284. resultDict.update({'refundProtection':refundProtection})
  285. consumeInfo = self.get_dev_consume_count()
  286. resultDict.update(consumeInfo)
  287. return resultDict
  288. #设置设备配置参数
  289. def set_dev_setting(self,setConf):
  290. dev = Device.objects.get(devNo=self._device['devNo'])
  291. dev.otherConf.update({'coinActive': setConf['coinActive']})
  292. dev.otherConf.update({'cardActive': setConf['cardActive']})
  293. dev.otherConf.update({'refundProtection': setConf['refundProtection']})
  294. dev.save()
  295. def get_coin_card_enable(self):
  296. devs = Device.get_collection().find({'devNo': self._device['devNo']})
  297. if devs.count == 0:
  298. raise ServiceException(
  299. {'result': 2, 'description': u'没有找到设备哦'})
  300. return {'putCoins': devs[0].get('otherConf', {}).get('putCoins', False),
  301. 'icCard': devs[0].get('otherConf', {}).get('icCard', False)}
  302. def set_coin_card_enable(self, infoDict):
  303. data = ''
  304. if infoDict['putCoins']:
  305. data += '01'
  306. else:
  307. data += '00'
  308. if infoDict['icCard']:
  309. data += '01'
  310. else:
  311. data += '00'
  312. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  313. {'IMEI': self._device['devNo'], "funCode": '09', 'data': data})
  314. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  315. if devInfo['rst'] == -1:
  316. raise ServiceException(
  317. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  318. elif devInfo['rst'] == 1:
  319. raise ServiceException(
  320. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  321. try:
  322. conf = Device.objects.get(devNo = self._device['devNo']).otherConf
  323. conf.update({'putCoins': infoDict['putCoins'], 'icCard': infoDict['icCard']})
  324. Device.get_collection().update({'devNo': self._device['devNo']}, {'$set': {'otherConf': conf}})
  325. except Exception, e:
  326. logger.error('update dev=%s coin enable ic enable e=%s' % (self._device['devNo'], e))
  327. def set_device_function(self, request, lastSetConf):
  328. if request.POST.has_key('putCoins'):
  329. # putCoins = True if request.POST.get('putCoins') == 'true' else False
  330. putCoins = request.POST.get("putCoins", False)
  331. lastSetConf.update({'putCoins': putCoins})
  332. self.set_coin_card_enable(lastSetConf)
  333. if request.POST.has_key('icCard'):
  334. # icCard = True if request.POST.get('icCard') == 'true' else False
  335. icCard = request.POST.get("icCard", False)
  336. lastSetConf.update({'icCard': icCard})
  337. self.set_coin_card_enable(lastSetConf)
  338. if request.POST.has_key('refundProtection'):
  339. # refundProtection = 1 if request.POST.get('refundProtection') == 'true' else 0
  340. refundProtection = int(request.POST.get("refundProtection", False))
  341. dev = Device.objects.get(devNo = self._device['devNo'])
  342. dev.otherConf['refundProtection'] = refundProtection
  343. dev.save()
  344. @property
  345. def isHaveStopEvent(self):
  346. return True