ligeCar.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. # coding=utf-8
  2. """
  3. 暂时不做了 通信方式为socket
  4. """
  5. from apilib.utils_string import split_str
  6. from apps.web.core.adapter.base import *
  7. class IntSettingsIndex(object):
  8. MAX_VOLTAGE = 5
  9. class StrSettingsIndex(object):
  10. LOGICAL_CODE = 1
  11. STANDARD_TIME = 2
  12. ADMIN_PASSWORD = 3
  13. OPERATOR_PASSWORD = 4
  14. SERVER_PASSWORD = 6
  15. SERVER_PHONE = 8
  16. PAY_QR_CODE = 10
  17. class ControlIndex(object):
  18. STOP_CHARGE = 2
  19. CHARGE_MODE = 9
  20. CANCEL = 10
  21. DEVICE_REBOOT = 11
  22. class LiGeCar(SmartBox):
  23. """
  24. 里歌汽车充电桩
  25. """
  26. _to_ascii = staticmethod(lambda x: binascii.hexlify(x))
  27. _to_str = staticmethod(lambda x: binascii.unhexlify(x))
  28. def __init__(self, device):
  29. super(LiGeCar, self).__init__(device)
  30. def _send_data(self, funCode, data, cmd = None, timeout = MQTT_TIMEOUT.NORMAL):
  31. if not cmd:
  32. cmd = DeviceCmdCode.OPERATE_DEV_SYNC
  33. result = MessageSender.send(device = self.device, cmd = cmd, payload = {
  34. "IMEI": self.device.devNo,
  35. "funCode": funCode,
  36. "data": data,
  37. }, timeout = timeout)
  38. if "rst" in result and result.get("rst") != 0:
  39. if result.get("rst") == -1:
  40. raise ServiceException({"result": 2, "description": u"网络故障,请重新试试"})
  41. if result.get("rst") == 1:
  42. raise ServiceException({"result": 2, "description": u"充电桩无响应,请稍后再试试"})
  43. return result
  44. def _query_int_type_settings(self, index, num):
  45. """
  46. 查询整形参数 目前只支持查询
  47. :param index: 查询参数起始下标
  48. :param num: 查询参数的个数
  49. :return:
  50. """
  51. indexHex = fill_2_hexByte(hex(int(index)), 8, reverse=True)
  52. numHex = fill_2_hexByte(hex(int(num)), 2)
  53. sendData = "".join(("0000", "0000", "00", indexHex, numHex))
  54. result = self._send_data("0001", sendData)
  55. if result.get("data", "")[84:86] != "00":
  56. raise ServiceException({"result": 2, "description": u"获取参数失败,请重新试试"})
  57. data = result.get("data", "")[86:-2]
  58. # 每个参数4个字节,开始解析数据, 比率换算交由上层函数
  59. dataList = list()
  60. for i in xrange(num):
  61. tempData = reverse_hex(data[i*8: i*8+8])
  62. dataList.append(int(tempData))
  63. return dataList
  64. def _query_str_type_settings(self, index, toStr=True):
  65. """
  66. 获取字符串参数
  67. :param index: 查询数据的下标
  68. :return:
  69. """
  70. indexHex = fill_2_hexByte(hex(int(index)), 8, reverse=True)
  71. sendData = "".join(("0000", "0000", "00", indexHex))
  72. result = self._send_data("0003", sendData)
  73. if result.get("data", "")[82:84] != "00":
  74. raise ServiceException({"result": 2, "description": u"获取参数失败,请重新试试"})
  75. data = result.get("data", "")[84:-2]
  76. if toStr:
  77. return self._to_str(data)
  78. return data
  79. def _set_int_type_settings(self, index, data):
  80. """
  81. 设置整形参数 可对于连续参数进行设置
  82. :param index: 设置整形参数的起始下标
  83. :param data: iterable 形式
  84. :return:
  85. """
  86. indexHex = fill_2_hexByte(hex(int(index)), 8, reverse=True)
  87. numHex = fill_2_hexByte(hex(len(data)), 2)
  88. dataLenHex = fill_2_hexByte(hex(len(data)*4), 4, reverse=True) # 设置参数字节数 每个参数4个字节
  89. dataHex = "".join([fill_2_hexByte(hex(int(item)), 8, reverse=True) for item in data])
  90. sendData = "".join(("0000", "0000", "01", indexHex, numHex, dataLenHex, dataHex))
  91. result = self._send_data("0003", sendData)
  92. if result.get("data")[84:86] != "00":
  93. raise ServiceException({"result": 2, "description": u"设置参数失败,请重新试试"})
  94. def _set_str_type_settings(self, index, data, lens, makeUp=True, toAscii=True):
  95. """
  96. 设置字符串的参数 除了 标准时间 以及 桩登陆密码 设置之外,其余所有参数均需要转为ascii码
  97. :param index: 设置参数的索引
  98. :param data: 设置参数的数值
  99. :param lens: 参数的标准长度 单位是字节
  100. :param toAscii: 是否将参数转为ascii码
  101. :param makeUp: 是否需要将参数补齐
  102. :return:
  103. """
  104. if toAscii:
  105. data = self._to_str(data)
  106. if makeUp:
  107. data = ("{:<0%s}" % lens*2).format(data)
  108. indexHex = fill_2_hexByte(hex(int(index)), 8, reverse=True)
  109. dataLen = fill_2_hexByte(hex(len(data)/2), 4, reverse=True)
  110. sendData = "".join(("0000", "0000", "01", indexHex, dataLen, data))
  111. result = self._send_data("0003", sendData)
  112. if result.get("data")[82:84] != "00":
  113. raise ServiceException({"result": 2, "description": u"设置参数失败,请重新试试"})
  114. def _control_device(self, port, index, data):
  115. """
  116. 后台服务器下发充电桩控制命令
  117. 按照协议规范,其实可以一次下发多条连续的命令,但业务目前不需要, 目前只做单一指令下发设计
  118. :param index: 控制命令下标
  119. :param data: 控制命令数据
  120. :return:
  121. """
  122. portHex = fill_2_hexByte(hex(int(port)), 2)
  123. indexHex = fill_2_hexByte(hex(int(index)), 8, reverse=True)
  124. dataHex = fill_2_hexByte(hex(int(data)), 8, reverse=True)
  125. sendData = "".join(("0000", "0000", portHex, indexHex, "01", "0004", dataHex))
  126. result = self._send_data("0005", sendData)
  127. if result.get("data")[84:86] != "00":
  128. raise ServiceException({"result": 2, "description": u"操作失败,请重新试试"})
  129. def _start_charge(self, port, chargeStrategy, times, openId, orderNo):
  130. """
  131. 后台服务器向充电桩开始充电控制命令 目前扫码充电暂不支持 预约充电功能 reservationTime/reservationTimeOut 留作扩展
  132. :param port: 充电端口号
  133. :param chargeStrategy: 充电策略 0:充满为止/1:时间控制充电/2:金额控制充电/3:电量控制充电
  134. :param times: 充电策略参数 时间单位是秒 金额单位是分 电量单位是0.01kw.h
  135. :param openId: 用户识别号
  136. :param orderNo: 充电流水号 单号
  137. :return:
  138. """
  139. portHex = fill_2_hexByte(hex(int(port)), 2)
  140. typeHex = fill_2_hexByte(hex(int(1)), 8, reverse=True)
  141. stopPasswordHex = fill_2_hexByte(hex(int(0)), 8, reverse=True)
  142. chargeStrategyHex = fill_2_hexByte(hex(int(chargeStrategy)), 8, reverse=True)
  143. timesHex = fill_2_hexByte(hex(int(times)), 8, reverse=True)
  144. reservationTimeHex = fill_2_hexByte(hex(int(0)), 16, reverse=True)
  145. reservationTimeOutHex = fill_2_hexByte(hex(int(0)), 2)
  146. openIdAscii = "{:<064}".format(self._to_ascii(openId))
  147. chargeWithoutNetHex = fill_2_hexByte(hex(int(1)), 2)
  148. chargeWithoutNetElecHex = "FFFFFFFF"
  149. orderNoAscii = "{:<064}".format(self._to_ascii(orderNo))
  150. sendDataList = [
  151. "0000",
  152. "0000",
  153. portHex,
  154. typeHex,
  155. stopPasswordHex,
  156. chargeStrategyHex,
  157. timesHex,
  158. reservationTimeHex,
  159. reservationTimeOutHex,
  160. openIdAscii,
  161. chargeWithoutNetHex,
  162. chargeWithoutNetElecHex,
  163. orderNoAscii
  164. ]
  165. sendData = "".join(sendDataList)
  166. result = self._send_data("0007", sendData)
  167. reasonCode = int(result.get("data", "")[74:82], 16)
  168. if reasonCode != 0:
  169. raise ServiceException({"result": 2, "description": u"启动充电失败,请重新试试{}".format(reasonCode)})
  170. def _parse_device_port_status(self, data):
  171. """
  172. 充电桩定期发送此信息上报充电桩当前工作状态信息
  173. 工作状态 0/空闲中 1/正准备开始充电 2/充电进行中 3/充电结束 4/启动失败 5/预约状态 6/系统故障
  174. :param data: 原始的报文数据
  175. :return:
  176. """
  177. # TODO 无用数据太多 等待模块层处理
  178. data = split_str(data, startIndex=16, lens=[4, 4, 64, 2, 2,
  179. 2, 2, 2, 8, 2,
  180. 8, 8, 8, 4, 4,
  181. 4, 4, 2, 4, 4,
  182. 4, 4, 4, 4, 4,
  183. 8, 8, 8, 8, 2,
  184. 2, 8, 2, 64, 2,
  185. 16, 8, 8, 8, 8,
  186. 8, 8, 2, 2, 2,
  187. 36, 2]
  188. )
  189. portStr = data[4]
  190. status = data[6]
  191. connectStatus = data[9]
  192. fee = data[10]
  193. def _parse_warning(self, data):
  194. """
  195. 解析告警事件
  196. :param data:
  197. :return:
  198. """
  199. # TODO 对于告警信息的处理 这个地方细分种类太多, 共计255种告警信息 是否需要进行分类处理
  200. def _parse_account_info(self):
  201. """
  202. 充电桩充电上传用户帐户查询报文
  203. 这个接口主要的功能在于查询用户卡号是否OK
  204. :return:
  205. """
  206. # 对于卡片而言,目前我们只做了卡号鉴‘
  207. # 对
  208. def get_port_status_from_dev(self):
  209. """
  210. 从设备端直接获取端口状态,此款主板没有直接提供,就先从换从缓存中直接读取
  211. :return:
  212. """
  213. devCache = Device.get_dev_control_cache(self._device["devNo"])
  214. allPorts = devCache.get("allPorts")
  215. if not allPorts:
  216. allPorts, portNums = self._query_int_type_settings(3, 2)
  217. Device.update_dev_control_cache(self.device.devNo, {"allPorts": allPorts})
  218. portInfo = {}
  219. for i in xrange(allPorts):
  220. portStr = str(i+1)
  221. portTemp = devCache.get(portStr)
  222. if not portTemp: portTemp = {"status": Const.DEV_WORK_STATUS_IDLE}
  223. portInfo[portStr] = portTemp
  224. return portInfo
  225. def get_port_status(self, force = False):
  226. """
  227. 获取端口状态, 用户扫码或者经销商上分的时候会需要
  228. :param force:
  229. :return:
  230. """
  231. if force:
  232. return self.get_port_status_from_dev()
  233. devCache = Device.get_dev_control_cache(self._device['devNo'])
  234. statusDict = dict()
  235. allPorts = devCache.get('allPorts')
  236. if not allPorts:
  237. allPorts, portNums = self._query_int_type_settings(3, 2)
  238. Device.update_dev_control_cache(self.device.devNo, {"allPorts": allPorts})
  239. for portNum in range(allPorts):
  240. tempDict = devCache.get(str(portNum + 1), {})
  241. if "status" in tempDict:
  242. statusDict[str(portNum + 1)] = {'status': tempDict.get('status')}
  243. elif "isStart" in tempDict:
  244. if tempDict['isStart']:
  245. statusDict[str(portNum + 1)] = {'status': Const.DEV_WORK_STATUS_WORKING}
  246. else:
  247. statusDict[str(portNum + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  248. else:
  249. statusDict[str(portNum + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  250. allPorts, usedPorts, usePorts = self.get_port_static_info(statusDict)
  251. Device.update_dev_control_cache(self._device['devNo'], {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  252. return statusDict
  253. def start_device(self, package, openId, attachParas):
  254. """
  255. 启动设备
  256. :param package: 套餐
  257. :param openId: 用户
  258. :param attachParas: 其余参数
  259. :return:
  260. """
  261. chargeIndex = attachParas.get("chargeIndex")
  262. if not chargeIndex:
  263. raise ServiceException({"result": 2, "description": u"请选择相应的充电端口号之后再启动充电"})
  264. unit = package.get("unit")
  265. _time = package.get("time")
  266. coins = package.get("coins")
  267. def analyze_event_data(self, data):
  268. """
  269. 设备上报的事件
  270. :param data:
  271. :return:
  272. """