weifule.py 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849
  1. # -*- coding: utf-8 -*-
  2. #!/usr/bin/env python
  3. import copy
  4. import datetime
  5. import time
  6. from apps.web.constant import DeviceCmdCode, Const, FAULT_CODE, ErrorCode, MQTT_TIMEOUT
  7. from apps.web.core.adapter.base import SmartBox
  8. from apps.web.core.exceptions import ServiceException
  9. from apps.web.core.networking import MessageSender
  10. from apps.web.device.models import Device
  11. from apps.web.user.models import ConsumeRecord, MyUser, Card
  12. from apilib.utils_datetime import to_datetime
  13. from apilib.utils_AES import EncryptDate
  14. cardKey = 'FR4e1OFCnDdrYA7u'
  15. class ChargingWEIFULEBox(SmartBox):
  16. def __init__(self, device):
  17. super(ChargingWEIFULEBox, self).__init__(device)
  18. def translate_funcode(self, fun_code):
  19. fun_codeDict = {
  20. '01': u'查询所有端口状态',
  21. '02': u'查询端口详细信息',
  22. '03': u'上报投币充电事件',
  23. '04': u'上报刷卡事件',
  24. '05': u'上报充电结束事件',
  25. '06': u'远程停止充电',
  26. '07': u'远程启动充电',
  27. '08': u'查询投币总额',
  28. '09': u'清除投币总数',
  29. '0A': u'查询订单信息',
  30. }
  31. return fun_codeDict.get(fun_code, '')
  32. def translate_event_cmdcode(self, cmdCode):
  33. cmdDict = {
  34. }
  35. return cmdDict.get(cmdCode, '')
  36. def test(self, coins):
  37. data = {'fun_code':0x07,'order_id':'1111','coins':coins,'port':1,'time':60}
  38. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  39. {'IMEI': self._device['devNo'], 'data': data})
  40. return devInfo
  41. def check_feedback_result(self,devInfo):
  42. if not devInfo.has_key('rst'):
  43. raise ServiceException({'result': 2, 'description': u'报文异常'})
  44. if devInfo['rst'] == -1:
  45. raise ServiceException({'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  46. if devInfo['rst'] == 1:
  47. raise ServiceException({'result': 2, 'description': u'串口通讯失败,您稍候再试,或者联系客服'})
  48. if devInfo['rst'] == 2:
  49. raise ServiceException({'result': 2, 'description': u'端口被禁用'})
  50. if devInfo['rst'] == 3:
  51. raise ServiceException({'result': 2, 'description': u'端口计量器故障'})
  52. if devInfo['rst'] == 4:
  53. raise ServiceException({'result': 2, 'description': u'设备订单已达上限'})
  54. if devInfo['rst'] == 5:
  55. raise ServiceException({'result': 2, 'description': u'设备正在自检'})
  56. # result = devInfo['data']['result']
  57. # if result == 0:
  58. # return
  59. # else:#等待设备的错误码进行细化
  60. # raise ServiceException({'result': 2, 'description': u'充电桩返回了错误,请您重试看看能否解决问题'})
  61. def start_device(self, package, openId, attachParas):
  62. if attachParas is None:
  63. raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路、电池类型信息'})
  64. if not attachParas.has_key('chargeIndex'):
  65. raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路'})
  66. port = int(attachParas['chargeIndex'])
  67. unit = package.get('unit', u'分钟')
  68. needTime,power = None,None
  69. coins = int(float(package['coins'])*100)#单位为分
  70. onPoints = attachParas.get('onPoints')
  71. if onPoints: # 远程上分
  72. self.stop_charging_port(port)
  73. order_no = ConsumeRecord.make_no()
  74. else:
  75. order_no = str(attachParas.get("orderNo"))
  76. if unit == u'秒':
  77. if int(package['time']) < 60:
  78. raise ServiceException({'result': 2, 'description': u'套餐的最小时间不能小于60秒'})
  79. needTime = int(float(package['time']))
  80. data = {'fun_code':0x07,'order_id':order_no,'coins':coins,'port':port,'time':needTime}
  81. elif unit == u'分钟':
  82. needTime = int(package['time'])*60
  83. data = {'fun_code':0x07,'order_id':order_no,'coins':coins,'port':port,'time':needTime}
  84. elif unit == u'小时':
  85. needTime = int(float(package['time']) * 60 * 60)
  86. data = {'fun_code':0x07,'order_id':order_no,'coins':coins,'port':port,'time':needTime}
  87. elif unit == u'天':
  88. needTime = int(float(package['time']) * 60 * 60 * 24)
  89. data = {'fun_code':0x07,'order_id':order_no,'coins':coins,'port':port,'time':needTime}
  90. elif unit == u'度':
  91. power = int(float(package['time'])*1000000)#微度,需要乘于16个零
  92. data = {'fun_code':0x07,'order_id':order_no,'coins':coins,'port':port,'elec':power}
  93. else:
  94. needTime = int(package['time'])
  95. data = {'fun_code':0x07,'order_id':order_no,'coins':coins,'port':port,'time':needTime}
  96. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  97. {'IMEI': self._device['devNo'], 'data': data}, timeout = MQTT_TIMEOUT.START_DEVICE)
  98. self.check_feedback_result(devInfo)
  99. data = devInfo['data']
  100. if devInfo['rst'] == 0: # 成功
  101. devInfo['consumeOrderNo'] = data['order']['id']
  102. newValue = {
  103. str(port): {
  104. 'status': Const.DEV_WORK_STATUS_WORKING,
  105. 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  106. }
  107. }
  108. else:#TODO result的枚举列出原因
  109. raise ServiceException({'result': 2, 'description': u'充电桩响应异常,请您稍后再试哦'})
  110. if needTime:
  111. finishedTime = int(time.time()) + needTime
  112. devInfo['needTime'] = needTime/60
  113. if power:
  114. finishedTime = int(time.time()) + 60 * 60 * 10#设定10个小时,确实很难知道可以用多久结束
  115. devInfo['needElec'] = float(package['time'])
  116. newValue.update({'finishedTime': finishedTime})
  117. Device.update_dev_control_cache(self._device['devNo'], newValue)
  118. devInfo['finished_time'] = finishedTime
  119. devInfo['consumeOrderNo'] = order_no
  120. return devInfo
  121. def analyze_event_data(self, data):
  122. if data['fun_code'] == '34':#如果是结束事件,需要把reason翻译出来
  123. descDict = {
  124. '1':u'开始充电,但是没有接充电器',
  125. '2':u'充电过程中,插座脱落',
  126. '3':u'用户按下关闭按钮关闭',
  127. '4':u'用户刷卡结束充电',
  128. '5':u'远程结束充电',
  129. '6':u'充电端口故障,为了安全主动关闭',
  130. '7':u'订购的时间使用完毕',
  131. '8':u'订购的电量使用完毕',
  132. '9':u'本端口功率过载,主动关闭',
  133. '10':u'整机功率过载,主动关闭',
  134. '11':u'其他异常导致的关闭'
  135. }
  136. order = data['order']
  137. order['reason'] = descDict.get(str(order['closeType']),u'')
  138. data['order'] = order
  139. return data
  140. def get_dev_consume_count(self):
  141. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  142. {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x08}})
  143. self.check_feedback_result(devInfo)
  144. data = devInfo['data']
  145. return {'cardFee': data['total_card']/100.0, 'coinFee': data['total_coin']/100.0}#单位为分
  146. def get_many_port_info(self,portList):
  147. devInfo = MessageSender.send(self.device, self.make_random_cmdcode(),
  148. {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x12}})
  149. self.check_feedback_result(devInfo)
  150. resultDict = {}
  151. for port,data in devInfo['data']['details'].items():
  152. if port not in portList:
  153. continue
  154. result = {'index':port}
  155. result['status'] = self.__translate_status_from_str(data['status'])
  156. result['power'] = data.get('watt',0)
  157. result['ampere'] = data.get('ampr',0) / 1000.0
  158. result['voltage'] = data.get('volt')
  159. curOrder = None
  160. for order in data['orders']:
  161. if order['status'] == 'running':
  162. curOrder = order
  163. break
  164. if curOrder:
  165. result['elec'] = curOrder['elec']
  166. if curOrder['order_type'] == 'apps_start':
  167. result['consumeType'] = 'mobile'
  168. try:
  169. rcd = ConsumeRecord.objects.get(orderNo = curOrder['id'])
  170. if u'虚拟卡' in rcd.remarks:
  171. result['consumeType'] = 'mobile_vcard'
  172. except Exception,e:
  173. pass
  174. elif curOrder['order_type'] == 'coin_start':
  175. result['consumeType'] = 'coin'
  176. elif curOrder['order_type'] == 'card_start':
  177. result['consumeType'] = 'card'
  178. if curOrder.has_key('amount_time'):
  179. result['needTime'] = round(curOrder['amount_time']/60.0,1)
  180. if curOrder.has_key('time'):
  181. result['duration'] = round(curOrder['time']/60.0,1)
  182. result['usedTime'] = round(curOrder['time']/60.0,1)
  183. if curOrder.has_key('left_time'):
  184. result['leftTime'] = round(curOrder['left_time']/60.0,1)
  185. if curOrder.has_key('amount_elec'):
  186. result['needElec'] = curOrder['amount_elec']/1000000.0
  187. if curOrder.has_key('elec'):
  188. result['elec'] = curOrder.get('elec')/1000000.0
  189. if curOrder.has_key('execute_time'):
  190. result['startTime'] = datetime.datetime.fromtimestamp(int(curOrder['execute_time'])).strftime('%m-%d %H:%M:%S')
  191. if curOrder.has_key('id') and (curOrder['order_type'] not in ['coin_start','card_charge']) :#card_charge
  192. try:
  193. rcd = ConsumeRecord.objects.get(orderNo = curOrder['id'])
  194. result['openId'] = rcd['openId']
  195. result['coins'] = float(str(rcd['coin']))#都用coins
  196. result['money'] = float(str(rcd['money']))
  197. result['orderNo'] = str(curOrder['id'])
  198. user = MyUser.objects(openId=result['openId']).first()
  199. if user:
  200. result['nickName'] = user.nickname
  201. except Exception,e:#IC卡,如果没有绑定,不会有consumeRcd,应该直接从订单中拿数据
  202. result['coins'] = float(order['coins']/100.0)
  203. result['money'] = float(order['coins']/100.0)
  204. pass
  205. resultDict[port] = result
  206. return resultDict
  207. def get_port_info(self, line):
  208. devInfo = MessageSender.send(self.device, self.make_random_cmdcode(),
  209. {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x02, 'port': int(line)}})
  210. self.check_feedback_result(devInfo)
  211. #找出当前正在执行的订单
  212. result = {}
  213. data = devInfo['data']
  214. result['status'] = self.__translate_status_from_str(data['status'])
  215. result['power'] = data.get('watt',0)
  216. result['ampere'] = data.get('ampr',0) / 1000.0
  217. result['voltage'] = data.get('volt')
  218. curOrder = None
  219. for order in data['orders']:
  220. if order['status'] == 'running':
  221. curOrder = order
  222. break
  223. if curOrder:
  224. result['elec'] = curOrder['elec']
  225. if curOrder['order_type'] == 'apps_start':
  226. result['consumeType'] = 'mobile'
  227. try:
  228. rcd = ConsumeRecord.objects.get(orderNo = curOrder['id'])
  229. if u'虚拟卡' in rcd.remarks:
  230. result['consumeType'] = 'mobile_vcard'
  231. except Exception,e:
  232. pass
  233. elif curOrder['order_type'] == 'coin_start':
  234. result['consumeType'] = 'coin'
  235. elif curOrder['order_type'] == 'card_start':
  236. result['consumeType'] = 'card'
  237. if curOrder.has_key('amount_time'):
  238. result['needTime'] = round(curOrder['amount_time']/60.0,1)
  239. if curOrder.has_key('time'):
  240. result['duration'] = round(curOrder['time']/60.0,1)
  241. result['usedTime'] = round(curOrder['time']/60.0,1)
  242. if curOrder.has_key('left_time'):
  243. result['leftTime'] = round(curOrder['left_time']/60.0,1)
  244. if curOrder.has_key('amount_elec'):
  245. result['needElec'] = curOrder['amount_elec']/1000000.0
  246. if curOrder.has_key('elec'):
  247. result['elec'] = curOrder.get('elec')/1000000.0
  248. if curOrder.has_key('execute_time'):
  249. result['startTime'] = datetime.datetime.fromtimestamp(int(curOrder['execute_time'])).strftime('%m-%d %H:%M:%S')
  250. if curOrder.has_key('card_no'):
  251. result['cardNo'] = str(int(curOrder['card_no'],16))
  252. if curOrder.has_key('id') and (curOrder['order_type'] not in ['coin_start','card_charge']) :#card_charge
  253. try:
  254. rcd = ConsumeRecord.objects.get(orderNo = curOrder['id'])
  255. result['openId'] = rcd['openId']
  256. result['coins'] = float(str(rcd['coin']))#都用coins
  257. result['money'] = float(str(rcd['money']))
  258. result['orderNo'] = str(curOrder['id'])
  259. except Exception,e:
  260. pass
  261. return result
  262. def __translate_status_from_str(self,status):
  263. dictConf = {
  264. 'idle':Const.DEV_WORK_STATUS_IDLE,
  265. 'busy':Const.DEV_WORK_STATUS_WORKING,
  266. 'forbid':Const.DEV_WORK_STATUS_FORBIDDEN,
  267. 'fault':Const.DEV_WORK_STATUS_FAULT,
  268. 'running':Const.DEV_WORK_STATUS_WORKING
  269. }
  270. return dictConf.get(status,Const.DEV_WORK_STATUS_IDLE)
  271. # 访问设备,获取设备端口信息
  272. def get_port_status_from_dev(self):
  273. devInfo = MessageSender.send(self.device, self.make_random_cmdcode(),
  274. {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x01}})
  275. self.check_feedback_result(devInfo)
  276. data = devInfo['data']
  277. result = {}
  278. portNum = data.get('total',10)
  279. ii = 0
  280. while ii < portNum:
  281. ii += 1
  282. result[str(ii)] = {'status':self.__translate_status_from_str(data['status'].get(str(ii)))}
  283. allPorts, usedPorts, usePorts = self.get_port_static_info(result)
  284. Device.update_dev_control_cache(self._device['devNo'],
  285. {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  286. # 这里存在多线程更新缓存的场景,可能性比较低,但是必须先取出来,然后逐个更新状态,然后再记录缓存
  287. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  288. for strPort, info in result.items():
  289. if ctrInfo.has_key(strPort):
  290. ctrInfo[strPort].update({'status': info['status']})
  291. else:
  292. ctrInfo[strPort] = info
  293. Device.update_dev_control_cache(self._device['devNo'], ctrInfo)
  294. return result
  295. def get_port_status(self, force = False):
  296. if force:
  297. return self.get_port_status_from_dev()
  298. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  299. if not ctrInfo.has_key('allPorts'):
  300. self.get_port_status_from_dev()
  301. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  302. allPorts = ctrInfo.get('allPorts', 10)
  303. statusDict = {}
  304. for ii in range(allPorts):
  305. tempDict = ctrInfo.get(str(ii + 1), {})
  306. if tempDict.has_key('status'):
  307. statusDict[str(ii + 1)] = {'status': tempDict.get('status')}
  308. elif tempDict.has_key('isStart'):
  309. if tempDict['isStart']:
  310. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_WORKING}
  311. else:
  312. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  313. else:
  314. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  315. allPorts, usedPorts, usePorts = self.get_port_static_info(statusDict)
  316. Device.update_dev_control_cache(self._device['devNo'],
  317. {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  318. return statusDict
  319. def lock_unlock_port(self, port, lock = True):
  320. portInfo = self.get_port_info(port)
  321. if portInfo and portInfo['status'] == Const.DEV_WORK_STATUS_WORKING and lock:
  322. raise ServiceException({'result': 2, 'description': u'端口正忙,请先关闭端口后,再禁止端口'})
  323. typeStr = 'deactive' if lock else 'active'
  324. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  325. {'IMEI': self._device['devNo'], 'data': {'port':int(port),'type':typeStr,'fun_code':0x0D}})
  326. self.check_feedback_result(devInfo)
  327. if devInfo['rst'] == 0:
  328. if lock:
  329. Device.update_dev_control_cache(self._device['devNo'], {str(port): {'status': Const.DEV_WORK_STATUS_FORBIDDEN}})
  330. else:
  331. Device.update_dev_control_cache(self._device['devNo'], {str(port): {'status': Const.DEV_WORK_STATUS_IDLE}})
  332. else:
  333. Device.update_dev_control_cache(self._device['devNo'], {str(port): {'status': Const.DEV_WORK_STATUS_IDLE}})
  334. def active_deactive_port(self, port, active):
  335. if active:
  336. raise ServiceException({'result': 2, 'description': u'该设备不支持直接打开端口'})
  337. return self.stop_charging_port(port)
  338. #停止该端口下的所有任务
  339. def stop_charging_port(self, port):
  340. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  341. {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x06, 'port': port}})
  342. self.check_feedback_result(devInfo)
  343. if devInfo['rst'] == 0:
  344. Device.update_dev_control_cache(self._device['devNo'], {str(port): {'status': Const.DEV_WORK_STATUS_IDLE}})
  345. return True if devInfo['rst'] == 0 else False
  346. def get_order(self,order_no):
  347. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  348. {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x0A, 'order_id': order_no}})
  349. self.check_feedback_result(devInfo)
  350. curOrder = devInfo['data'].get('order',None)
  351. result = {}
  352. if not curOrder.has_key('order_type'):#有可能没有订单,应该返回空
  353. return result
  354. result['status'] = curOrder['status']
  355. result['elec'] = curOrder['elec']
  356. if curOrder['order_type'] == 'apps_start':
  357. result['consumeType'] = 'mobile'
  358. try:
  359. rcd = ConsumeRecord.objects.get(orderNo = curOrder['id'])
  360. if u'虚拟卡' in rcd.remarks:
  361. result['consumeType'] = 'mobile_vcard'
  362. except Exception,e:
  363. pass
  364. elif curOrder['order_type'] == 'coin_start':
  365. result['consumeType'] = 'coin'
  366. elif curOrder['order_type'] == 'card_start':
  367. result['consumeType'] = 'card'
  368. if curOrder.has_key('amount_time'):
  369. result['needTime'] = round(curOrder['amount_time']/60.0,1)
  370. if curOrder.has_key('time'):
  371. result['duration'] = round(curOrder['time']/60.0,1)
  372. result['usedTime'] = round(curOrder['time']/60.0,1)
  373. if curOrder.has_key('left_time'):
  374. result['leftTime'] = round(curOrder['left_time']/60.0,1)
  375. if curOrder.has_key('amount_elec'):
  376. result['needElec'] = curOrder['amount_elec']/1000000.0
  377. if curOrder.has_key('elec'):
  378. result['elec'] = curOrder.get('elec')/1000000.0
  379. if curOrder.has_key('execute_time'):
  380. result['startTime'] = datetime.datetime.fromtimestamp(int(curOrder['execute_time'])).strftime('%m-%d %H:%M:%S')
  381. if curOrder.has_key('card_no'):
  382. result['cardNo'] = str(int(curOrder['card_no'],16))
  383. if curOrder.has_key('id') and (curOrder['order_type'] not in ['coin_start','card_charge']) :#card_charge
  384. try:
  385. rcd = ConsumeRecord.objects.get(orderNo = curOrder['id'])
  386. result['openId'] = rcd['openId']
  387. result['coins'] = float(str(rcd['coin']))#都用coins
  388. result['money'] = float(str(rcd['money']))
  389. result['orderNo'] = str(curOrder['id'])
  390. except Exception,e:
  391. return None
  392. return result
  393. def response_card_balance(self, cardNo,balance,result):
  394. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  395. {'IMEI': self._device['devNo'], 'data': {'fun_code':35,'card_no':cardNo,'balance':int(100*float(balance)),'result':result}})
  396. self.check_feedback_result(devInfo)
  397. # 获取设备配置参数
  398. def get_dev_setting(self):
  399. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  400. {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x0C}})
  401. self.check_feedback_result(devInfo)
  402. result = devInfo['data']
  403. result.update({
  404. 'fcharge_watt':result['fcharge']['watt'],
  405. 'fcharge_time':result['fcharge']['time'],
  406. 'fcharge_check':result['fcharge'].get('check',10),
  407. })
  408. packageElec,packageTime = [],[]
  409. for name,conf in result['package'].items():
  410. if name == 'TIME':
  411. for k,v in conf.items():
  412. packageTime.append({'price':int(k)/100,'time':v/60})#单位从秒转为分钟
  413. else:
  414. for k,v in conf.items():
  415. packageElec.append({'price':int(k)/100,'time':v/1000000})#单位从微度转为度
  416. result['package_elec'] = packageElec
  417. result['package_time'] = packageTime
  418. powerRate = []
  419. for k,v in result['power_rate'].items():
  420. powerRate.append(v)
  421. def cmp_power(x,y):
  422. if x['max'] < y['max']:
  423. return -1
  424. elif x['max'] > y['max']:
  425. return 1
  426. return 0
  427. powerRate = sorted(powerRate,cmp_power)
  428. result['power_rate'] = powerRate
  429. defaultVolume = 0
  430. volumeList = []
  431. for k,v in result['volume'].items():
  432. if k == 'default':
  433. defaultVolume = v
  434. else:
  435. tempList = k.split('-')
  436. volumeList.append({'start':tempList[0],'end':tempList[1],'volume':v})
  437. result.pop('volume')
  438. result['volume'] = defaultVolume
  439. result['volume_list'] = volumeList
  440. result['once_card'] = result['once_card'] / 100.0
  441. result['once_coin'] = result['once_coin'] / 100.0
  442. state = self.get_dev_consume_count()
  443. result.update(state)
  444. return result
  445. # 获取设备配置参数
  446. def set_dev_setting(self, setConf):
  447. #如果是卡密码,直接进行修改
  448. if setConf.has_key('card_pwd'):
  449. return self.set_card_pwd(setConf['card_pwd'])
  450. #时间套餐不能为空
  451. if setConf.has_key('package_time') and len(setConf['package_time']) == 0:
  452. raise ServiceException({'result': 2, 'description': u'按时间计费的套餐不能为空'})
  453. #检查数据
  454. if not (int(setConf['fcharge_time']) >= 1 and int(setConf['fcharge_time']) <= 300):
  455. raise ServiceException({'result': 2, 'description': u'浮充时间必须大于等于1分钟,小于等于300分钟'})
  456. if not (int(setConf['fcharge_check']) >= 2 and int(setConf['fcharge_check']) <= 30):
  457. raise ServiceException({'result': 2, 'description': u'浮充检测时间必须大于等于2分钟,小于等于30分钟'})
  458. if not (int(setConf['fcharge_watt']) >= 20 and int(setConf['fcharge_watt']) <= 100):
  459. raise ServiceException({'result': 2, 'description': u'浮充功率必须大于等于20瓦,小于等于100瓦'})
  460. if not (int(setConf['volume']) >= 0 and int(setConf['volume']) <= 7):
  461. raise ServiceException({'result': 2, 'description': u'音量必须大于等于0,小于等于7'})
  462. volumeDict = {'default':int(setConf['volume'])}
  463. for vl in setConf['volume_list']:
  464. startTime = to_datetime('2020-01-01 %s:00' % vl['start'])
  465. endTime = to_datetime('2020-01-01 %s:00' % vl['end'])
  466. if endTime <= startTime:
  467. raise ServiceException({'result': 2, 'description': u'结束时间一定要大于起始时间。比如起始时间为07:00,结束时间为23:59'})
  468. volumeDict['%s-%s' % (vl['start'],vl['end'])] = int(vl['volume'])
  469. setConf.pop('volume')
  470. setConf.pop('volume_list')
  471. setConf['volume'] = volumeDict
  472. setConf['once_card'] = int(float(setConf['once_card'])*100)
  473. setConf['once_coin'] = int(float(setConf['once_coin'])*100)
  474. setConf['max_watt'] = int(setConf['max_watt'])
  475. if not (int(setConf['max_watt']) >= 50 and int(setConf['max_watt']) <= 1000):
  476. raise ServiceException({'result': 2, 'description': u'最大功率必须大于等于100瓦,小于等于1000瓦'})
  477. setConf.update({'fun_code':0x0B})
  478. setConf['fcharge'] = {
  479. 'watt':int(setConf['fcharge_watt']),
  480. 'time':int(setConf['fcharge_time']),
  481. 'check':int(setConf['fcharge_check']),
  482. }
  483. setConf.pop('fcharge_watt')
  484. setConf.pop('fcharge_time')
  485. setConf.pop('fcharge_check')
  486. packageElec,packageTime = {},{}
  487. isHaveOneCoinForElec,isHaveOneCoinForTime = False,False
  488. for pConf in setConf['package_time']:
  489. if pConf['price'] == 1:
  490. isHaveOneCoinForTime = True
  491. packageTime[str(int(float(pConf['price'])*100))] = int(float(pConf['time'])*60)
  492. for pConf in setConf['package_elec']:
  493. if pConf['price'] == 1:
  494. isHaveOneCoinForElec = True
  495. packageElec[str(int(float(pConf['price'])*100))] = int(float(pConf['time'])*1000000)
  496. if (setConf['chrmt']=='ELEC' and not isHaveOneCoinForElec) or (setConf['chrmt']=='TIME' and not isHaveOneCoinForTime):
  497. raise ServiceException({'result': 2, 'description': u'刷卡投币收费模式中,至少需要包含1元的使用套餐'})
  498. if ( len(packageElec) != len(setConf['package_elec']) ) or ( len(packageTime) != len(setConf['package_time']) ):
  499. raise ServiceException({'result': 2, 'description': u'套餐价格不允许出现重复'})
  500. setConf.pop('package_time')
  501. setConf.pop('package_elec')
  502. setConf['package'] = {'ELEC':packageElec,'TIME':packageTime}
  503. powerRate = {}
  504. powerList = []
  505. for pConf in setConf['power_rate']:
  506. powerRate[str(pConf['max'])] = pConf
  507. powerList.append(int(pConf['max']))
  508. powerList = sorted(powerList)
  509. powerSetDict = {}
  510. ii = 0
  511. prePw = 0
  512. for pw in powerList:
  513. ii += 1
  514. if ii == 1:
  515. powerSetDict[str(ii)] = {'min':0,'max':pw,'rate':int(powerRate[str(pw)]['rate'])}
  516. else:
  517. if int(powerRate[str(pw)]['rate']) < int(powerRate[str(prePw)]['rate']):
  518. raise ServiceException({'result': 2, 'description': u'功率越大,比例应该越高,这样用户的时间才消耗快一些,您才不会亏哦'})
  519. powerSetDict[str(ii)] = {'min':prePw,'max':pw,'rate':int(powerRate[str(pw)]['rate'])}
  520. prePw = pw
  521. setConf.pop('power_rate')
  522. setConf['power_rate'] = powerSetDict
  523. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  524. {'IMEI': self._device['devNo'], 'data': setConf})
  525. self.check_feedback_result(devInfo)
  526. def ack_event(self,orderNo,funCode):
  527. devInfo = MessageSender.send(self.device, DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE,
  528. {'IMEI': self._device['devNo'], 'data': {'fun_code':funCode,'order_id':orderNo}})
  529. self.check_feedback_result(devInfo)
  530. def clear_dev_feecount(self):
  531. devInfo = MessageSender.send(self.device, self.make_random_cmdcode(),
  532. {'IMEI': self._device['devNo'], 'data': {'fun_code':0x09,'total_coin':True,'total_card':True}})
  533. self.check_feedback_result(devInfo)
  534. def response_card_charge_result(self,cardNo,result):
  535. MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SERVER_ASYNC,
  536. {'IMEI': self._device['devNo'], 'data': {'fun_code':37,'card_no':cardNo,'result':result}})
  537. def reboot_device(self):
  538. data = {'fun_code':0x0B,'reset_mcu':True}
  539. MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  540. {'IMEI': self._device['devNo'], 'data': data})
  541. MessageSender.async_send(self.device, DeviceCmdCode.SET_DEVINFO,
  542. {'IMEI': self._device['devNo'], 'restart': True})
  543. def set_device_function(self, request, lastSetConf):
  544. if request.POST.has_key('clearSum'):
  545. self.clear_dev_feecount()
  546. elif request.POST.has_key('reboot'):
  547. self.reboot_device()
  548. def set_device_function_param(self, request, lastSetConf):
  549. newConf = copy.deepcopy(request.POST)
  550. newConf.pop('logicalCode', None)
  551. self.set_dev_setting(newConf)
  552. # if request.POST.has_key('maxPower') and request.POST.has_key('icMoney'):
  553. # lastSetConf.update({'maxPower': int(request.POST.get('maxPower', 0))})
  554. # lastSetConf.update({'icMoney': int(request.POST.get('icMoney', 0))})
  555. # self.set_IC_coin_power_config(lastSetConf)
  556. #
  557. # if request.POST.has_key('time1') and request.POST.has_key('time2') and request.POST.has_key('time3'):
  558. # lastSetConf.update({'time1': int(request.POST.get('time1', 0))})
  559. # lastSetConf.update({'time2': int(request.POST.get('time2', 0))})
  560. # lastSetConf.update({'time3': int(request.POST.get('time3', 0))})
  561. # self.set_IC_coin_power_config(lastSetConf)
  562. #
  563. # if request.POST.has_key('power1') and request.POST.has_key('power1ratio'):
  564. # lastSetConf.update({'power1': int(request.POST.get('power1', 0))})
  565. # lastSetConf.update({'power1ratio': int(request.POST.get('power1ratio', 0))})
  566. # lastSetConf.update({'power2': int(request.POST.get('power2', 0))})
  567. # lastSetConf.update({'power2ratio': int(request.POST.get('power2ratio', 0))})
  568. # lastSetConf.update({'power3': int(request.POST.get('power3', 0))})
  569. # lastSetConf.update({'power3ratio': int(request.POST.get('power3ratio', 0))})
  570. # lastSetConf.update({'power4': int(request.POST.get('power4', 0))})
  571. # lastSetConf.update({'power4ratio': int(request.POST.get('power4ratio', 0))})
  572. # lastSetConf.update({'power5': int(request.POST.get('power5', 0))})
  573. # lastSetConf.update({'power5ratio': int(request.POST.get('power5ratio', 0))})
  574. # self.set_gear_conf(lastSetConf)
  575. #
  576. # if request.POST.has_key('card1Time') and request.POST.has_key('card2Time') and request.POST.has_key(
  577. # 'card3Time'):
  578. # lastSetConf.update({'card1Time': int(request.POST.get('card1Time', 0))})
  579. # lastSetConf.update({'card2Time': int(request.POST.get('card2Time', 0))})
  580. # lastSetConf.update({'card3Time': int(request.POST.get('card3Time', 0))})
  581. # self.set_card_time_config(lastSetConf)
  582. #
  583. # if request.POST.has_key('fuchongPower') and request.POST.has_key('fuChongTime'):
  584. # lastSetConf.update({'fuchongPower': int(request.POST.get('fuchongPower', 0))})
  585. # lastSetConf.update({'fuChongTime': int(request.POST.get('fuChongTime', 0))})
  586. # self.set_fuchong_config(lastSetConf)
  587. def get_card_pwd(self):
  588. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  589. {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x11}})
  590. self.check_feedback_result(devInfo)
  591. result = devInfo['data']
  592. enObj = EncryptDate(cardKey)
  593. cardPwd = enObj.decrypt(result['card_pwd'])
  594. if not cardPwd:
  595. cardPwd = ''
  596. return {'card_pwd':cardPwd}
  597. def translante_card_no(self,hexCardNo):
  598. return int(hexCardNo,16)
  599. def check_pwd(self,pwd):
  600. if len(pwd) != 6:
  601. raise ServiceException({'result': 2, 'description': u'密码必须是6位纯数字密码'})
  602. for char in pwd:
  603. if not (char>='0' and char<='9'):
  604. raise ServiceException({'result': 2, 'description': u'密码必须是6位纯数字密码'})
  605. return
  606. def set_card_pwd(self,pwd):
  607. self.check_pwd(pwd)
  608. enObj = EncryptDate(cardKey)
  609. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  610. {'IMEI': self._device['devNo'], 'data': {'fun_code':0x0B,'card_pwd':enObj.encrypt(pwd)}})
  611. self.check_feedback_result(devInfo)
  612. def set_card_mode(self,setConf):
  613. cardMode = int(setConf.get('card_mode'))
  614. valueDict = {'fun_code':0x10}
  615. if cardMode == 0:
  616. valueDict.update({'mode':cardMode})
  617. elif cardMode == 1:#格式化为离线卡
  618. self.check_pwd(setConf['new_pwd'])
  619. newPwd = setConf['new_pwd']
  620. if not str(setConf['balance']).isdigit():
  621. raise ServiceException({'result': 2, 'description': u'余额必须是数字'})
  622. balance = int(setConf['balance'])
  623. if not (balance >=0 and balance <= 5000):
  624. raise ServiceException({'result': 2, 'description': u'余额必须在0和5000元之间'})
  625. balance = balance * 10#硬件模块记录的单位是角
  626. enObj = EncryptDate(cardKey)
  627. valueDict.update({'mode':1,'new_pwd':enObj.encrypt(str(newPwd)),'balance':balance})
  628. elif cardMode == 2:
  629. self.check_pwd(setConf['old_pwd'])
  630. self.check_pwd(setConf['new_pwd'])
  631. newPwd = setConf['new_pwd']
  632. oldPwd = setConf['old_pwd']
  633. enObj = EncryptDate(cardKey)
  634. valueDict.update({'mode':2,'old_pwd':enObj.encrypt(str(oldPwd)),'new_pwd':enObj.encrypt(str(newPwd))})
  635. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  636. {'IMEI': self._device['devNo'], 'data': valueDict})
  637. self.check_feedback_result(devInfo)
  638. def get_card_mode(self):
  639. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  640. {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x11}})
  641. self.check_feedback_result(devInfo)
  642. cardMode = devInfo['data']['card_mode']
  643. return {'card_mode':cardMode}
  644. def check_alarm(self,alarm):
  645. if alarm.faultCode == FAULT_CODE.MCU_REBOOT:
  646. return u'此告警,建议您多观察,如果比较频繁不停上报单台设备的告警,可能会运行不稳定。需要您联系技术支持确认。'
  647. elif alarm.faultCode == FAULT_CODE.COUNTER_FAULT:
  648. portInfo = self.get_port_info(alarm.portNo)
  649. if portInfo['status'] == Const.DEV_WORK_STATUS_FAULT:
  650. return u'此端口目前已经为故障状态,继电器可能运行不稳定,建议您联系技术支持,以确认设备运行情况。'
  651. else:
  652. return u'端口状态检查正常,继电器或存偶尔无法获取数据。暂不影响使用。'
  653. elif alarm.faultCode == FAULT_CODE.RELAY_FAULT:
  654. return u'无法进行远程诊断,建议您到现场,直接插上插座,然后检查是否不用付款,就能够充电。如果是的,就属于继电器粘连。'
  655. elif alarm.faultCode == FAULT_CODE.DEV_OVERLOAD:
  656. return u'整机功率最大限定为7500瓦,接入的负载超过此负载,为了安全,将强行关闭所有充电端口。'
  657. elif alarm.faultCode == FAULT_CODE.COPY_CARD:
  658. return u'出现一模一样的卡,可能是用户复制了另外一张离线卡,然后使用,会给您造成经济上的损失,建议冻结。'
  659. return ''
  660. def get_part_info(self):
  661. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  662. {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x05}})
  663. self.check_feedback_result(devInfo)
  664. partInfo = devInfo['data']
  665. result = {}
  666. nameDesc = {'networkBoard':u'网络板','chargeBoard':u'充电板','cardBoard':u'刷卡板'}
  667. for k,v in partInfo.items():
  668. if k not in nameDesc:
  669. continue
  670. result[nameDesc.get(k)] = {'SN':v}
  671. return result
  672. def recharge_card(self, cardNo, money, orderNo=None):
  673. hex_cardNo = hex(int(cardNo))[2::].replace('L', '').upper()
  674. result = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SERVER_ASYNC,
  675. {'IMEI': self.device['devNo'],
  676. 'data': {'fun_code': 37, 'result': 1, 'card_no': hex_cardNo,
  677. 'charge': int(money * 100),
  678. 'order_id': orderNo}})
  679. # 返回验证
  680. self.check_feedback_result(result)
  681. card = Card.objects.filter(cardNo=cardNo,dealerId=self.device.ownerId).first()
  682. balance = card.balance + money
  683. return {
  684. 'result': ErrorCode.SUCCESS,
  685. 'description': ''
  686. }, balance
  687. def get_customize_score_unit(self):
  688. return u'元'
  689. def get_customize_package_unit(self):
  690. chrmt = self.device['otherConf'].get('chrmt')
  691. if not chrmt:
  692. chrmt = self.get_dev_setting().get('chrmt')
  693. # 按时间计费
  694. if chrmt == 'TIME' or chrmt == 'POWER':
  695. return u'分钟'
  696. # 按电量计费
  697. elif chrmt == 'ELEC':
  698. return u'度'
  699. return u'元'
  700. def start_customize_point(self,pointNum,openId,port):
  701. unit = self.get_customize_package_unit()
  702. package = {'name':'customizePoint','price':pointNum,'coins':pointNum,'unit':unit,'time':999} # 如果是时间,就用时间作为兜底的控制,钱填大一些;
  703. attachParas = {'chargeIndex':port,'onPoints':True}
  704. return self.start_device(package, openId, attachParas)