dc_plug_node.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. # -*- coding: utf-8 -*-
  2. #!/usr/bin/env python
  3. import copy
  4. import datetime
  5. import time
  6. from apilib.monetary import RMB
  7. from apps.web.common.transaction import UserConsumeSubType
  8. from apps.web.constant import DeviceCmdCode, Const, MQTT_TIMEOUT
  9. from apps.web.core.adapter.base import SmartBox
  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. from apps.web.user.models import ConsumeRecord, MyUser
  14. class ChargingPlugBox(SmartBox):
  15. def __init__(self, device):
  16. super(ChargingPlugBox, self).__init__(device)
  17. def translate_funcode(self, fun_code):
  18. fun_codeDict = {
  19. '01': u'查询所有子设备状态',
  20. '02': u'查询全部端口详情',
  21. '03': u'绑定设备',
  22. '04': u'重启设备',
  23. '05': u'解绑设备',
  24. '07': u'启动端口',
  25. '08': u'查询设备信息',
  26. '09': u'查询端口信息',
  27. '0B': u'设置设备配置',
  28. '0C': u'查询设备配置',
  29. }
  30. return fun_codeDict.get(fun_code, '')
  31. def translate_event_cmdcode(self, cmdCode):
  32. cmdDict = {
  33. }
  34. return cmdDict.get(cmdCode, '')
  35. def test(self, coins):
  36. data = {'fun_code':0x07,'order_id':'1111','chrmt':0,'port_id':1,'amount':60}
  37. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  38. {'IMEI': self._device['devNo'], 'data': data})
  39. return devInfo
  40. def check_feedback_result(self,devInfo):
  41. if not devInfo.has_key('rst'):
  42. raise ServiceException({'result': 2, 'description': u'报文异常'})
  43. if devInfo['rst'] == -1:
  44. raise ServiceException({'result': 2, 'description': u'充电插座正在玩命找网络,请您稍候再试'})
  45. if devInfo['rst'] == 1:
  46. raise ServiceException({'result': 2, 'description': u'串口通讯失败,您稍候再试,或者联系客服'})
  47. if devInfo['rst'] == 2:
  48. raise ServiceException({'result': 2, 'description': u'端口被禁用'})
  49. if devInfo['rst'] == 3:
  50. raise ServiceException({'result': 2, 'description': u'端口计量器故障'})
  51. if devInfo['rst'] == 4:
  52. raise ServiceException({'result': 2, 'description': u'设备订单已达上限'})
  53. if devInfo['rst'] == 5:
  54. raise ServiceException({'result': 2, 'description': u'设备正在自检'})
  55. # result = devInfo['data']['result']
  56. # if result == 0:
  57. # return
  58. # else:#等待设备的错误码进行细化
  59. # raise ServiceException({'result': 2, 'description': u'充电插座返回了错误,请您重试看看能否解决问题'})
  60. def get_port_from_ab(self,portAB):
  61. portConf = {'A':1,'B':2,'C':3}
  62. if portAB in portConf:
  63. return portConf[portAB]
  64. return portAB
  65. def get_abport_from_index(self,port):
  66. portConf = {'1':'A','2':'B','3':'C'}
  67. return portConf.get(port)
  68. def start_device(self, package, openId, attachParas):
  69. if attachParas is None:
  70. raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路、电池类型信息'})
  71. if not attachParas.has_key('chargeIndex'):
  72. raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路'})
  73. port = int(self.get_port_from_ab(attachParas['chargeIndex']))
  74. attachParas['chargeIndex'] = port
  75. unit = package.get('unit', u'分钟')
  76. needTime,needElec = None,None
  77. order_no = ConsumeRecord.make_no(self.device.logicalCode, UserConsumeSubType.NETPAY)
  78. if unit == u'秒':
  79. if int(package['time']) < 60:
  80. raise ServiceException({'result': 2, 'description': u'套餐的最小时间不能小于60秒'})
  81. needTime = int(float(package['time']))
  82. data = {'fun_code':0x07,'order_id':order_no,'chrmt':0,'port_id':port,'amount':needTime}
  83. elif unit == u'分钟':
  84. needTime = int(package['time'])*60
  85. data = {'fun_code':0x07,'order_id':order_no,'chrmt':0,'port_id':port,'amount':needTime}
  86. elif unit == u'小时':
  87. needTime = int(float(package['time']) * 60 * 60)
  88. data = {'fun_code':0x07,'order_id':order_no,'chrmt':0,'port_id':port,'amount':needTime}
  89. elif unit == u'天':
  90. needTime = int(float(package['time']) * 60 * 60 * 24)
  91. data = {'fun_code':0x07,'order_id':order_no,'chrmt':0,'port_id':port,'amount':needTime}
  92. elif unit == u'度':
  93. needElec = int(float(package['time'])*1000)#微度,需要乘于16个零
  94. data = {'fun_code':0x07,'order_id':order_no,'chrmt':2,'port_id':port,'amount':needElec}
  95. else:
  96. needTime = int(package['time'])
  97. data = {'fun_code':0x07,'order_id':order_no,'chrmt':1,'port_id':port,'amount':needTime}
  98. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  99. {'IMEI': self._device['devNo'], 'data': data}, timeout = MQTT_TIMEOUT.START_DEVICE)
  100. self.check_feedback_result(devInfo)
  101. data = devInfo['data']
  102. if devInfo['rst'] == 0: # 成功
  103. newValue = {
  104. str(port): {
  105. 'status': Const.DEV_WORK_STATUS_WORKING,
  106. 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  107. }
  108. }
  109. else:#TODO result的枚举列出原因
  110. raise ServiceException({'result': 2, 'description': u'充电插座响应异常,请您稍后再试哦'})
  111. if needTime:
  112. finishedTime = int(time.time()) + needTime
  113. devInfo['needTime'] = needTime/60
  114. elif needElec:
  115. finishedTime = int(time.time()) + 60 * 60 * 10#设定10个小时,确实很难知道可以用多久结束
  116. devInfo['needElec'] = float(package['time'])
  117. else:
  118. finishedTime = int(time.time()) + 60 * 60 * 10#设定10个小时,确实很难知道可以用多久结束
  119. pass
  120. newValue.update({'finishedTime': finishedTime})
  121. Device.update_dev_control_cache(self._device['devNo'], newValue)
  122. devInfo['finished_time'] = finishedTime
  123. devInfo['consumeOrderNo'] = order_no
  124. return devInfo
  125. def analyze_event_data(self, data):
  126. if data['fun_code'] == '34':#如果是结束事件,需要把reason翻译出来
  127. descDict = {
  128. '5':u'支付的金额已经使用完毕',
  129. '6':u'用户手工停止了充电',
  130. '7':u'电池充满自停',
  131. '8':u'故障导致充电停止',
  132. '9':u'本端口功率过载,主动关闭',
  133. '10':u'没有连接充电器,主动关闭',
  134. '11':u'远程关闭',
  135. '12':u'检测到烟雾告警,主动关闭'
  136. }
  137. order = data['order']
  138. order['reason'] = descDict.get(str(order['closeType']),u'')
  139. data['order'] = order
  140. return data
  141. def get_port_status_from_dev(self):
  142. # 先到设备上,把所有子节点的信息取出来,记录到主节点的缓存
  143. devInfo = MessageSender.send(self.device, self.make_random_cmdcode(),
  144. {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x02}})
  145. self.check_feedback_result(devInfo)
  146. allPorts,usedPorts = 0,0
  147. result = {}
  148. for portId,portInfo in devInfo['data']['ports'].items():
  149. portDict = {
  150. 'status':self.__translate_status_from_str(str(portInfo['port_status'])),
  151. 'power':round(portInfo['watt']*0.1,2),
  152. 'electricity':round(portInfo['ampr']*0.1,2),
  153. }
  154. if not portInfo.has_key('order') or not portInfo['order']:
  155. Device.clear_port_control_cache(self._device['devNo'],portId)
  156. result[portId] = portDict
  157. allPorts += 1
  158. continue
  159. order = portInfo['order']
  160. portDict['elec'] = order['elec'] * 0.001
  161. if order['order_type'] == 'apps_start':
  162. portDict['consumeType'] = 'mobile'
  163. try:
  164. rcd = ConsumeRecord.objects.get(orderNo = order['id'])
  165. if u'虚拟卡' in rcd.remarks:
  166. portDict['consumeType'] = 'mobile_vcard'
  167. except Exception,e:
  168. pass
  169. elif order['order_type'] == 'coin_start':
  170. portDict['consumeType'] = 'coin'
  171. elif order['order_type'] == 'card_start':
  172. portDict['consumeType'] = 'card'
  173. else:
  174. portDict['consumeType'] = 'mobile'
  175. if order.has_key('chrmt'):
  176. if order['chrmt'] in [0,1]:
  177. portDict['needTime'] = round(order['amount']/60.0,1)
  178. if order.has_key('time'):
  179. portDict['duration'] = round(order['time']/60.0,1)
  180. portDict['usedTime'] = round(order['time']/60.0,1)
  181. if order.has_key('left_time'):
  182. portDict['leftTime'] = round(order['left_time']/60.0,1)
  183. else:
  184. portDict['needElec'] = order['amount'] * 0.001
  185. if order.has_key('elec'):
  186. portDict['usedElec'] = order.get('elec') * 0.001
  187. if order.has_key('execute_time'):
  188. portDict['startTime'] = datetime.datetime.fromtimestamp(int(portInfo['execute_time'])).strftime('%m-%d %H:%M:%S')
  189. if order.has_key('id') and (order['order_type'] not in ['coin_start','card_charge']) :#card_charge
  190. try:
  191. rcd = ConsumeRecord.objects.get(orderNo = order['id'])
  192. portDict['openId'] = rcd['openId']
  193. portDict['coins'] = float(str(rcd['coin']))#都用coins
  194. portDict['money'] = float(str(rcd['money']))
  195. portDict['orderNo'] = str(portInfo['id'])
  196. user = MyUser.objects(openId=portDict['openId']).first()
  197. if user:
  198. portDict['nickName'] = user.nickname
  199. except Exception,e:#IC卡,如果没有绑定,不会有consumeRcd,应该直接从订单中拿数据
  200. pass
  201. result[portId] = portDict
  202. if portInfo['port_status'] == 1:
  203. usedPorts += 1
  204. allPorts += 1
  205. result.update({'usedPorts':usedPorts,'allPorts':allPorts,'usePorts':allPorts-usedPorts})
  206. Device.update_dev_control_cache(self._device['devNo'],result)
  207. return result
  208. def get_port_info(self, line):
  209. line = self.get_port_from_ab(line)
  210. portCache = Device.get_dev_control_cache(self._device['devNo'])
  211. return portCache.get(str(line),{})
  212. def __translate_status_from_str(self,status):
  213. dictConf = {
  214. '0':Const.DEV_WORK_STATUS_IDLE,
  215. '1':Const.DEV_WORK_STATUS_WORKING,
  216. '2':Const.DEV_WORK_STATUS_FAULT,
  217. '3':Const.DEV_WORK_STATUS_FAULT,
  218. }
  219. return dictConf.get(status,Const.DEV_WORK_STATUS_FAULT)
  220. def get_port_status(self, force = False):
  221. if force:
  222. self.get_port_status_from_dev()
  223. portCache = Device.get_dev_control_cache(self._device['devNo'])
  224. result = {}
  225. for ii in range(5):
  226. if str(ii) in portCache:
  227. if ii == 1:
  228. result['A'] = portCache[str(ii)]
  229. elif ii == 2:
  230. result['B'] = portCache[str(ii)]
  231. elif ii == 3:
  232. result['C'] = portCache[str(ii)]
  233. return result
  234. def lock_unlock_port(self, port, lock = True):
  235. port = self.get_port_from_ab(port)
  236. portInfo = self.get_port_info(port)
  237. if portInfo['status'] == Const.DEV_WORK_STATUS_WORKING:
  238. raise ServiceException({'result': 2, 'description': u'当前端口正在使用,请您先关闭掉后,再操作'})
  239. if lock:
  240. Device.update_dev_control_cache(self._device['devNo'],{str(port): {'status': Const.DEV_WORK_STATUS_FORBIDDEN}})
  241. else:
  242. Device.update_dev_control_cache(self._device['devNo'], {str(port): {'status': Const.DEV_WORK_STATUS_IDLE}})
  243. #停止该端口下的所有任务
  244. def stop_charging_port(self, port):
  245. port = self.get_port_from_ab(port)
  246. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  247. {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x06, 'port_id': port}})
  248. self.check_feedback_result(devInfo)
  249. if devInfo['rst'] == 0:
  250. Device.update_dev_control_cache(self._device['devNo'], {str(port): {'status': Const.DEV_WORK_STATUS_IDLE}})
  251. return True if devInfo['rst'] == 0 else False
  252. def add_to_gateway(self,gatewayDevNo):
  253. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  254. {'IMEI': gatewayDevNo, 'data': {'fun_code': 0x03, 'dev_id': self._device['devNo']}})
  255. self.check_feedback_result(devInfo)
  256. # 子节点的父节点也要更新
  257. devObj = Device.objects.get(devNo = self._device['devNo'])
  258. devObj.gatewayNode = gatewayDevNo
  259. devObj.save()
  260. def remove_from_gateway(self,gatewayDevNo):
  261. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  262. {'IMEI': gatewayDevNo, 'data': {'fun_code': 0x05, 'dev_id': self._device['devNo']}})
  263. self.check_feedback_result(devInfo)
  264. # 子节点的父节点也要更新
  265. devObj = Device.objects.get(devNo = self._device['devNo'])
  266. devObj.gatewayNode = ''
  267. devObj.save()
  268. def response_card_event(self, cardNo,portId,balance,result=None):
  269. otherConf = Device.objects.get(devNo = self._device['devNo']).otherConf
  270. cardConf = otherConf.get('cardConf',{'billingType':'time','onceCard':100,'onceTime':180*60})
  271. chrmt = 0
  272. if cardConf['billingType'] == 'time':
  273. amount = float(cardConf['onceCard']) * 0.1 * int(cardConf['onceTime']) * 60
  274. chrmt = 0
  275. elif cardConf['billingType'] == 'elec':
  276. amount = float(cardConf['onceCard']) * 0.1 * float(cardConf['onceElec']) * 1000
  277. chrmt = 2
  278. else:
  279. amount = float(cardConf['onceCard']) * 0.1 * int(cardConf['onceTime']) * 60
  280. chrmt = 1
  281. if result is None:
  282. if RMB(float(cardConf['onceCard']) * 0.1) > balance:
  283. result = 5
  284. else:
  285. result = 1
  286. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  287. {'IMEI': self._device['devNo'], 'data': {'fun_code':35,
  288. 'card_no':cardNo,
  289. 'balance':int(100*float(balance)),
  290. 'amount':int(amount),
  291. 'result':result,
  292. 'chrmt':chrmt,
  293. 'port_id':portId
  294. }})
  295. self.check_feedback_result(devInfo)
  296. # 获取设备配置参数
  297. def get_dev_setting(self):
  298. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  299. {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x0C}})
  300. self.check_feedback_result(devInfo)
  301. configs = devInfo['data']['configs']
  302. devObj = Device.objects.get(devNo = self._device['devNo'])
  303. otherConf = devObj.otherConf
  304. configs['online_card_once_card'] =otherConf.get('cardConf',{}).get('onceCard',100)
  305. configs['online_card_once_time'] = otherConf.get('cardConf',{}).get('onceTime',180)
  306. configs['online_card_once_elec'] = otherConf.get('cardConf',{}).get('onceElec',1)
  307. if configs['chrmt_mode'] in [0,1]:
  308. configs['once_offline_card_charge_time'] = configs['card_lv1_val']
  309. configs['once_offline_card_elec'] = otherConf.get('cardConf',{}).get('offlineOnceElec',1)
  310. else:
  311. configs['once_offline_card_charge_time'] = otherConf.get('cardConf',{}).get('offlineOnceTime',180)
  312. configs['once_offline_card_elec'] = configs['card_lv1_val']
  313. modeDict = {'0':'time','1':'power','2':'elec'}
  314. configs['chrmt_mode'] = modeDict.get(str(configs['chrmt_mode']))
  315. return configs
  316. # 获取设备配置参数
  317. def set_dev_setting(self, setConf):
  318. setConf.update({'fun_code':0x0B})
  319. if setConf['chrmt_mode'] == 'time':
  320. setConf['chrmt_mode'] = 0
  321. setConf['card_lv1_val'] = setConf['once_offline_card_charge_time']
  322. elif setConf['chrmt_mode'] == 'power':
  323. setConf['chrmt_mode'] = 1
  324. setConf['card_lv1_val'] = setConf['once_offline_card_charge_time']
  325. else:
  326. setConf['chrmt_mode'] = 2
  327. setConf['card_lv1_val'] = setConf['once_offline_card_elec']
  328. setConf['temp_threshold'] = int(setConf['temp_threshold'])
  329. setConf['float_charge_watt'] = int(setConf['float_charge_watt'])
  330. setConf['float_charge_time'] = int(setConf['float_charge_time'])
  331. setConf['noload_check_time'] = int(setConf['noload_check_time'])
  332. setConf['port_max_watt'] = int(setConf['port_max_watt'])
  333. setConf['mach_max_watt'] = int(setConf['mach_max_watt'])
  334. setConf['noload_check_watt'] = int(setConf['noload_check_watt'])
  335. setConf['power_lv1_watt'] = int(setConf['power_lv1_watt'])
  336. setConf['power_lv1_time'] = int(setConf['power_lv1_time'])
  337. setConf['power_lv2_watt'] = int(setConf['power_lv2_watt'])
  338. setConf['power_lv2_time'] = int(setConf['power_lv2_time'])
  339. setConf['power_lv3_watt'] = int(setConf['power_lv3_watt'])
  340. setConf['power_lv3_time'] = int(setConf['power_lv3_time'])
  341. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,{'IMEI': self._device['devNo'],'data': setConf})
  342. self.check_feedback_result(devInfo)
  343. devObj = Device.objects.get(devNo = self._device['devNo'])
  344. otherConf = devObj.otherConf
  345. if not otherConf.has_key('cardConf'):
  346. otherConf['cardConf'] = {}
  347. modeDict = {'0':'time','1':'power','2':'elec'}
  348. otherConf['cardConf']['billingType'] = modeDict.get(str(setConf['chrmt_mode']))
  349. otherConf['cardConf']['onceCard'] = float(setConf['online_card_once_card'])
  350. otherConf['cardConf']['onceTime'] = int(setConf['online_card_once_time'])
  351. otherConf['cardConf']['onceElec'] = int(setConf['online_card_once_elec'])
  352. otherConf['cardConf']['offlineOnceTime'] = setConf['once_offline_card_charge_time']
  353. otherConf['cardConf']['offlineOnceElec'] = setConf['once_offline_card_elec']
  354. devObj.save()
  355. def ack_event(self,orderNo,funCode):
  356. devInfo = MessageSender.send(self.device, DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE,
  357. {'IMEI': self._device['devNo'], 'data': {'fun_code':funCode,'order_id':orderNo}})
  358. self.check_feedback_result(devInfo)
  359. def reboot_device(self):
  360. data = {'fun_code':0x04}
  361. MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,{'IMEI': self._device['devNo'], 'data': data})
  362. def active_deactive_port(self, port, active):
  363. port = self.get_port_from_ab(port)
  364. if active:
  365. raise ServiceException({'result': 2, 'description': u'该设备不支持直接打开端口'})
  366. return self.stop_charging_port(port)
  367. def set_device_function_param(self, request, lastSetConf):
  368. newConf = copy.deepcopy(request.POST)
  369. newConf.pop('logicalCode', None)
  370. self.set_dev_setting(newConf)