dekang.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import time
  4. from apilib.utils_datetime import timestamp_to_dt
  5. from apps.web.constant import Const, DeviceCmdCode, MQTT_TIMEOUT
  6. from apps.web.core.adapter.base import SmartBox, fill_2_hexByte, hexbyte_2_bin
  7. from apps.web.core.exceptions import ServiceException
  8. from apps.web.core.networking import MessageSender
  9. from apps.web.device.models import Device
  10. class DataType(object):
  11. # 发送设备事件
  12. GET_PORT_INFO = '02'
  13. GET_DEV_SETTING = '01'
  14. START_DEV = '51'
  15. STOP_DEV = '63'
  16. class DeKangBox(SmartBox):
  17. DATA = '{port}{dataType}{data}00'
  18. def __sendData(self, data, FunCode='A4',timeout = MQTT_TIMEOUT.NORMAL,orderNo=None):
  19. result = MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  20. payload = {'IMEI': self._device['devNo'], 'funCode': FunCode, 'data': data},
  21. timeout = timeout)
  22. if result.has_key('rst') and result['rst'] != 0:
  23. if result['rst'] == -1:
  24. raise ServiceException({'result': 2, 'description': u'充电桩正在玩命找网络,请稍候再试'})
  25. elif result['rst'] == 1:
  26. raise ServiceException({'result': 2, 'description': u'充电桩主板连接故障'})
  27. else:
  28. raise ServiceException({'result': 2, 'description': u'系统错误'})
  29. return result
  30. @staticmethod
  31. def __parse_port_data(data):
  32. """
  33. 状态:
  34. 00 : 未插 空闲 AA5524019B010000000008CA000000 00 000000000000000000000000000000000000000000B409BB
  35. 11 : 插上 检测负载 AA5524019B010000000008CA000000 11 0000000000000000000000000000000000000000002AD9BB
  36. 12 : 付款 继电器连接 AA5524019B01000000000000043B3B 12 1400000000000000000000000000000000000000003401BB
  37. 13 : 付款 继电器断开 AA5524019B010000000025D404330B 13 1400000000000000000000000000000000000000005529BB
  38. 52 : 启动 使用 AA5524019B01000000000000043B3A 52 14000000000000000000000000000000000000000065B1BB
  39. :param data:
  40. :return:
  41. """
  42. portStr = str(int(data[: 2], 16))
  43. power = str(int(data[2: 6], 16) / 10.0)
  44. ampere = str(int(data[6: 10], 16) / 1000.0)
  45. rightTime = str(int(data[14: 16], 16) * 3600 + int(data[16: 18], 16) * 60 + int(data[18: 20], 16))
  46. portStatus = hexbyte_2_bin(data[20: 22]) # 设备状态是一个字节 解析成bit进行判断
  47. curConsume = str(int(data[22: 24], 16))
  48. canPay = bool(int(portStatus[3: 4]))
  49. statusBin = portStatus[4:]
  50. leftTime = (int(rightTime) + 59 )/ 60
  51. if statusBin == "0000": # 待机转改
  52. status = Const.DEV_WORK_STATUS_IDLE
  53. elif statusBin == "0001": # 负载接入状态
  54. status = Const.DEV_WORK_STATUS_CONNECTED
  55. elif statusBin == "0010": # 充电正常运行状态
  56. status = Const.DEV_WORK_STATUS_WORKING
  57. elif statusBin == "0011": # 负载断开状态
  58. status = Const.DEV_WORK_STATUS_WORKING
  59. elif statusBin == "0100": # 充电完成负载未断开
  60. status = Const.DEV_WORK_STATUS_FINISHED
  61. else:
  62. status = Const.DEV_WORK_STATUS_FAULT
  63. return {
  64. portStr: {
  65. 'power': power,
  66. 'ampere': ampere,
  67. 'rightTime': rightTime,
  68. 'canPay': canPay,
  69. 'status': status,
  70. 'curConsume': curConsume,
  71. 'portStatus': statusBin,
  72. 'leftTime': leftTime,
  73. 'port': portStr
  74. }
  75. }
  76. def __get_one_port_info_from_dev(self, port):
  77. portStr = fill_2_hexByte(hex(int(port)), num=2)
  78. sendData = self.DATA.format(port=portStr, dataType=DataType.GET_PORT_INFO, data=fill_2_hexByte(hex(0), 16))
  79. result = self.__sendData(sendData)
  80. data = result['data'][10: -6]
  81. tempStatus = self.__parse_port_data(data)
  82. data = tempStatus.popitem()[1]
  83. data.update({'portStr': portStr})
  84. return data
  85. def analyze_event_data(self, data):
  86. """
  87. 目前只有状态改变才会有上报事件 上报数据为状态改变的通道当前状态
  88. """
  89. tempStatus = self.__parse_port_data(data[10: -6])
  90. analyzeData = {}
  91. for k, v in tempStatus.items():
  92. v.update({'portStr': k})
  93. analyzeData = v
  94. return analyzeData
  95. def check_dev_status(self, attachParas = None):
  96. if not isinstance(attachParas, dict):
  97. raise ServiceException({'result': 2, 'description': u'请选择合适的充电桩'})
  98. port = attachParas.get('chargeIndex', None)
  99. if not port:
  100. raise ServiceException({'result': 2, 'description': u'请选择合适的充电桩'})
  101. # 校验端口状态
  102. canPay, desc = self.is_port_can_use(port)
  103. if not canPay:
  104. raise ServiceException({'result': 2, 'description': desc})
  105. def start_device(self, package, openId, attachParas):
  106. if not isinstance(attachParas, dict):
  107. raise ServiceException({'result': 2, 'description': u'请选择合适的充电桩'})
  108. port = attachParas.get('chargeIndex', None)
  109. if not port:
  110. raise ServiceException({'result': 2, 'description': u'请选择合适的充电桩'})
  111. portStr = fill_2_hexByte(hex(int(port)), 2)
  112. # 校验远程上分
  113. onPoints = attachParas.get('onPoints')
  114. if onPoints:
  115. # 校验端口状态
  116. canPay, desc = self.is_port_can_use(port)
  117. if not canPay:
  118. raise ServiceException({'result': 2, 'description': desc})
  119. # 获取启动设备的金币
  120. coins = float(package['coins'])
  121. price = float(package['price'])
  122. hexCoins = fill_2_hexByte(hex(int(coins * 10)), 4)
  123. finishedTime = int(time.time()) + 3600 * 12
  124. orderNo = attachParas.get('orderNo')
  125. # 启动设备
  126. sendData = self.DATA.format(port=portStr, dataType=DataType.START_DEV, data=hexCoins+hexCoins+fill_2_hexByte(hex(0), 8))
  127. result = self.__sendData(sendData,timeout=MQTT_TIMEOUT.START_DEVICE,orderNo=orderNo)
  128. # 缓存服务信息
  129. start_timestamp = int(time.time())
  130. result['finishedTime'] = finishedTime
  131. portDict = {
  132. 'startTime': timestamp_to_dt(start_timestamp).strftime('%Y-%m-%d %H:%M:%S'),
  133. 'status': Const.DEV_WORK_STATUS_WORKING,
  134. 'finishedTime': result['finishedTime'],
  135. 'coins': coins,
  136. 'price': price,
  137. 'isStart': True,
  138. 'openId': openId,
  139. 'vCardId': self._vcard_id,
  140. 'orderNo': orderNo
  141. }
  142. if 'linkedRechargeRecordId' in attachParas and attachParas.get('isQuickPay', False):
  143. pay_info = []
  144. item = {
  145. 'rechargeRcdId': str(attachParas['linkedRechargeRecordId'])
  146. }
  147. pay_info.append(item)
  148. portDict['payInfo'] = pay_info
  149. Device.update_dev_control_cache(self._device['devNo'], {str(port): portDict})
  150. return result
  151. def stop(self, port=None):
  152. """
  153. 停止事件中 返还的数据会有存在对于该端口的剩余时间和剩余的金额 对其进行退款
  154. """
  155. if port is None: return
  156. portStr = fill_2_hexByte(hex(int(port)), 2)
  157. sendData = self.DATA.format(port=portStr, dataType=DataType.STOP_DEV, data=fill_2_hexByte(hex(0), 16))
  158. return self.__sendData(sendData)
  159. def get_port_status_from_dev(self):
  160. sendData = self.DATA.format(port='00', dataType=DataType.GET_PORT_INFO, data=fill_2_hexByte(hex(0), 16))
  161. result = self.__sendData(sendData)
  162. data = result['data'][10: -6]
  163. portInfo = dict()
  164. while data:
  165. tempStatus = self.__parse_port_data(data)
  166. portInfo.update(tempStatus)
  167. data = data[64:]
  168. allPorts, usedPorts, usePorts = self.get_port_static_info(portInfo)
  169. Device.update_dev_control_cache(self._device['devNo'], {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  170. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  171. for port, info in portInfo.items():
  172. if ctrInfo.has_key(port):
  173. lineInfo = ctrInfo[port]
  174. if 'openId' in lineInfo and lineInfo.get('isStart') and info['status'] == Const.DEV_WORK_STATUS_IDLE:
  175. ctrInfo[port] = info
  176. else:
  177. ctrInfo[port].update({'status': info['status']})
  178. ctrInfo[port].update({'portStatus': info['portStatus']})
  179. else:
  180. ctrInfo[port] = info
  181. Device.update_dev_control_cache(self._device['devNo'], ctrInfo)
  182. return portInfo
  183. def get_port_status(self, force = False):
  184. """
  185. 从缓存中获取状态,若是缓存中没有获取到设备状态,直接从设备获取后再从缓存获取
  186. 然后从设备的信息中提取出端口状态 最后再次更新设备端口缓存 并返还提取的端口状态
  187. :param force:
  188. """
  189. if force:
  190. return self.get_port_status_from_dev()
  191. curInfo = Device.get_dev_control_cache(self._device['devNo'])
  192. if not curInfo.has_key('allPorts'):
  193. self.get_port_status_from_dev()
  194. curInfo = Device.get_dev_control_cache(self._device['devNo'])
  195. allPorts = curInfo.get('allPorts')
  196. statusDict = dict()
  197. portStr = lambda x:str(x+1)
  198. for port in xrange(allPorts):
  199. tempDict = curInfo.get(portStr(port), {})
  200. if tempDict.has_key('status'):
  201. tempStatus = {'status': tempDict.get('status')}
  202. elif tempDict.has_key('isStart'):
  203. if tempDict.get('isStart'):
  204. tempStatus = {'status': Const.DEV_WORK_STATUS_WORKING}
  205. else:
  206. tempStatus = {'status': Const.DEV_WORK_STATUS_IDLE}
  207. else:
  208. tempStatus = {'status': Const.DEV_WORK_STATUS_IDLE}
  209. statusDict.setdefault(portStr(port), tempStatus)
  210. allPorts, usedPorts, usePorts = self.get_port_static_info(statusDict)
  211. Device.update_dev_control_cache(
  212. self._device['devNo'],
  213. {
  214. 'allPorts': allPorts,
  215. 'usedPorts': usedPorts,
  216. 'usePorts': usePorts
  217. }
  218. )
  219. return statusDict
  220. def is_port_can_use(self, port, canAdd=False):
  221. portInfo = self.__get_one_port_info_from_dev(port)
  222. canPay = portInfo.get('canPay')
  223. desc = '' if canPay else u'请检查充电插座是否连接成功'
  224. return canPay, desc
  225. def get_port_info(self, line=None):
  226. portHex = fill_2_hexByte(hex(int(line)), 2)
  227. sendData = self.DATA.format(port=portHex, dataType=DataType.GET_PORT_INFO, data=fill_2_hexByte(hex(0), 16))
  228. result = self.__sendData(sendData)
  229. tempStatus = self.__parse_port_data(result.get('data')[10: -6]).get(str(line))
  230. leftTime = int(round(int(tempStatus.get('rightTime')) / 60.0, 0))
  231. power = tempStatus.get('power')
  232. ampere = tempStatus.get('ampere')
  233. return {'port': str(line), 'leftTime': leftTime, 'power': power, 'ampere':ampere}
  234. def get_dev_setting(self):
  235. sendData = self.DATA.format(port='00', dataType=DataType.GET_DEV_SETTING, data=fill_2_hexByte(hex(0), 16))
  236. result = self.__sendData(sendData)
  237. data = result['data'][10: -6]
  238. maxPayment = str(int(data[2: 4], 16)) # 单通道单次最大付款数(角)
  239. unitChargeTime = str(int(data[6: 8], 16) * 6) # 单位金额充电时间(1元充电时间) 分钟 倍率是6 也就是说解析出来的数据*6才是真正的时间 单位分钟
  240. maxPower = str(int(data[32: 34], 16) * 10) # 设备最大功率(w)
  241. coinsNum = str(int(data[50: 54], 16)) # 投币数量
  242. version = 'V'+str(round(int(data[54: 56], 16) * 0.1, 1)) # 软件版本
  243. totalCardConsume = str(int(data[56: 64], 16) / 10.0) # 刷卡总数(获取数据角,显示元)
  244. result = {
  245. 'maxPayment': maxPayment,
  246. 'unitChargeTime': unitChargeTime,
  247. 'maxPower': maxPower,
  248. 'coinsNum': coinsNum,
  249. 'version': version,
  250. 'totalCardConsume': totalCardConsume
  251. }
  252. return result
  253. def active_deactive_port(self, port, active):
  254. if not active:
  255. self.stop(port)
  256. def isHaveStopEvent(self):
  257. return True