baojia.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823
  1. # -*- coding: utf-8 -*-
  2. """
  3. --- 电量修改 zjl
  4. --- 主要修改功能 增加电量模式 根据不通的套餐单位 决定当前的计费模式
  5. """
  6. import datetime
  7. import logging
  8. import time
  9. from apilib.utils_datetime import timestamp_to_dt
  10. from apps.web.constant import Const, DeviceCmdCode, MQTT_TIMEOUT
  11. from apps.web.core.adapter.base import SmartBox, fill_2_hexByte
  12. from apps.web.core.exceptions import ServiceException
  13. from apps.web.core.networking import MessageSender
  14. from apps.web.dealer.models import Dealer
  15. from apps.web.device.models import Device
  16. from apps.web.core.device_define.baojia import CMD_CODE, DEFAULT_PARAM, BILLING_TYPE
  17. from apps.web.user.models import MyUser
  18. logger = logging.getLogger(__name__)
  19. class ChargingBAOJIABox(SmartBox):
  20. CHARGE_CARD_REASON_MAP = {
  21. "00": u"充值成功",
  22. "01": u"读卡充值",
  23. "02": u"卡片未注册",
  24. "03": u"读卡器故障",
  25. "04": u"主板故障"
  26. }
  27. PORT_STATUS_MAP = {
  28. "01": Const.DEV_WORK_STATUS_IDLE,
  29. "02": Const.DEV_WORK_STATUS_WORKING,
  30. "03": Const.DEV_WORK_STATUS_FORBIDDEN,
  31. "04": Const.DEV_WORK_STATUS_FAULT
  32. }
  33. FINISHED_REASON_MAP = {
  34. '00': u'购买的充电时间或电量用完了。',
  35. '01': u'可能是插头被拔掉,或者电瓶已经充满。系统判断为异常断电,由于电瓶车充电器种类繁多,可能存在误差。如有问题,请您及时联系商家协助解决问题并恢复充电。',
  36. '02': u'恭喜您!电池已经充满电!',
  37. '03': u'警告!您的电池超功率,已经停止充电,为了公共安全,不建议您在该充电桩充电!提醒您,为了安全大功率的电池不要放入楼道、室内等位置充电哦',
  38. '04': u'远程断电。',
  39. '0B': u'设备或端口出现问题,为了安全起见,被迫停止工作。建议您根据已经充电的时间评估是否需要到现场换到其他端口充电。'
  40. }
  41. def __init__(self, device):
  42. super(ChargingBAOJIABox, self).__init__(device)
  43. @staticmethod
  44. def _parse_card_09(data):
  45. """
  46. 09命令 设备至模块 在线查询卡余额
  47. :return:
  48. """
  49. cardNoHex = data[6:14]
  50. return {"cardNo": str(int(cardNoHex, 16)), "cardNoHex": cardNoHex}
  51. @staticmethod
  52. def _parse_card_13(data):
  53. """
  54. 通知后台线下卡的消费
  55. :param data:
  56. :return:
  57. """
  58. cardNoHex = data[6:14]
  59. payType = data[14:16]
  60. money = int(data[16:18], 16) / 10.0
  61. cardType = data[18:20]
  62. balance = int(data[20:24], 16) / 10.0
  63. return {
  64. "cardNo": str(int(cardNoHex, 16)),
  65. "payType": payType,
  66. "money": money,
  67. "cardType": cardType,
  68. "balance": balance,
  69. "cardNoHex": cardNoHex
  70. }
  71. @staticmethod
  72. def _parse_card_27(data):
  73. """
  74. ID 卡消费确认
  75. :param data:
  76. :return:
  77. """
  78. portStr = str(int(data[6:8], 16))
  79. cardNo = str(int(data[8:16], 16))
  80. money = int(data[16:20], 16) / 10.0
  81. _time = int(data[20:24], 16)
  82. elec = int(data[24:28], 16) / 100.0
  83. return {
  84. "portStr": portStr,
  85. "cardNo": cardNo,
  86. "money": money,
  87. "time": _time,
  88. "elec": elec
  89. }
  90. @staticmethod
  91. def _parse_card_12(data):
  92. """
  93. 解析远程卡充值的情况
  94. :param data:
  95. :return:
  96. """
  97. cardNo = str(int(data[6:14], 16))
  98. money = str(int(data[16:20], 16) / 10.0)
  99. balance = str(int(data[20:24], 16) / 10.0)
  100. res = data[24:26]
  101. reasonCode = data[26:28]
  102. reason = ChargingBAOJIABox.CHARGE_CARD_REASON_MAP.get(reasonCode, "")
  103. return {
  104. "cardNo": cardNo,
  105. "money": money,
  106. "balance": balance,
  107. "res": res,
  108. "reason": reason
  109. }
  110. @staticmethod
  111. def _parse_port_status_21(data):
  112. """
  113. 解析上报的21数据 端口实时状态
  114. :param data:
  115. :return:
  116. """
  117. portNum = int(data[6:8], 16)
  118. portStatusDict = dict()
  119. tempData = data[8:]
  120. while portNum:
  121. tempPort = str(int(tempData[:2], 16))
  122. tempStatus = ChargingBAOJIABox.PORT_STATUS_MAP.get(tempData[2:4], Const.DEV_WORK_STATUS_IDLE)
  123. tempTime = int(tempData[4:8], 16)
  124. tempPower = int(tempData[8:12], 16)
  125. tempElec = int(tempData[12:16], 16) * 0.01
  126. portStatusDict[tempPort] = {
  127. "status": tempStatus,
  128. "power": tempPower,
  129. }
  130. if tempTime:
  131. portStatusDict[tempPort].update({"leftTime": tempTime})
  132. if tempElec:
  133. portStatusDict[tempPort].update({"leftElec": tempElec})
  134. portNum -= 1
  135. tempData = tempData[16:]
  136. return portStatusDict
  137. @staticmethod
  138. def _parse_finished(data):
  139. """
  140. 解析结束上报过来的是数据
  141. :param data:
  142. :return:
  143. """
  144. portStr = str(int(data[6:8], 16))
  145. leftMoney = int(data[8:12], 16) / 10.0
  146. leftTime = int(data[12:16], 16)
  147. leftElec = int(data[16:20], 16) / 100.0
  148. reasonCode = data[20:22]
  149. return {
  150. "portStr": portStr,
  151. "leftMoney": leftMoney,
  152. "leftTime": leftTime,
  153. "leftElec": leftElec,
  154. "reasonCode": reasonCode,
  155. "reason": ChargingBAOJIABox.FINISHED_REASON_MAP.get(reasonCode, u"未知原因")
  156. }
  157. @staticmethod
  158. def _check_charge_package(package):
  159. """
  160. 检查 套餐的配置 目前不支持按电量计费
  161. :param package:
  162. :return:
  163. """
  164. def _remote_charge_card(self, money):
  165. """
  166. # 充值
  167. :param money:
  168. :return:
  169. """
  170. cardType = "02"
  171. moneyHex = fill_2_hexByte(hex(int(float(money) * 10)), 4)
  172. self._send_data("12", cardType + moneyHex, cmd = DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
  173. def _restart(self):
  174. self._send_data("05", "FF01", cmd = DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
  175. def _stop(self, port):
  176. portStr = fill_2_hexByte(hex(int(port)), 2)
  177. self._send_data("05", portStr + "03", cmd = DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
  178. def _get_device_consume_count(self):
  179. """
  180. 从设备侧获取 消费总额的统计
  181. :return:
  182. """
  183. result = self._send_data("07", "0000")
  184. data = result.get("data", "")
  185. cardFee = int(data[6:10], 16) / 10.0
  186. coinFee = int(data[10:14], 16)
  187. return {
  188. "cardFee": cardFee,
  189. 'coinFee': coinFee
  190. }
  191. def _response_card_balance(self, cardNoHex, balance):
  192. """
  193. 回复主板 卡的余额09指令
  194. :param cardNoHex:
  195. :param balance:
  196. :return:
  197. """
  198. balanceHex = fill_2_hexByte(hex(int(balance * 10)), 4)
  199. self._send_data("09", cardNoHex + balanceHex, cmd = DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
  200. def _response_card_consume(self, cardNoHex, cardMoney, cardType, status):
  201. """
  202. 回复主板 卡的消费的13指令
  203. :param cardNoHex:
  204. :param cardMoney:
  205. :param cardType:
  206. :param status:
  207. :return:
  208. """
  209. cardMoneyHex = fill_2_hexByte(hex(int(cardMoney * 10)), 4)
  210. sendData = cardNoHex + cardMoneyHex + cardType + status
  211. self._send_data("13", sendData, cmd = DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
  212. def _send_data(self, funCode, data = None, cmd = None, timeout = MQTT_TIMEOUT.NORMAL):
  213. """
  214. 发送数据到mqtt服务器
  215. :param funCode:
  216. :param cmd:
  217. :param data:
  218. :param timeout:
  219. :return:
  220. """
  221. if cmd is None:
  222. cmd = DeviceCmdCode.OPERATE_DEV_SYNC
  223. if data is None:
  224. data = "00"
  225. result = MessageSender.send(device = self.device, cmd = cmd, payload = {
  226. "IMEI": self.device.devNo,
  227. "funCode": funCode,
  228. "data": data
  229. }, timeout = timeout)
  230. if "rst" in result and result.get("rst") != 0:
  231. if result.get("rst") == -1:
  232. raise ServiceException({"result": 2, "description": u"网络故障,请重新试试"})
  233. if result.get("rst") == 1:
  234. raise ServiceException({"result": 2, "description": u"充电桩无响应,请稍后再试试"})
  235. return result
  236. @staticmethod
  237. def _suit_package_by_unit(package):
  238. goodsUnit = package.get("unit")
  239. if goodsUnit not in [u"度", u"分钟", u"小时", u"秒"]:
  240. raise ServiceException({"result": 2, "description": "套餐单位错误,联系设备经销商"})
  241. coins = float(package.get("coins"))
  242. goodsNum = float(package.get("time"))
  243. billingType = BILLING_TYPE.TIME
  244. _time = 0xFFFF
  245. elec = 0xFFFF
  246. if goodsUnit == u"秒":
  247. _time = goodsNum / 60.0
  248. elif goodsUnit == u"分钟":
  249. _time = goodsNum
  250. elif goodsUnit == u"小时":
  251. _time = goodsNum * 60
  252. else:
  253. elec = goodsNum * 100
  254. billingType = BILLING_TYPE.ELEC
  255. return coins, _time, elec, billingType
  256. def remote_charge_card(self, price, rechargeRecord=None):
  257. """
  258. 对接上层接口,发卡机
  259. :param price: 充值的金额
  260. :param rechargeRecord: 充值记录
  261. :return:
  262. """
  263. self._remote_charge_card(price)
  264. def get_port_status_from_dev(self):
  265. """
  266. 从设备端获取 端口状态
  267. :return:
  268. """
  269. devInfo = self._send_data("0F", "00")
  270. data = devInfo['data'][4::]
  271. result = {}
  272. portNum = int(data[2:4], 16)
  273. portData = data[4::]
  274. ii = 0
  275. while ii < portNum:
  276. port = int(portData[ii * 4:ii * 4 + 2], 16)
  277. statusTemp = portData[ii * 4 + 2:ii * 4 + 4]
  278. if statusTemp == '01':
  279. status = {'status': Const.DEV_WORK_STATUS_IDLE}
  280. elif statusTemp == '02':
  281. status = {'status': Const.DEV_WORK_STATUS_WORKING}
  282. elif statusTemp == '03':
  283. status = {'status': Const.DEV_WORK_STATUS_FORBIDDEN}
  284. elif statusTemp == '04':
  285. status = {'status': Const.DEV_WORK_STATUS_FAULT}
  286. else:
  287. status = {'status': Const.DEV_WORK_STATUS_IDLE}
  288. ii += 1
  289. result[str(port)] = status
  290. allPorts, usedPorts, usePorts = self.get_port_static_info(result)
  291. Device.update_dev_control_cache(self._device['devNo'],
  292. {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  293. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  294. for strPort, info in result.items():
  295. if ctrInfo.has_key(strPort):
  296. ctrInfo[strPort].update({'status': info['status']})
  297. else:
  298. ctrInfo[strPort] = info
  299. Device.update_dev_control_cache(self._device['devNo'], ctrInfo)
  300. return result
  301. def analyze_event_data(self, data):
  302. cmdCode = data[4:6]
  303. if cmdCode == CMD_CODE.DEVICE_SUBMIT_CHARGE_FINISHED_16:
  304. eventData = self._parse_finished(data)
  305. eventData.update({"cmdCode": cmdCode})
  306. return eventData
  307. elif cmdCode == CMD_CODE.DEVICE_SUBMIT_DEVICE_FAULT_0A:
  308. port = int(data[6:8], 16)
  309. errCode = int(data[8:12], 16)
  310. return {
  311. 'status': Const.DEV_WORK_STATUS_FAULT,
  312. 'statusInfo': u'设备故障',
  313. 'cmdCode': cmdCode,
  314. 'port': port,
  315. 'FaultCode': errCode
  316. }
  317. # 以下都是宝佳电子 1.7 版本新增的命令
  318. elif cmdCode == CMD_CODE.CARD_BALANCE_09:
  319. eventData = self._parse_card_09(data)
  320. eventData.update({"cmdCode": cmdCode})
  321. return eventData
  322. elif cmdCode == CMD_CODE.CARD_CONSUME_13:
  323. eventData = self._parse_card_13(data)
  324. eventData.update({"cmdCode": cmdCode})
  325. return eventData
  326. elif cmdCode == CMD_CODE.CARD_CONSUME_SURE_27:
  327. eventData = self._parse_card_27(data)
  328. eventData.update({"cmdCode": cmdCode})
  329. return eventData
  330. elif cmdCode == CMD_CODE.CARD_REMOTE_CHARGE_12:
  331. eventData = self._parse_card_12(data)
  332. eventData.update({"cmdCode": cmdCode})
  333. return eventData
  334. elif cmdCode == CMD_CODE.PORT_STATUS_21:
  335. portInfo = self._parse_port_status_21(data)
  336. eventData = {
  337. "portInfo": portInfo,
  338. "cmdCode": cmdCode
  339. }
  340. return eventData
  341. @property
  342. def isHaveStopEvent(self):
  343. return True
  344. def translate_funcode(self, funCode):
  345. funCodeDict = {
  346. '01': u'获取端口数量',
  347. '02': u'获取端口数据',
  348. '14': u'移动支付',
  349. '07': u'获取刷卡投币统计数据',
  350. '15': u'获取设备端口详情',
  351. '0F': u'获取端口状态',
  352. '0C': u'端口锁操作',
  353. '0D': u'端口开关',
  354. '1E': u'获取设备设置',
  355. '18': u'设置设备参数',
  356. '10': u'回复卡余额',
  357. }
  358. return funCodeDict.get(funCode, '')
  359. def translate_event_cmdcode(self, cmdCode):
  360. cmdDict = {
  361. '06': u'充电结束',
  362. '16': u'充电结束',
  363. '0A': u'故障',
  364. '10': u'刷卡上报',
  365. '20': u'启动设备',
  366. }
  367. return cmdDict.get(cmdCode, '')
  368. def test(self, coins):
  369. hexElec = fill_2_hexByte(hex(0), 4)
  370. hexPort = fill_2_hexByte(hex(1), 2)
  371. hexCoins = fill_2_hexByte(hex(1))
  372. hexTime = fill_2_hexByte(hex(60))
  373. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  374. {'IMEI': self._device['devNo'], "funCode": '14',
  375. 'data': hexPort + hexCoins + hexTime + hexElec})
  376. return devInfo
  377. def stop(self, port = None):
  378. if not port:
  379. raise ServiceException({"result": 0, "description": u"请指定停止端口"})
  380. self._stop(port)
  381. def start_device(self, package, openId, attachParas):
  382. """
  383. 启动函数 分不同的启动方式
  384. 目前主板暂时不支持 按电量计费 仅支持按时间计费
  385. :param package:
  386. :param openId:
  387. :param attachParas:
  388. :return:
  389. """
  390. if attachParas is None:
  391. raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路、电池类型信息'})
  392. if "chargeIndex" not in attachParas:
  393. raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路'})
  394. portStr = attachParas.get("chargeIndex")
  395. coins, _time, elec, billingType = self._suit_package_by_unit(package)
  396. portHex = "{:0>2X}".format(int(portStr))[:2]
  397. moneyHex = "{:0>4X}".format(int(coins*10))[:4]
  398. timeHex = "{:0>4X}".format(int(_time))[:4]
  399. elecHex = "{:0>4X}".format(int(elec))[:4]
  400. result = self._send_data("14", portHex + moneyHex + timeHex + elecHex, timeout=MQTT_TIMEOUT.START_DEVICE)
  401. resultCode = result.get("data", "")[8:10]
  402. if resultCode != "01":
  403. raise ServiceException({'result': 2, 'description': u'充电桩启动失败,您的金币还在,请换个充电口试试'})
  404. start_timestamp = int(time.time())
  405. otherConf = self.device.get("otherConf", dict())
  406. refundProtection = otherConf.get("refundProtection", DEFAULT_PARAM.DEFAULT_REFUND_PROTECTION)
  407. refundProtectionTime = otherConf.get("refundProtectionTime", DEFAULT_PARAM.DEFAULT_REFUND_PROTECTION_TIME)
  408. lowPowerDetectionSwitch = otherConf.get("lowPowerDetectionSwitch", DEFAULT_PARAM.DEFAULT_LOW_POWER_SWITCH)
  409. lowPowerDetectionTime = otherConf.get("lowPowerDetectionTime", DEFAULT_PARAM.DEFAULT_LOW_POWER_TIME)
  410. lowPowerDetectionPower = otherConf.get("lowPowerDetectionPower", DEFAULT_PARAM.DEFAULT_LOW_POWER)
  411. portDict = {
  412. 'startTime': timestamp_to_dt(start_timestamp).strftime('%Y-%m-%d %H:%M:%S'),
  413. 'status': Const.DEV_WORK_STATUS_WORKING,
  414. 'coins': float(coins),
  415. 'isStart': True,
  416. 'openId': openId,
  417. 'refunded': False,
  418. 'billingType': billingType,
  419. 'refundProtection': refundProtection,
  420. 'refundProtectionTime': refundProtectionTime,
  421. 'vCardId': self._vcard_id,
  422. # 用户购买的商品量 单位为 度或者分钟
  423. 'need{}'.format(billingType.capitalize()): _time if billingType == BILLING_TYPE.TIME else elec / 100.0
  424. }
  425. finishedTime = start_timestamp + min(86400, _time*60)
  426. if 'orderNo' in attachParas:
  427. portDict.update({'orderNo': attachParas['orderNo']})
  428. portDict.update({
  429. 'finishedTime': finishedTime
  430. })
  431. Device.update_dev_control_cache(self._device['devNo'], {str(portStr): portDict})
  432. result["finishedTime"] = finishedTime
  433. # begin: 低功率检测逻辑
  434. try:
  435. if not bool(int(lowPowerDetectionSwitch)) or not openId:
  436. return result
  437. user = MyUser.objects(openId = openId, groupId = self._device['groupId']).first()
  438. dealer = Dealer.get_dealer_from_groupId(self._device['groupId'])
  439. self.notify_low_power_to_user(
  440. user, dealer, portStr,
  441. int(lowPowerDetectionTime),
  442. int(lowPowerDetectionPower)
  443. )
  444. except Exception as e:
  445. logger.error(e)
  446. # end: 低功率检测逻辑
  447. return result
  448. def get_port_info(self, line):
  449. devCache = Device.get_dev_control_cache(self.device.devNo)
  450. portCache = devCache.get(str(line), dict())
  451. billingType = portCache.get("billingType", BILLING_TYPE.TIME)
  452. portHex = fill_2_hexByte(hex(int(line)), 2)
  453. result = self._send_data("15", portHex, cmd = self.make_random_cmdcode())
  454. data = result.get("data", "")
  455. if data[6:8] == "00":
  456. return {"port": line, "leftTime": 0, "power": 0, "surp": 0, "elec": 0}
  457. power = data[12:16]
  458. if power in ["0000", "FFFF"]:
  459. power = 0
  460. else:
  461. power = int(power, 16)
  462. if billingType == BILLING_TYPE.TIME:
  463. leftHex = data[8:12]
  464. ratio = 1.0
  465. else:
  466. leftHex = data[16:20]
  467. ratio = 100.0
  468. if leftHex in ["0000", "FFFF"]:
  469. left = 0
  470. else:
  471. left = int(leftHex, 16) / ratio
  472. surp = int(data[20:24], 16) / 10.0
  473. responseData = {
  474. "port": line,
  475. "power": power,
  476. "surp": surp,
  477. "left{}".format(billingType.capitalize()): left
  478. }
  479. return responseData
  480. def get_port_status(self, force = False):
  481. if force:
  482. return self.get_port_status_from_dev()
  483. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  484. statusDict = {}
  485. if not ctrInfo.has_key('allPorts'):
  486. self.get_port_status_from_dev()
  487. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  488. allPorts = ctrInfo.get('allPorts', 10)
  489. for ii in range(allPorts):
  490. tempDict = ctrInfo.get(str(ii + 1), {})
  491. if tempDict.has_key('status'):
  492. statusDict[str(ii + 1)] = {'status': tempDict.get('status')}
  493. elif tempDict.has_key('isStart'):
  494. if tempDict['isStart']:
  495. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_WORKING}
  496. else:
  497. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  498. else:
  499. statusDict[str(ii + 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 lock_unlock_port(self, port, lock = True):
  505. """
  506. 禁用/解禁端口
  507. :param port:
  508. :param lock:
  509. :return:
  510. """
  511. lockStr = '05' if lock else '06'
  512. portStr = fill_2_hexByte(hex(int(port)), 2)
  513. self._send_data("05", portStr + lockStr, cmd = DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
  514. if lock:
  515. Device.update_dev_control_cache(self._device['devNo'],
  516. {str(port): {'status': Const.DEV_WORK_STATUS_FORBIDDEN}})
  517. else:
  518. Device.update_dev_control_cache(self._device['devNo'], {str(port): {'status': Const.DEV_WORK_STATUS_IDLE}})
  519. def active_deactive_port(self, port, active):
  520. """
  521. 停止/激活充电
  522. :param port:
  523. :param active:
  524. :return:
  525. """
  526. if not active:
  527. self._stop(port)
  528. devInfo = Device.get_dev_control_cache(self._device['devNo'])
  529. portCtrInfo = devInfo.get(str(port), {})
  530. portCtrInfo.update({'isStart': False, 'status': Const.DEV_WORK_STATUS_IDLE,
  531. 'endTime': datetime.datetime.now().strftime(Const.DATETIME_FMT)})
  532. newValue = {str(port): portCtrInfo}
  533. Device.update_dev_control_cache(self._device['devNo'], newValue)
  534. else:
  535. raise ServiceException({'result': 2, 'description': u'此设备不支持直接打开端口'})
  536. def get_dev_setting(self):
  537. """
  538. 获取设备参数 有些参数从主板上获取 有些参数从数据库获取
  539. :return:
  540. """
  541. result = self._send_data("1E", "00")
  542. data = result.get("data", "")[6:]
  543. coinMin = int(data[0:4], 16) # 投币充电时间
  544. cardMin = int(data[4:8], 16) # 刷卡充电时间
  545. coinElec = int(data[8:10], 16) # 投币充电电量
  546. cardElec = int(data[10:12], 16) # 刷卡充电电量
  547. cst = int(data[12:16], 16) # 刷卡扣费金额
  548. powerMax1 = int(data[16:20], 16) # 一档功率
  549. powerMax2 = int(data[20:24], 16) # 二档功率
  550. powerMax3 = int(data[24:28], 16) # 三档功率
  551. powerMax4 = int(data[28:32], 16) # 四档功率
  552. power2Ti = int(data[32:34], 16) # 二档百分比
  553. power3Ti = int(data[34:36], 16) # 三档百分比
  554. power4Ti = int(data[36:38], 16) # 四档百分比
  555. spRecMon = int(data[38:40], 16) # 余额回收开关
  556. spFullEmpty = int(data[40:42], 16) # 断电自停
  557. fullPowerMin = int(data[42:44], 16) # 浮充功率(主板判断充满电功率)
  558. fullChargeTime = int(data[44:46], 16) # 浮充时间(充满后继续充电时间)
  559. elecTimeFirst = int(data[46:48], 16) # 是否显示剩余电量(0显示时间/1显示电量)
  560. # 下面的6个参数都是配置在服务器侧,不通过主板,直接服务器保存
  561. otherConf = self.device.get("otherConf", dict())
  562. # 计费方式: 目前主板没有计费方式的开关 先保存到服务器一侧
  563. billingType = otherConf.get("billingType", DEFAULT_PARAM.DEFAULT_BILLING_TYPE)
  564. elecFee = otherConf.get("elecFee", DEFAULT_PARAM.DEFAULT_ELEC_FEE)
  565. # 退费保护: 此开关打开之后,用户充电时间少于一定时间(分钟)全额退费
  566. refundProtection = otherConf.get("refundProtection", DEFAULT_PARAM.DEFAULT_REFUND_PROTECTION)
  567. refundProtectionTime = otherConf.get("refundProtectionTime", DEFAULT_PARAM.DEFAULT_REFUND_PROTECTION_TIME)
  568. # 低功率检测: 用户启动充电后,过一段时间进行检测,检测主板当前充电功率,小于一定功率之后发送微信通知告警用户
  569. lowPowerDetectionSwitch = otherConf.get("lowPowerDetectionSwitch", DEFAULT_PARAM.DEFAULT_LOW_POWER_SWITCH)
  570. lowPowerDetectionTime = otherConf.get("lowPowerDetectionTime", DEFAULT_PARAM.DEFAULT_LOW_POWER_TIME)
  571. lowPowerDetectionPower = otherConf.get("lowPowerDetectionPower", DEFAULT_PARAM.DEFAULT_LOW_POWER)
  572. resultDict = {
  573. 'coinMin': coinMin,
  574. 'cardMin': cardMin,
  575. 'coinElec': coinElec,
  576. 'cardElec': cardElec,
  577. 'cst': cst,
  578. 'powerMax1': powerMax1,
  579. 'powerMax2': powerMax2,
  580. 'powerMax3': powerMax3,
  581. 'powerMax4': powerMax4,
  582. 'power2Ti': power2Ti,
  583. 'power3Ti': power3Ti,
  584. 'power4Ti': power4Ti,
  585. 'spRecMon': spRecMon,
  586. 'spFullEmpty': spFullEmpty,
  587. 'fullPowerMin': fullPowerMin,
  588. 'fullChargeTime': fullChargeTime,
  589. 'elecTimeFirst': elecTimeFirst,
  590. 'billingType': billingType,
  591. 'elecFee': elecFee,
  592. 'refundProtection': int(refundProtection),
  593. "refundProtectionTime": refundProtectionTime,
  594. 'lowPowerDetectionTime': lowPowerDetectionTime,
  595. 'lowPowerDetectionSwitch': int(lowPowerDetectionSwitch),
  596. 'lowPowerDetectionPower': lowPowerDetectionPower
  597. }
  598. # 获取刷卡投币的金额统计信息
  599. consumeInfo = self._get_device_consume_count()
  600. resultDict.update(consumeInfo)
  601. return resultDict
  602. def set_dev_setting(self, setConf):
  603. data = ''
  604. data += fill_2_hexByte(hex(int(setConf['coinMin'])), 4)
  605. data += fill_2_hexByte(hex(int(setConf['cardMin'])), 4)
  606. data += fill_2_hexByte(hex(int(setConf['coinElec'])), 2)
  607. data += fill_2_hexByte(hex(int(setConf['cardElec'])), 2)
  608. data += fill_2_hexByte(hex(int(setConf['cst'])), 4)
  609. data += fill_2_hexByte(hex(int(setConf['powerMax1'])), 4)
  610. data += fill_2_hexByte(hex(int(setConf['powerMax2'])), 4)
  611. data += fill_2_hexByte(hex(int(setConf['powerMax3'])), 4)
  612. data += fill_2_hexByte(hex(int(setConf['powerMax4'])), 4)
  613. data += fill_2_hexByte(hex(int(setConf['power2Ti'])), 2)
  614. data += fill_2_hexByte(hex(int(setConf['power3Ti'])), 2)
  615. data += fill_2_hexByte(hex(int(setConf['power4Ti'])), 2)
  616. data += fill_2_hexByte(hex(int(setConf['spRecMon'])), 2)
  617. data += fill_2_hexByte(hex(int(setConf['spFullEmpty'])), 2)
  618. data += fill_2_hexByte(hex(int(setConf['fullPowerMin'])), 2)
  619. data += fill_2_hexByte(hex(int(setConf['fullChargeTime'])), 2)
  620. data += fill_2_hexByte(hex(int(setConf['elecTimeFirst'])), 2)
  621. self._send_data("18", data)
  622. def set_device_function_param(self, request, lastSetConf):
  623. """
  624. 设置 设备参数
  625. :param request: requestBody
  626. :param lastSetConf: 缓存数据
  627. :return:
  628. """
  629. coinMin = request.POST.get('coinMin', None)
  630. cardMin = request.POST.get('cardMin', None)
  631. coinElec = request.POST.get('coinElec', None)
  632. cardElec = request.POST.get('cardElec', None)
  633. cst = request.POST.get('cst', None)
  634. powerMax1 = request.POST.get('powerMax1', None)
  635. powerMax2 = request.POST.get('powerMax2', None)
  636. powerMax3 = request.POST.get('powerMax3', None)
  637. powerMax4 = request.POST.get('powerMax4', None)
  638. power2Ti = request.POST.get('power2Ti', None)
  639. power3Ti = request.POST.get('power3Ti', None)
  640. power4Ti = request.POST.get('power4Ti', None)
  641. spRecMon = request.POST.get('spRecMon', None)
  642. spFullEmpty = request.POST.get('spFullEmpty', None)
  643. fullPowerMin = request.POST.get('fullPowerMin', None)
  644. fullChargeTime = request.POST.get('fullChargeTime', None)
  645. elecTimeFirst = request.POST.get('elecTimeFirst', None)
  646. billingType = request.POST.get('billingType')
  647. elecFee = request.POST.get('elecFee')
  648. refundProtection = request.POST.get('refundProtection')
  649. refundProtectionTime = request.POST.get("refundProtection")
  650. lowPowerDetectionTime = request.POST.get('lowPowerDetectionTime')
  651. lowPowerDetectionSwitch = request.POST.get('lowPowerDetectionSwitch')
  652. lowPowerDetectionPower = request.POST.get('lowPowerDetectionPower')
  653. if coinMin:
  654. lastSetConf.update({'coinMin': int(coinMin)})
  655. if cardMin:
  656. lastSetConf.update({'cardMin': int(cardMin)})
  657. if coinElec:
  658. lastSetConf.update({'coinElec': int(coinElec)})
  659. if cardElec:
  660. lastSetConf.update({'cardElec': int(cardElec)})
  661. if cst:
  662. lastSetConf.update({'cst': int(cst)})
  663. if powerMax1:
  664. lastSetConf.update({'powerMax1': int(powerMax1)})
  665. if powerMax2:
  666. lastSetConf.update({'powerMax2': int(powerMax2)})
  667. if powerMax3:
  668. lastSetConf.update({'powerMax3': int(powerMax3)})
  669. if powerMax4:
  670. lastSetConf.update({'powerMax4': int(powerMax4)})
  671. if power2Ti:
  672. lastSetConf.update({'power2Ti': int(power2Ti)})
  673. if power3Ti:
  674. lastSetConf.update({'power3Ti': int(power3Ti)})
  675. if power4Ti:
  676. lastSetConf.update({'power4Ti': int(power4Ti)})
  677. if spRecMon:
  678. lastSetConf.update({'spRecMon': int(spRecMon)})
  679. if spFullEmpty:
  680. lastSetConf.update({'spFullEmpty': int(spFullEmpty)})
  681. if fullPowerMin:
  682. lastSetConf.update({'fullPowerMin': int(fullPowerMin)})
  683. if fullChargeTime:
  684. lastSetConf.update({'fullChargeTime': int(fullChargeTime)})
  685. if elecTimeFirst:
  686. lastSetConf.update({'elecTimeFirst': int(elecTimeFirst)})
  687. # 设备侧的参数同步到设备
  688. self.set_dev_setting(lastSetConf)
  689. otherConf = self.device.get("otherConf", dict())
  690. if billingType is not None:
  691. otherConf.update({"billingType": billingType})
  692. if elecFee is not None:
  693. otherConf.update({"elecFee": elecFee})
  694. if refundProtection is not None:
  695. otherConf.update({"refundProtection": refundProtection})
  696. if refundProtectionTime is not None:
  697. otherConf.update({"refundProtectionTim": refundProtectionTime})
  698. if lowPowerDetectionTime is not None:
  699. otherConf.update({"lowPowerDetectionTime": lowPowerDetectionTime})
  700. if lowPowerDetectionSwitch is not None:
  701. otherConf.update({"lowPowerDetectionSwitch": lowPowerDetectionSwitch})
  702. if lowPowerDetectionPower is not None:
  703. otherConf.update({"lowPowerDetectionPower": lowPowerDetectionPower})
  704. Device.objects.filter(devNo = self.device.devNo).update(otherConf = otherConf)
  705. Device.invalid_device_cache(self.device.devNo)
  706. def set_device_function(self, request, lastSetConf):
  707. if request.POST.get("reboot", False):
  708. self._restart()