changyuanPower.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import time
  4. import logging
  5. from decimal import Decimal
  6. from apilib.utils_datetime import timestamp_to_dt
  7. from apps.common.utils import int_to_hex
  8. from apps.web.common.models import TempValues
  9. from apps.web.constant import Const, DeviceCmdCode, MQTT_TIMEOUT
  10. from apps.web.core.adapter.base import SmartBox, fill_2_hexByte
  11. from apps.web.core.exceptions import ServiceException
  12. from apps.web.core.networking import MessageSender
  13. from apps.web.device.models import Device
  14. logger = logging.getLogger(__name__)
  15. class ChangYuanPower(SmartBox):
  16. FINISH_REASON_MAP = {
  17. "E0": "设备过压停止",
  18. "E1": "设备过流停止",
  19. "E2": "设备超功率停止",
  20. "E3": "充电时间使用完毕",
  21. "E4": "正常结束",
  22. "E5": "支付金额使用完",
  23. "E6": "设备温度超过停止",
  24. "E7": "计量芯片通信失败",
  25. "E8": "远程停止",
  26. }
  27. PAY_TYPE_MAP = {
  28. "cash": "01",
  29. "vCard": "02",
  30. "coin": "03"
  31. }
  32. @staticmethod
  33. def _parse_D0_data(data):
  34. """
  35. 解析 设备参数 返还的数据
  36. :param data:
  37. :return:
  38. """
  39. elecPrice = int(data[6:10], 16) / 100.0
  40. cardPrice = int(data[10:14], 16) / 100.0
  41. chargeTimeMax = int(data[14:18], 16)
  42. portPowerMax = int(data[18:22], 16)
  43. powerMax = int(data[22:26], 16)
  44. powerCheckMin = int(data[26:28], 16)
  45. powerCheckTime = int(data[28:30], 16)
  46. cardFree = True if data[30:32] == 'AA' else False
  47. voice = int(data[32:34], 16)
  48. return locals()
  49. @staticmethod
  50. def _parse_F4_data(data):
  51. """
  52. 解析 请求卡同步 指令
  53. :param data:
  54. :type data:
  55. :return:
  56. :rtype:
  57. """
  58. cardType = data[6:8]
  59. cardNo = data[8:16]
  60. cardBalance = int(data[16:22], 16)
  61. if cardType == "01":
  62. cardBalance = cardBalance / 100.0
  63. return locals()
  64. @staticmethod
  65. def _parse_F3_data(data):
  66. """
  67. 解析 卡余额同步 结果
  68. :param data:
  69. :type data:
  70. :return:
  71. :rtype:
  72. """
  73. cardType = data[6:8]
  74. cardNo = data[8:16]
  75. cardBalance = int(data[16:22], 16)
  76. asyncStatus = data[22:24] == "AA"
  77. sid = int(data[24:28], 16)
  78. if cardType == "01":
  79. cardBalance = cardBalance / 100.0
  80. return locals()
  81. @staticmethod
  82. def _parse_D7_data(data):
  83. """
  84. 火警数据解析
  85. :param data:
  86. :type data:
  87. :return:
  88. :rtype:
  89. """
  90. return {'data': data[6:8]}
  91. @staticmethod
  92. def _parse_D8_data(data):
  93. """
  94. 定时上传 桩的工作状态
  95. :param data:
  96. :type data:
  97. :return:
  98. :rtype:
  99. """
  100. defaultPortInfo = {"status": Const.DEV_WORK_STATUS_IDLE}
  101. portInfo = dict.fromkeys("1234", defaultPortInfo)
  102. if len(data) == 22:
  103. voltage = int(data[6:10], 16) / 10.0
  104. temperature = int(data[12:14], 16)
  105. if data[10:12] == "01":
  106. temperature = -temperature
  107. return {"voltage": voltage, "temperature": temperature, "portInfo": portInfo}
  108. statusHex = data[6:14]
  109. leftBalanceHex = data[14:30]
  110. usedElecHex = data[30:46] if data[30:46] else '0' * 16
  111. usedTimeHex = data[46:62] if data[30:46] else '0' * 16
  112. powerHex = data[62:78]
  113. voltage = int(data[-16:-12], 16) / 10.0
  114. temperature = int(data[-10:-8], 16)
  115. if data[-12:-10] == "01":
  116. temperature = -temperature
  117. for portNum in range(4):
  118. item = {}
  119. tempStatus = statusHex[portNum * 2:portNum * 2 + 2]
  120. tempLeftBalance = int(leftBalanceHex[portNum * 4: portNum * 4 + 4], 16) / 100.0
  121. tempUsedElec = int(usedElecHex[portNum * 4: portNum * 4 + 4], 16) / 100.0 or '0'
  122. tempUsedTime = int(usedTimeHex[portNum * 4: portNum * 4 + 4], 16)
  123. tempPower = int(powerHex[portNum * 4: portNum * 4 + 4], 16)
  124. if tempStatus == "00":
  125. status = Const.DEV_WORK_STATUS_IDLE
  126. elif tempStatus == "01":
  127. status = Const.DEV_WORK_STATUS_WORKING
  128. elif tempStatus == "04":
  129. status = Const.DEV_WORK_STATUS_FAULT_RELAY_CONNECT
  130. else:
  131. status = Const.DEV_WORK_STATUS_IDLE
  132. item["status"] = status
  133. item["leftMoney"] = tempLeftBalance
  134. item["usedElec"] = tempUsedElec
  135. item["usedTime"] = tempUsedTime
  136. item["power"] = tempPower
  137. portInfo[str(portNum + 1)] = item.copy()
  138. return {"voltage": voltage, "temperature": temperature, "portInfo": portInfo}
  139. @staticmethod
  140. def _parse_D8_data_two_port(data):
  141. """
  142. 定时上传 桩的工作状态
  143. :param data:
  144. :type data:
  145. :return:
  146. :rtype:
  147. """
  148. defaultPortInfo = {"status": Const.DEV_WORK_STATUS_IDLE}
  149. portInfo = dict.fromkeys("12", defaultPortInfo)
  150. if len(data) == 22:
  151. voltage = int(data[6:10], 16) / 10.0
  152. temperature = int(data[12:14], 16)
  153. if data[10:12] == "01":
  154. temperature = -temperature
  155. return {"voltage": voltage, "temperature": temperature, "portInfo": portInfo}
  156. statusHex = data[6:14]
  157. leftBalanceHex = data[14:30]
  158. usedElecHex = data[30:46] if data[30:46] else '0' * 16
  159. usedTimeHex = data[46:62] if data[30:46] else '0' * 16
  160. powerHex = data[62:78]
  161. voltage = int(data[-16:-12], 16) / 10.0
  162. temperature = int(data[-10:-8], 16)
  163. if data[-12:-10] == "01":
  164. temperature = -temperature
  165. for portNum in range(2):
  166. item = {}
  167. tempStatus = statusHex[portNum * 2:portNum * 2 + 2]
  168. tempLeftBalance = int(leftBalanceHex[portNum * 4: portNum * 4 + 4], 16) / 100.0
  169. tempUsedElec = int(usedElecHex[portNum * 4: portNum * 4 + 4], 16) / 100.0 or '0'
  170. tempUsedTime = int(usedTimeHex[portNum * 4: portNum * 4 + 4], 16)
  171. tempPower = int(powerHex[portNum * 4: portNum * 4 + 4], 16)
  172. if tempStatus == "00":
  173. status = Const.DEV_WORK_STATUS_IDLE
  174. elif tempStatus == "01":
  175. status = Const.DEV_WORK_STATUS_WORKING
  176. elif tempStatus == "04":
  177. status = Const.DEV_WORK_STATUS_FAULT_RELAY_CONNECT
  178. else:
  179. status = Const.DEV_WORK_STATUS_IDLE
  180. item["status"] = status
  181. item["leftMoney"] = tempLeftBalance
  182. item["usedElec"] = tempUsedElec
  183. item["usedTime"] = tempUsedTime
  184. item["power"] = tempPower
  185. portInfo[str(portNum + 1)] = item.copy()
  186. return {"voltage": voltage, "temperature": temperature, "portInfo": portInfo}
  187. @staticmethod
  188. def _parse_D9_data(data):
  189. """
  190. 充电开始 上传
  191. :param data:
  192. :type data:
  193. :return:
  194. :rtype:
  195. """
  196. cardNo = data[6:14]
  197. cardBalance = int(data[14:20], 16) / 100.0
  198. allPayMoney = int(data[20:24], 16) / 100.0
  199. payMoney = int(data[24:28], 16) / 100.0
  200. portStr = str(int(data[28:30], 16))
  201. return locals()
  202. @staticmethod
  203. def _parse_DA_data(data):
  204. """
  205. 充电结束上传
  206. :param data:
  207. :type data:
  208. :return:
  209. :rtype:
  210. """
  211. portStr = str(int(data[6:8], 16))
  212. reasonCode = data[8:10]
  213. cardNo = data[10:18]
  214. usedElec = int(data[18:22], 16) / 100.0 # 本单结束 该单使用电量 单位:度
  215. usedTime = int(data[22:26], 16) # 本单结束,该单使用时间 单位:分钟
  216. leftBalance = int(data[26:30], 16) / 100.0 # 本单结束,该单剩余金额 单位:元
  217. desc = ChangYuanPower.FINISH_REASON_MAP.get(reasonCode, u"未知停止方式")
  218. return locals()
  219. @staticmethod
  220. def _parse_DF_data(data):
  221. """
  222. 实体卡返费 的指令
  223. :param data:
  224. :type data:
  225. :return:
  226. :rtype:
  227. """
  228. cardNo = data[6:14]
  229. beforeRefund = int(data[14:20], 16) / 100.0
  230. refund = int(data[20:24], 16) / 100.0
  231. afterRefund = int(data[24:30], 16) / 100.0
  232. return locals()
  233. @staticmethod
  234. def _parse_COMMON_data(data):
  235. """
  236. 解析 通用的数据返回 一般表示成功还是失败
  237. :param data:
  238. :type data:
  239. :return:
  240. :rtype:
  241. """
  242. return True if data[6:10] == "4F4B" else False
  243. @staticmethod
  244. def _parse_DE_data(data):
  245. """
  246. 解析 10 个未返费的卡号以及剩余金额
  247. :param data:
  248. :type data:
  249. :return:
  250. :rtype:
  251. """
  252. return
  253. def _send_data(self, funCode, data, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, timeout = MQTT_TIMEOUT.NORMAL):
  254. result = MessageSender.send(device = self.device, cmd = cmd, payload = {
  255. "IMEI": self.device.devNo,
  256. "funCode": funCode,
  257. "data": data
  258. }, timeout = timeout)
  259. if result["rst"] != 0:
  260. if result['rst'] == -1:
  261. raise ServiceException({'result': 2, 'description': u'充电桩正在玩命找网络,请稍候再试'})
  262. elif result['rst'] == 1:
  263. raise ServiceException({'result': 2, 'description': u'充电桩主板连接故障'})
  264. else:
  265. raise ServiceException({'result': 2, 'description': u'系统错误'})
  266. return result
  267. def _start(self, payType, payMoney, port):
  268. """
  269. 启动设备
  270. :param payType:
  271. :param payMoney:
  272. :return:
  273. """
  274. data = self.PAY_TYPE_MAP[payType]
  275. data += int_to_hex(int(float(payMoney) * 100))
  276. data += "0000"
  277. data += '%.2d' % int(port)
  278. result = self._send_data("D2", data, timeout = MQTT_TIMEOUT.START_DEVICE)
  279. if "data" not in result or not ChangYuanPower._parse_COMMON_data(result.get("data")):
  280. raise ServiceException({"result": "2", "description": u"设备启动错误,请联系经销商协助解决"})
  281. return result
  282. @staticmethod
  283. def transform_password(password):
  284. passwordHex = ""
  285. while password:
  286. passwordHex += fill_2_hexByte(hex(int(password[: 2])), 2)
  287. password = password[2:]
  288. return passwordHex
  289. def _set_password(self, password):
  290. """
  291. 设置设备的小区密码
  292. 密码是否还需要再校验,以及是否是0开头
  293. """
  294. if not password.isdigit():
  295. raise ServiceException({"result": "2", "description": u"密码必须必须为0-9数字"})
  296. if len(password) != 10:
  297. raise ServiceException({"result": 0, "description": u"密码长度必须为10位"})
  298. passwordHex = self.transform_password(password)
  299. result = self._send_data("DB", data = passwordHex)
  300. if "data" not in result or not ChangYuanPower._parse_COMMON_data(result.get("data")):
  301. raise ServiceException({"result": "2", "description": u"设置小区密码失败,请重新试试"})
  302. return result
  303. def _set_send_card(self, oldPassword, newPassword):
  304. """
  305. 设置卡机模式 用于重置 实体卡的小区密码
  306. """
  307. if not oldPassword.isdigit() or not newPassword.isdigit():
  308. raise ServiceException({"result": "2", "description": u"密码必须必须为0-9数字"})
  309. if len(oldPassword) != 10 or len(newPassword) != 10:
  310. raise ServiceException({"result": 0, "description": u"密码长度必须为10位"})
  311. oldPasswordHex = self.transform_password(oldPassword)
  312. newPasswordHex = self.transform_password(newPassword)
  313. result = self._send_data("DC", data = oldPasswordHex + newPasswordHex)
  314. if "data" not in result or not ChangYuanPower._parse_COMMON_data(result.get("data")):
  315. raise ServiceException({"result": "2", "description": u"设置发卡机模式失败,请重新试试"})
  316. return result
  317. def _reboot_device(self):
  318. result = self._send_data("D5", "0000")
  319. if "data" not in result or not ChangYuanPower._parse_COMMON_data(result.get("data")):
  320. raise ServiceException({"result": "2", "description": u"设备复位错误,请重新试试"})
  321. return result
  322. def _get_not_refund_record(self):
  323. """
  324. 获取 10 条 未返费的卡号以及剩余金额
  325. :return:
  326. """
  327. result = self._send_data("DE", "00")
  328. data = result.get("data", "")
  329. if ChangYuanPower._parse_COMMON_data(data):
  330. raise ServiceException({"result": "2", "description": u"获取未返费记录错误,请重新试试"})
  331. noRefund = list()
  332. data = data[6: -8]
  333. for i in xrange(10):
  334. tempData = data[12 * i: 12 * (i + 1)]
  335. cardNo = tempData[: 8]
  336. amount = tempData[8: 12]
  337. if cardNo == "FFFFFFFF" or cardNo == "00000000":
  338. continue
  339. amount = int(amount, 16) / 100.0
  340. noRefund.append({"cardNo": cardNo, "amount": amount})
  341. return noRefund
  342. def _async_card_balance(self, cardType, cardNo, asyncMoney):
  343. """
  344. 同步卡内余额
  345. :param cardType:
  346. :param cardNo:
  347. :param asyncMoney:
  348. :return:
  349. """
  350. balance = asyncMoney if cardType == "00" else asyncMoney * 100
  351. # 获取随机流水号
  352. sidKey = "{}-{}".format(self.device.devNo, cardNo)
  353. sid = TempValues.get(sidKey)
  354. balanceHex = int_to_hex(int(balance), 6)
  355. sidHex = int_to_hex(int(sid))
  356. MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, payload = {
  357. "IMEI": self.device.devNo,
  358. "funCode": "F3",
  359. "data": cardType + balanceHex + sidHex
  360. })
  361. def _ack_finished_massage(self, daid):
  362. return MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, payload = {
  363. "IMEI": self.device.devNo,
  364. "data": "",
  365. "cmd": 220,
  366. "daid": daid,
  367. "funCode": "FA"})
  368. # 获取10条未返费的记录 暂时未使用
  369. def _no_refund_record(self):
  370. result = self._send_data("DE", "00")
  371. data = result["data"]
  372. noRefund = list()
  373. data = data[6: -8]
  374. for i in xrange(0, 120, 12):
  375. tempData = data[i:i + 12]
  376. cardNo = tempData[:8]
  377. amount = tempData[8:]
  378. if cardNo == "FFFFFFFF" or cardNo == "00000000": continue
  379. amount = int(amount, 16) / 100.0
  380. noRefund.append({"cardNo": cardNo, "amount": amount})
  381. return noRefund
  382. def _set_all_settings(self,newSetConf,lastSetConf):
  383. elecPrice = newSetConf.POST.get("elecPrice") or lastSetConf.get("elecPrice")
  384. cardPrice = newSetConf.POST.get("cardPrice") or lastSetConf.get("cardPrice")
  385. chargeTimeMax = newSetConf.POST.get("chargeTimeMax") or lastSetConf.get("chargeTimeMax")
  386. portPowerMax = newSetConf.POST.get("portPowerMax") or lastSetConf.get("portPowerMax")
  387. powerMax = newSetConf.POST.get("powerMax") or lastSetConf.get("powerMax")
  388. powerCheckMin = newSetConf.POST.get("powerCheckMin") or lastSetConf.get("powerCheckMin")
  389. powerCheckTime = newSetConf.POST.get("powerCheckTime") or lastSetConf.get("powerCheckTime")
  390. cardFree = newSetConf.POST.get("cardFree") or '00' if lastSetConf.get("powerCheckTime") else 'AA'
  391. voice = newSetConf.POST.get("voice") or lastSetConf.get("voice")
  392. if float(elecPrice) < 0 or float(elecPrice) > 5.00:
  393. raise ServiceException({"result": "2", "description": u"电价设置范围为0-5.00元"})
  394. if float(cardPrice) < 1 or float(cardPrice) > 650:
  395. raise ServiceException({"result": "2", "description": u"刷卡预扣金额范围为1-650元"})
  396. if int(chargeTimeMax) < 0 or int(chargeTimeMax) > 9999:
  397. raise ServiceException({"result": "2", "description": u"单次充电时间设置范围为0-9999分钟"})
  398. if int(portPowerMax) < 0 or int(portPowerMax) > 14000:
  399. raise ServiceException({"result": "2", "description": u"单路功率设置范围为0-14000瓦"})
  400. if int(powerMax) < 0 or int(powerMax) > 55000:
  401. raise ServiceException({"result": "2", "description": u"总计功率设置范围为0-55000瓦"})
  402. if int(powerCheckMin) < 0 or int(powerCheckMin) > 120:
  403. raise ServiceException({"result": "2", "description": u"功率检测范围设置范围为0-120瓦"})
  404. if int(powerCheckTime) < 0 or int(powerCheckTime) > 255:
  405. raise ServiceException({"result": "2", "description": u"最小功率检测时间设置范围为0-255秒"})
  406. if int(voice) < 0 or int(voice) > 8:
  407. raise ServiceException({"result": "2", "description": u"喇叭音量设置范围为0-8"})
  408. data = int_to_hex(int(Decimal(elecPrice) * 100))
  409. data += int_to_hex(int(Decimal(cardPrice)) * 100)
  410. data += int_to_hex(int(chargeTimeMax))
  411. data += int_to_hex(int(portPowerMax))
  412. data += int_to_hex(int(powerMax))
  413. data += int_to_hex(int(powerCheckMin), 2)
  414. data += int_to_hex(int(powerCheckTime), 2)
  415. data += cardFree
  416. data += int_to_hex(int(voice), 2)
  417. result = self._send_data("D1", data)
  418. if "data" not in result or not ChangYuanPower._parse_COMMON_data(result.get("data")):
  419. raise ServiceException({"result": "2", "description": u"设置设备参数错误,请联系经销商协助解决"})
  420. return result
  421. def test(self, coins,port):
  422. """
  423. 测试端口 测试机器启动 固定端口为 01
  424. {
  425. "IMEI": "865650040606119",
  426. "cmd": 210,
  427. "data": "0303E8000001",
  428. "funCode": "D2"
  429. },
  430. """
  431. return self._start('coin', coins, port)
  432. def get_dev_setting(self):
  433. """
  434. 获取设备参数
  435. :return:
  436. """
  437. result = self._send_data("D0", "00")
  438. data = result.get("data")
  439. devSetting = self._parse_D0_data(data)
  440. disable = self.device.get('otherConf',{}).get('disableDevice')
  441. needBindCard = self._device.get("otherConf", dict()).get("needBindCard", True)
  442. devSetting.update({'disable': disable, 'needBindCard': needBindCard})
  443. dev_control_cache = Device.get_dev_control_cache(self.device.devNo)
  444. devSetting['temperature'] = dev_control_cache.get('temperature', "获取中...")
  445. devSetting['voltage'] = dev_control_cache.get('voltage', "获取中...")
  446. return devSetting
  447. def set_device_function_param(self, request, lastSetConf):
  448. # 设置小区密码
  449. pwd = request.POST.get('pwd')
  450. if pwd:
  451. return self._set_password(pwd)
  452. # 设置发卡机模式
  453. old_pwd = request.POST.get('old_pwd')
  454. new_pwd = request.POST.get('new_pwd')
  455. if old_pwd and new_pwd:
  456. return self._set_send_card(old_pwd, new_pwd)
  457. # 设置基本参数
  458. else:
  459. return self._set_all_settings(request,lastSetConf)
  460. def set_device_function(self, request, lastSetConf):
  461. if request.POST.has_key("cardFree"):
  462. cardFree = request.POST.get('cardFree')
  463. cardFree = 'AA' if cardFree else '00'
  464. request.POST['cardFree'] = cardFree
  465. return self._set_all_settings(request, lastSetConf)
  466. if request.POST.has_key("reboot"):
  467. self._reboot_device()
  468. if request.POST.has_key("disable"):
  469. self.set_dev_disable(request.POST.get("disable"))
  470. if "needBindCard" in request.POST:
  471. needBindCard = request.POST.get('needBindCard')
  472. otherConf = self.device.get("otherConf", dict())
  473. otherConf.update({"needBindCard": needBindCard})
  474. Device.objects.get(devNo=self.device.devNo).update(otherConf=otherConf)
  475. Device.invalid_device_cache(self.device.devNo)
  476. def get_port_status(self, force = False):
  477. """
  478. 获取设备状态 昌原的状态都是被动获取的
  479. :param force:
  480. :return:
  481. """
  482. devCache = Device.get_dev_control_cache(self._device['devNo'])
  483. statusDict = dict()
  484. only_two_port = self.device.devType.get("features", {}).get('only_two_port', False)
  485. if only_two_port:
  486. allPorts = 2
  487. else:
  488. allPorts = 4
  489. for portNum in range(allPorts):
  490. tempDict = devCache.get(str(portNum + 1), {})
  491. if "status" in tempDict:
  492. statusDict[str(portNum + 1)] = {'status': tempDict.get('status')}
  493. elif "isStart" in tempDict:
  494. if tempDict['isStart']:
  495. statusDict[str(portNum + 1)] = {'status': Const.DEV_WORK_STATUS_WORKING}
  496. else:
  497. statusDict[str(portNum + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  498. else:
  499. statusDict[str(portNum + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  500. allPorts, usedPorts, usePorts = self.get_port_static_info(statusDict)
  501. Device.update_dev_control_cache(self._device['devNo'],
  502. {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  503. return statusDict
  504. def get_port_status_from_dev(self):
  505. return self.get_port_info(False)
  506. def get_port_info(self, port):
  507. """
  508. 获取 端口的详细信息
  509. :param port:
  510. :return:
  511. """
  512. devCache = Device.get_dev_control_cache(self.device.devNo)
  513. return devCache.get(str(port), dict())
  514. @property
  515. def isHaveStopEvent(self):
  516. return True
  517. def set_dev_disable(self, disable):
  518. if disable:
  519. status = "00AA"
  520. else:
  521. status = "0055"
  522. result = self._send_data(funCode = "DD", data = status)
  523. if "data" not in result or not ChangYuanPower._parse_COMMON_data(result.get("data")):
  524. raise ServiceException({"result": "2", "description": u"设备停用错误,请联系厂商协助解决"})
  525. otherConf = self._device.get("otherConf", {})
  526. otherConf["disableDevice"] = disable
  527. Device.objects.filter(devNo = self._device["devNo"]).update(otherConf = otherConf)
  528. Device.invalid_device_cache(self._device["devNo"])
  529. def stop(self,port):
  530. self.stop_charging_port(port)
  531. def stop_charging_port(self, port):
  532. portHex = fill_2_hexByte(hex(int(port)), 2)
  533. result = self._send_data('D6', data = portHex)
  534. data = result.get("data")
  535. if data[6: 8] != "4F":
  536. raise ServiceException({"result": 2, "description": u"停止充电失败,请重新试试"})
  537. # 这里只下发命令 不清理端口缓存,等上报事件清除
  538. # Device.clear_port_control_cache(self.device.devNo,int(port))
  539. def start_device(self, package, openId, attachParas):
  540. chargeIndex = attachParas.get("chargeIndex")
  541. if not chargeIndex:
  542. raise ServiceException({"result": 2, "description": u"请选择正确的充电端口"})
  543. # TODO zjl 套餐单位是否正确
  544. unit = package.get("unit")
  545. coins = "{:.2f}".format(float(package.get("coins")))
  546. price = "{:.2f}".format(float(package.get("price")))
  547. rechargeRcdId = attachParas.get("linkedRechargeRecordId") # 这是个对象
  548. orderNo = attachParas.get("orderNo")
  549. startTimeStamp = int(time.time())
  550. # 先不考虑 续充的问题 后续做扩展
  551. portCache = {
  552. "openId": openId,
  553. "isStart": True,
  554. "coins": coins,
  555. "allPayMoney": 0,
  556. "orderNo": orderNo,
  557. "startTime": timestamp_to_dt(startTimeStamp).strftime("%Y-%m-%d %H:%M:%S"),
  558. "status": Const.DEV_WORK_STATUS_WORKING,
  559. "consumeType": "mobile", # 用来显示的 显示在端口管理里面
  560. }
  561. if self._vcard_id:
  562. portCache.update({"vCardId": self._vcard_id})
  563. payType = "vCard"
  564. consumeType = "mobile_vcard"
  565. allPayMoney = 0
  566. elif rechargeRcdId:
  567. portCache.update({"rechargeRcdId": str(rechargeRcdId)})
  568. payType = "cash"
  569. consumeType = "mobile"
  570. allPayMoney = price
  571. else:
  572. payType = "coin"
  573. allPayMoney = 0
  574. consumeType = "mobile"
  575. # TODO payType是控制退费的 consumeType 是控制端口管理里面显示的
  576. portCache.update({"payType": payType, 'allPayMoney': allPayMoney,'consumeType':consumeType})
  577. result = self._start(payType, coins, chargeIndex)
  578. result["finishedTime"] = startTimeStamp + 24 * 60 * 60
  579. Device.update_dev_control_cache(self.device.devNo, {str(chargeIndex): portCache,"consumeType": consumeType})
  580. return result
  581. def analyze_event_data(self, data):
  582. """
  583. 解析事件
  584. :param data:
  585. :return:
  586. """
  587. cmdCode = data[2:4]
  588. if cmdCode == "F3":
  589. eventData = ChangYuanPower._parse_F3_data(data)
  590. elif cmdCode == "F4":
  591. eventData = ChangYuanPower._parse_F4_data(data)
  592. elif cmdCode == "D7":
  593. eventData = ChangYuanPower._parse_D7_data(data)
  594. elif cmdCode == "D8":
  595. only_two_port = self.device.devType.get("features", {}).get('only_two_port', False)
  596. if not only_two_port:
  597. eventData = ChangYuanPower._parse_D8_data(data)
  598. else:
  599. eventData = ChangYuanPower._parse_D8_data_two_port(data)
  600. elif cmdCode == "D9":
  601. eventData = ChangYuanPower._parse_D9_data(data)
  602. elif cmdCode == "DA":
  603. eventData = ChangYuanPower._parse_DA_data(data)
  604. elif cmdCode == "DF":
  605. eventData = ChangYuanPower._parse_DF_data(data)
  606. else:
  607. logger.error("error cmdCode <{}>, data is <{}>".format(cmdCode, data))
  608. return
  609. eventData.update({"cmdCode": cmdCode})
  610. return eventData
  611. def get_device_function_by_key(self, data):
  612. if data == 'noRefund':
  613. res = self._no_refund_record()
  614. if not res:
  615. return {}
  616. return {'noRefund': res}
  617. def active_deactive_port(self, port, active):
  618. if not active:
  619. self.stop_charging_port(port)
  620. else:
  621. raise ServiceException({'result': 2, 'description': u'此设备不支持直接打开端口'})