ywt_chongdiangui.py 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import binascii
  4. import datetime
  5. import logging
  6. import os
  7. import random
  8. import time
  9. import simplejson as json
  10. from apilib.monetary import VirtualCoin, RMB
  11. from apps.web.common.proxy import ClientConsumeModelProxy
  12. from apps.web.constant import DeviceCmdCode, Const, MQTT_TIMEOUT
  13. from apps.web.core.adapter.base import SmartBox, reverse_hex, fill_2_hexByte
  14. from apps.web.core.device_define.ywt_chongdiangui import Calculater
  15. from apps.web.core.exceptions import ServiceException
  16. from apps.web.core.networking import MessageSender
  17. from apps.web.device.models import DevicePortReport, Group, Device, DeviceType
  18. from apps.web.user.models import ConsumeRecord, ServiceProgress, MyUser
  19. from apps.web.core.device_define.ywt_chongdiangui import DefaultParams
  20. from apps.web.utils import concat_user_login_entry_url
  21. from taskmanager.mediator import task_caller
  22. logger = logging.getLogger(__name__)
  23. class ChargeCabinet(SmartBox):
  24. def __init__(self, *args, **kwargs):
  25. super(ChargeCabinet, self).__init__(*args, **kwargs)
  26. # 主要用于计算费用
  27. self.devNo = self.device.devNo
  28. @property
  29. def password(self):
  30. return random.randint(0x2710, 0xFFFF)
  31. def _send_data(self, funCode, sendData, cmd=None, timeout=MQTT_TIMEOUT.NORMAL, orderNo=None):
  32. """
  33. 发送报文封装
  34. :param funCode:
  35. :param sendData:
  36. :param cmd:
  37. :param timeout:
  38. :param orderNo:
  39. :return:
  40. """
  41. if cmd is None:
  42. cmd = DeviceCmdCode.OPERATE_DEV_SYNC
  43. result = MessageSender.send(device = self.device, cmd = cmd, payload = {
  44. "IMEI": self._device["devNo"],
  45. "funCode": funCode,
  46. "data": sendData
  47. }, timeout = timeout)
  48. if result.has_key("rst"):
  49. if result["rst"] == -1:
  50. raise ServiceException({'result': 2, 'description': u'充电桩正在玩命找网络,请稍候再试'})
  51. elif result["rst"] == 1:
  52. raise ServiceException({'result': 2, 'description': u'充电桩主板连接故障'})
  53. elif result["rst"] == 0:
  54. return result
  55. else:
  56. raise ServiceException({'result': 2, 'description': u'系统错误'})
  57. else:
  58. raise ServiceException({'result': 2, 'description': u'系统错误'})
  59. @staticmethod
  60. def _to_str(data):
  61. return binascii.unhexlify(data)
  62. @staticmethod
  63. def _to_ascii(data):
  64. return binascii.hexlify(data)
  65. def _notify_user_service_over(self, managerialOpenId, port, chargeTime, stayTime, money, isPaid=True):
  66. group = Group.get_group(self.device.get("groupId"))
  67. notifyData = {
  68. "title": u"\\n\\n设备编号:\\t\\t{logicalCode}-{port}\\n\\n服务地址:\\t\\t{group}\\n\\n使用时长:\\t\\t{chargeTime}分钟\\n\\n占位时长:\\t\\t{stayTime}分钟\\n\\n本单消费:\\t\\t{money}".format(
  69. logicalCode=self._device["logicalCode"],
  70. port=port,
  71. group=group["address"],
  72. chargeTime=chargeTime,
  73. stayTime=stayTime,
  74. money=str(money),
  75. ),
  76. "service": u"已使用账户余额自动结算此次消费" if isPaid else u"您的账户余额已不足以抵扣此次消费,请前往账单中心进行支付",
  77. "finishTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  78. "remark": u'谢谢您的支持'
  79. }
  80. task_caller(
  81. func_name='report_to_user_via_wechat',
  82. openId=managerialOpenId,
  83. dealerId=self.device.get("ownerId"),
  84. templateName="service_complete",
  85. **notifyData
  86. )
  87. @staticmethod
  88. def _parse_event_A1(data):
  89. return dict()
  90. @staticmethod
  91. def _parse_event_A2(data):
  92. return dict()
  93. @staticmethod
  94. def _parse_event_B0(data):
  95. cardPre = ChargeCabinet._to_str(data[8: 16])
  96. cardNo = ChargeCabinet._to_str(data[16: 32])
  97. portStr = str(int(data[32: 34], 16))
  98. return {
  99. "cardPre": cardPre,
  100. "cardNo": cardNo,
  101. "cardNoHex": data[8: 32], # zjl 16->8
  102. "portStr": portStr,
  103. "portHex": data[32: 34] # zjl new
  104. }
  105. @staticmethod
  106. def _parse_event_B1(data):
  107. cardPre = ChargeCabinet._to_str(data[8: 16])
  108. cardNo = ChargeCabinet._to_str(data[16: 32])
  109. portStr = str(int(data[32: 34], 16))
  110. orderNoHex = data[34: 48]
  111. orderNo = str(int(reverse_hex(orderNoHex), 16))
  112. return {
  113. "cardPre": cardPre,
  114. "cardNo": cardNo,
  115. "cardNoHex": data[8: 32], # zjl 16->8
  116. "portStr": portStr,
  117. "portHex": data[32:34], # zjl
  118. "orderNoHex": orderNoHex,
  119. "orderNo": orderNo
  120. }
  121. @staticmethod
  122. def _parse_event_C0(data):
  123. orderNoHex = data[8: 22]
  124. orderNo = str(int(reverse_hex(orderNoHex), 16))
  125. portStr = str(int(data[22: 24], 16))
  126. voltage = int(reverse_hex(data[24: 28]), 16)
  127. power = int(reverse_hex(data[32: 36]), 16)
  128. elec = int(reverse_hex(data[36: 44]), 16) / 1000.0
  129. chargeTime = int(reverse_hex(data[44: 48]), 16)
  130. stayTime = int(reverse_hex(data[48:52]), 16)
  131. temperature = int(reverse_hex(data[52: 56]), 16)
  132. status = DefaultParams.STATUS_MAP.get(data[56: 58], Const.DEV_WORK_STATUS_IDLE)
  133. return {
  134. "orderNo": orderNo,
  135. "orderNoHex": orderNoHex,
  136. "portStr": portStr,
  137. "voltage": voltage,
  138. "power": power,
  139. "elec": elec,
  140. "chargeTime": chargeTime,
  141. "temperature": temperature,
  142. "status": status,
  143. "stayTime": stayTime
  144. }
  145. @staticmethod
  146. def _parse_event_E0(data):
  147. portStr = str(int(data[8: 10], 16))
  148. faultHex = data[10: 14]
  149. fault = str(int(faultHex[2: 4] + faultHex[:2], 16))
  150. faultReason = DefaultParams.FAULT_MAP.get(fault)
  151. return {
  152. "portHex": data[8: 10],
  153. "portStr": portStr,
  154. "faultCode": fault,
  155. "statusInfo": faultReason
  156. }
  157. @staticmethod
  158. def _parse_event_C1(data):
  159. orderNoHex = data[8: 22]
  160. orderNo = str(int(reverse_hex(orderNoHex), 16))
  161. portHex = data[22: 24]
  162. portStr = str(int(data[22: 24], 16))
  163. voltage = int(reverse_hex(data[24: 28]), 16)
  164. power = int(reverse_hex(data[32: 36]), 16)
  165. elec = int(reverse_hex(data[36: 44]), 16) / 1000.0
  166. chargeTime = int(reverse_hex(data[44: 48]), 16)
  167. stayTime = int(reverse_hex(data[48:52]), 16)
  168. temperature = int(reverse_hex(data[52: 56]), 16)
  169. reasonCode = data[56: 58]
  170. reason = DefaultParams.STOP_REASON_MAP.get(reasonCode)
  171. return {
  172. "orderNoHex": orderNoHex,
  173. "orderNo": orderNo,
  174. "portHex": portHex,
  175. "portStr": portStr,
  176. "voltage": voltage,
  177. "power": power,
  178. "elec": elec,
  179. "chargeTime": chargeTime,
  180. "temperature": temperature,
  181. "reasonCode": reasonCode,
  182. "reason": reason,
  183. "stayTime": stayTime
  184. }
  185. @staticmethod
  186. def _parse_result_B2(data):
  187. orderNoHex = data[8: 22]
  188. orderNo = str(int(reverse_hex(orderNoHex), 16))
  189. portHex = data[22: 24]
  190. portStr = str(int(data[22: 24], 16))
  191. voltage = int(reverse_hex(data[24: 28]), 16)
  192. power = int(reverse_hex(data[32: 36]), 16)
  193. elec = int(reverse_hex(data[36: 44]), 16) / 1000.0
  194. chargeTime = int(reverse_hex(data[44: 48]), 16)
  195. stayTime = int(reverse_hex(data[48:52]), 16)
  196. return {
  197. "orderNoHex": orderNoHex,
  198. "orderNo": orderNo,
  199. "portHex": portHex,
  200. "portStr": portStr,
  201. "voltage": voltage,
  202. "power": power,
  203. "elec": elec,
  204. "chargeTime": chargeTime,
  205. "stayTime": stayTime
  206. }
  207. @staticmethod
  208. def _parse_event_CA(data):
  209. data = data[8: -4]
  210. result = list()
  211. while data:
  212. orderNoHex = data[: 14]
  213. orderNo = str(int(reverse_hex(data[:14]), 16))
  214. portStr = str(int(data[14: 16], 16))
  215. voltage = int(reverse_hex(data[16: 20]), 16)
  216. power = int(reverse_hex(data[24: 28]), 16)
  217. elec = int(reverse_hex(data[28: 36]), 16) / 1000.0
  218. chargeTime = int(reverse_hex(data[36: 40]), 16)
  219. stayTime = int(reverse_hex(data[40: 44]), 16)
  220. temperature = int(reverse_hex(data[44: 48]), 16)
  221. status = DefaultParams.STATUS_MAP.get(data[48: 50], Const.DEV_WORK_STATUS_IDLE)
  222. data = data[50: ]
  223. result.append({
  224. "orderNo": orderNo,
  225. "orderNoHex": orderNoHex,
  226. "portStr": portStr,
  227. "voltage": voltage,
  228. "power": power,
  229. "elec": elec,
  230. "chargeTime": chargeTime,
  231. "temperature": temperature,
  232. "status": status,
  233. "stayTime": stayTime
  234. })
  235. return {"subChargeList": result}
  236. def _start(self, port, orderNo, pw, operate, chargeTime=0xFFFF):
  237. """
  238. :param port:
  239. :param orderNo:
  240. :param pw:
  241. :param chargeTime:
  242. :return:
  243. """
  244. orderNoHex = fill_2_hexByte(hex(int(orderNo)), 14, reverse=True)
  245. portHex = fill_2_hexByte(hex(int(port)), 2, reverse=True)
  246. chargeTimeHex = fill_2_hexByte(hex(int(chargeTime)), 4, reverse=True)
  247. operateHex = operate
  248. pwHex = fill_2_hexByte(hex(int(pw)), 4, reverse=True)
  249. data = orderNoHex+portHex+chargeTimeHex+operateHex+pwHex
  250. result = self._send_data("B2", data, timeout=120)
  251. result.update({"pw": pw})
  252. return result
  253. def _open(self, port, orderNo, pw, operate):
  254. """
  255. :param port:
  256. :param orderNo:
  257. :param pw:
  258. :return:
  259. """
  260. orderNoHex = fill_2_hexByte(hex(int(orderNo)), 14, reverse=True)
  261. portHex = fill_2_hexByte(hex(int(port)), 2, reverse=True)
  262. chargeTimeHex = '0000'
  263. operateHex = operate
  264. pwHex = fill_2_hexByte(hex(int(pw)), 4, reverse=True)
  265. data = orderNoHex+portHex+chargeTimeHex+operateHex+pwHex
  266. result = self._send_data("B2", data, timeout=120)
  267. result.update({"pw": pw})
  268. return result
  269. def _stop(self, port, orderNo, door=True):
  270. orderNoHex = fill_2_hexByte(hex(int(orderNo)), 14, reverse=True)
  271. portHex = fill_2_hexByte(hex(int(port)), 2, reverse=True)
  272. operateHex = "12" if door else "11"
  273. data = "{}{}0000{}0000".format(orderNoHex, portHex, operateHex)
  274. result = self._send_data("B2", data)
  275. return result
  276. def _get_port_status(self):
  277. result = self._send_data("B3", "00")
  278. data = result.get("data", "")[8: -4]
  279. portInfo = dict()
  280. while data:
  281. tempPort = str(int(data[0: 2], 16))
  282. tempStatus = DefaultParams.STATUS_MAP.get(data[2:4])
  283. portInfo.update({tempPort: tempStatus})
  284. data = data[4: ]
  285. return portInfo
  286. def _get_port_status_detail(self):
  287. result = self._send_data("B4", "00")
  288. data = result.get("data", "")[8: -4]
  289. portInfo = dict()
  290. while data:
  291. tempPort = str(int(data[0: 2], 16))
  292. tempVoltage = int(reverse_hex(data[2: 6]), 16)
  293. tempAmpere = int(reverse_hex(data[6:10]), 16) / 1000.0
  294. tempPower = int(reverse_hex(data[10: 14]), 16)
  295. tempElec = int(reverse_hex(data[14: 22]), 16) / 1000.0
  296. tempTime = int(reverse_hex(data[22: 26]), 16)
  297. tempOccTime = int(reverse_hex(data[26:30]), 16)
  298. tempTemper = int(reverse_hex(data[30: 34]), 16)
  299. tempStatus = DefaultParams.STATUS_MAP.get(data[34:36])
  300. portInfo.update({
  301. tempPort: {
  302. "voltage": tempVoltage,
  303. "elec": tempElec,
  304. "ampere": tempAmpere,
  305. "power": tempPower,
  306. "chargeTime": tempTime,
  307. "temperature": tempTemper,
  308. "status": tempStatus,
  309. "occTime": tempOccTime
  310. }
  311. })
  312. data = data[36: ]
  313. return portInfo
  314. def _reboot_device(self):
  315. return self._send_data("B5", "00")
  316. def _lock_device(self):
  317. operateHex = "01"
  318. return self._send_data("B6", operateHex)
  319. def _unlock_device(self):
  320. operateHex = "00"
  321. return self._send_data("B6", operateHex)
  322. def _sync_device_time(self):
  323. nowTime = datetime.datetime.now()
  324. yearHex = fill_2_hexByte(hex(nowTime.year), 4, reverse=True)
  325. monthHex = fill_2_hexByte(hex(nowTime.month), 2)
  326. dayHex = fill_2_hexByte(hex(nowTime.day), 2)
  327. hourHex = fill_2_hexByte(hex(nowTime.hour), 2)
  328. minuteHex = fill_2_hexByte(hex(nowTime.minute), 2)
  329. secondHex = fill_2_hexByte(hex(nowTime.second), 2)
  330. sendData = yearHex + monthHex + dayHex + hourHex + minuteHex + secondHex + "00"
  331. self._send_data("A1", sendData=sendData, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
  332. def _sync_device_settings(self):
  333. domain = os.environ.get("MY_DOMAIN")
  334. otherConf = self._device.get("otherConf", dict())
  335. qr_code_url = concat_user_login_entry_url(l = self._device["logicalCode"])
  336. snCode = otherConf.get("snCode", self._device["logicalCode"].replace("G", ""))
  337. minPower = otherConf.get("minPower", DefaultParams.DEFAULT_MIN_POWER)
  338. maxPower = otherConf.get("maxPower", DefaultParams.DEFAULT_MAX_POWER)
  339. floatTime = otherConf.get("floatTime", DefaultParams.DEFAULT_FLOAT_TIME)
  340. ICSetupCode = otherConf.get("ICSetupCode", DefaultParams.DEFAULT_IC_CODE)
  341. qrCodeLenHex = fill_2_hexByte(hex(len(qr_code_url)), 2)
  342. minPowerHex = fill_2_hexByte(hex(int(minPower)), 4, reverse=True)
  343. maxPowerHex = fill_2_hexByte(hex(int(maxPower)), 4, reverse=True)
  344. floatTimeHex = fill_2_hexByte(hex(int(floatTime)), 4, reverse=True)
  345. qrCodeHex = self._to_ascii(qr_code_url)
  346. snCodeHex = self._to_ascii(snCode)
  347. ICSetupCodeHex = self._to_ascii(ICSetupCode)
  348. sendData = snCodeHex + minPowerHex + maxPowerHex + floatTimeHex + ICSetupCodeHex + qrCodeLenHex + qrCodeHex
  349. self._send_data("A2", sendData=sendData, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
  350. # 添加特性 去掉前台的金额显示按钮
  351. otherConf.update({"payAfterUse": True})
  352. Device.objects.filter(devNo=self.device.devNo).update(otherConf=otherConf)
  353. Device.invalid_device_cache(self.device.devNo)
  354. def _update_device_conf(self, data):
  355. minAfterStartCoins = data.get("minAfterStartCoins", DefaultParams.DEFAULT_MIN_START_COINS)
  356. minPower = data.get("minPower", DefaultParams.DEFAULT_MIN_POWER)
  357. maxPower = data.get("maxPower", DefaultParams.DEFAULT_MAX_POWER)
  358. floatTime = data.get("floatTime", DefaultParams.DEFAULT_FLOAT_TIME)
  359. ICSetupCode = data.get("ICSetupCode", DefaultParams.DEFAULT_IC_CODE)
  360. actualPortNum = data.get("actualPortNum", DefaultParams.DEFAULT_PORT_NUM)
  361. chargeType = data.get("chargeType", DefaultParams.DEFAULT_CHARGE_TYPE)
  362. timeUnitPrice = data.get("timeUnitPrice", DefaultParams.DEFAULT_TIME_UNIT_PRICE)
  363. stayTimeUnitPrice = data.get("stayTimeUnitPrice", DefaultParams.DEFAULT_STAY_TIME_UNIT_PRICE)
  364. freeStayTime = data.get("freeStayTime", DefaultParams.DEFAULT_FREE_STAY_TIME)
  365. minConsume = data.get("minConsume", DefaultParams.DEFAULT_MIN_CONSUME)
  366. maxConsume = data.get("maxConsume", DefaultParams.DEFAULT_MAX_CONSUME)
  367. powerPackage = data.get("powerPackage", DefaultParams.DEFAULT_POWER_PACKAGE)
  368. otherConf = self._device.get("otherConf", {})
  369. otherConf.update({
  370. "minAfterStartCoins": int(minAfterStartCoins),
  371. "minPower": int(minPower),
  372. "maxPower": int(maxPower),
  373. "floatTime": int(floatTime),
  374. "ICSetupCode": ICSetupCode,
  375. "actualPortNum": int(actualPortNum),
  376. "chargeType": chargeType,
  377. "timeUnitPrice": float(timeUnitPrice),
  378. "stayTimeUnitPrice": float(stayTimeUnitPrice),
  379. "freeStayTime": float(freeStayTime),
  380. "minConsume": float(minConsume),
  381. "maxConsume": float(maxConsume),
  382. "powerPackage": powerPackage,
  383. })
  384. devNo = self._device["devNo"]
  385. try:
  386. Device.objects.filter(devNo=devNo).update(otherConf=otherConf)
  387. except Exception as e:
  388. logger.error(e)
  389. raise ServiceException({'result': 2, 'description': u'设置错误,请重新操作试试'})
  390. Device.invalid_device_cache(devNo)
  391. def _ack(self, ack_id):
  392. self._send_data(funCode='AK', sendData=ack_id, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
  393. def check_order_state(self, openId):
  394. """
  395. 通过 openId 以及设备来鉴别 订单
  396. :param openId:
  397. :return:
  398. """
  399. dealerId = self.device.ownerId
  400. devTypeCode = self.device.devType.get("code")
  401. return ClientConsumeModelProxy.get_not_finished_record(ownerId = dealerId,
  402. openId = openId,
  403. devTypeCode = devTypeCode)
  404. def analyze_event_data(self, data):
  405. cmdCode = data[6:8]
  406. funcName = "_parse_event_{}".format(cmdCode.upper())
  407. func = getattr(ChargeCabinet, funcName, None)
  408. if func and callable(func):
  409. eventData = func(data)
  410. eventData.update({"cmdCode": cmdCode})
  411. return eventData
  412. else:
  413. logger.warning("<{}> device receive an undefined cmd <{}>, data is <{}>".format(self.devNo, cmdCode, data))
  414. def get_dev_setting(self):
  415. otherConf = self._device.get("otherConf", {})
  416. return {
  417. "minAfterStartCoins": otherConf.get("minAfterStartCoins", DefaultParams.DEFAULT_MIN_START_COINS),
  418. "minPower": otherConf.get("minPower", DefaultParams.DEFAULT_MIN_POWER),
  419. "maxPower": otherConf.get("maxPower", DefaultParams.DEFAULT_MAX_POWER),
  420. "floatTime": otherConf.get("floatTime", DefaultParams.DEFAULT_FLOAT_TIME),
  421. "ICSetupCode": otherConf.get("ICSetupCode", DefaultParams.DEFAULT_IC_CODE),
  422. "actualPortNum": otherConf.get("actualPortNum", DefaultParams.DEFAULT_PORT_NUM),
  423. "chargeType": otherConf.get("chargeType", DefaultParams.DEFAULT_CHARGE_TYPE),
  424. "timeUnitPrice": otherConf.get("timeUnitPrice", DefaultParams.DEFAULT_TIME_UNIT_PRICE),
  425. "stayTimeUnitPrice": otherConf.get("stayTimeUnitPrice", DefaultParams.DEFAULT_STAY_TIME_UNIT_PRICE),
  426. "freeStayTime": otherConf.get("freeStayTime", DefaultParams.DEFAULT_FREE_STAY_TIME),
  427. "minConsume": otherConf.get("minConsume", DefaultParams.DEFAULT_MIN_CONSUME),
  428. "maxConsume": otherConf.get("maxConsume", DefaultParams.DEFAULT_MAX_CONSUME),
  429. "powerPackage": otherConf.get("powerPackage", DefaultParams.DEFAULT_POWER_PACKAGE),
  430. }
  431. def set_device_function_param(self, request, lastSetConf):
  432. actualPortNum = request.POST.get("actualPortNum", None)
  433. if actualPortNum is not None and int(actualPortNum) > 30:
  434. raise ServiceException({"result": 2, "description": u"实际端口数量最大30"})
  435. chargeType = request.POST.get("chargeType")
  436. if chargeType not in ("time", "power"):
  437. raise ServiceException({"result": 2, "description": u"暂不支持此消费模式"})
  438. minPower = int(request.POST.get("minPower"))
  439. maxPower = int(request.POST.get("maxPower"))
  440. # 校验功率计费套餐
  441. powerPackage = request.POST.get("powerPackage", None)
  442. if powerPackage is not None:
  443. if len(powerPackage) > 3:
  444. raise ServiceException({"result": 2, "description": u"功率计费不得大于三档"})
  445. checkNum = None
  446. for index, item in enumerate(powerPackage):
  447. tempMin = int(item.get("lowLimit"))
  448. tempMax = int(item.get("upLimit"))
  449. if checkNum is not None and checkNum != tempMin:
  450. raise ServiceException({"result": 2, "description": u"功率计费区间不连续"})
  451. if tempMin >= tempMax:
  452. raise ServiceException({"result": 2, "description": u"第{}功率计费区间最小值大于最大值".format(index+1)})
  453. if tempMin < minPower:
  454. raise ServiceException({"result": 2, "description": u"第{}功率计费区间最小值小于功率最小值".format(index+1)})
  455. if tempMax > maxPower:
  456. raise ServiceException({"result": 2, "description": u"第{}功率计费区间最大值大于功率最大值".format(index+1)})
  457. # 校验计费套餐
  458. maxConsume = request.POST.get("maxConsume")
  459. minConsume = request.POST.get("minConsume")
  460. if float(minConsume) < 0 or float(maxConsume) < 0:
  461. raise ServiceException({"result": 2, "description": u"最低消费金额和最高消费金额不能小于0"})
  462. if float(minConsume) > float(maxConsume):
  463. raise ServiceException({"result": 2, "description": u"最低消费金额不得大于最高消费金额"})
  464. self._update_device_conf(request.POST)
  465. def set_device_function(self, request, lastSetConf):
  466. lockDevice = request.POST.get("lockDevice", None)
  467. rebootDevice = request.POST.get("rebootDevice", None)
  468. setToDevice = request.POST.get("setToDevice", None)
  469. if rebootDevice is not None:
  470. self._reboot_device()
  471. return
  472. if lockDevice is not None and lockDevice in ("false", "true"):
  473. lockDevice = json.loads(lockDevice)
  474. self._lock_device() if lockDevice else self._unlock_device()
  475. return
  476. # 下发所有参数到设备,这个地方做一个保护 ,设备正在工作的时候不让设置参数
  477. actualPortNum = self.device.get("otherConf", dict()).get("actualPortNum")
  478. if setToDevice is not None:
  479. devCache = Device.get_dev_control_cache(self._device["devNo"])
  480. for _key, value in devCache.items():
  481. if _key.isdigit() and int(_key) > actualPortNum:
  482. continue
  483. if isinstance(value, dict) and value.get("status", Const.DEV_WORK_STATUS_IDLE) == Const.DEV_WORK_STATUS_WORKING:
  484. break
  485. elif isinstance(value, dict) and value.get("isStart", False):
  486. break
  487. else:
  488. continue
  489. else:
  490. return self._sync_device_settings()
  491. raise ServiceException({'result': 2, 'description': u'设备正在工作中,无法下发参数,请等待设备空闲时设置!'})
  492. def get_port_status(self, force=False):
  493. if force:
  494. self.get_port_status_from_dev()
  495. devCache = Device.get_dev_control_cache(self._device["devNo"])
  496. if "allPorts" not in devCache:
  497. self.get_port_status_from_dev()
  498. devCache = Device.get_dev_control_cache(self._device["devNo"])
  499. allPorts = devCache.get("allPorts")
  500. if allPorts is None:
  501. raise ServiceException({'result': 2, 'description': u'充电端口信息获取失败'})
  502. # 获取显示端口的数量 客户要求可以设置 显示给用户多少个端口
  503. showPortNum = self._device.get("otherConf", {}).get("actualPortNum", DefaultParams.DEFAULT_PORT_NUM)
  504. statusDict = dict()
  505. showStatusDict = dict()
  506. for portNum in xrange(allPorts):
  507. portStr = str(portNum + 1)
  508. tempDict = devCache.get(portStr, {})
  509. if "status" in tempDict:
  510. statusDict[portStr] = {"status": tempDict["status"]}
  511. elif "isStart" in tempDict:
  512. if tempDict["isStart"]:
  513. statusDict[portStr] = {"status": Const.DEV_WORK_STATUS_WORKING}
  514. else:
  515. statusDict[portStr] = {"status": Const.DEV_WORK_STATUS_IDLE}
  516. else:
  517. statusDict[portStr] = {"status": Const.DEV_WORK_STATUS_IDLE}
  518. if int(portStr) <= int(showPortNum):
  519. showStatusDict[portStr] = {"status": statusDict[portStr]["status"]}
  520. allPorts, usedPorts, usePorts = self.get_port_static_info(statusDict)
  521. portsDict = {"allPorts": allPorts, "usedPorts": usedPorts, "usePorts": usePorts}
  522. Device.update_dev_control_cache(self._device["devNo"], portsDict)
  523. # 返还的是要显示的端口数量
  524. return showStatusDict
  525. def get_port_status_from_dev(self):
  526. portInfo = self._get_port_status()
  527. portDict = dict()
  528. for portStr, status in portInfo.items():
  529. portDict[portStr] = {"status": status}
  530. # 更新可用端口数量
  531. allPorts, usedPorts, usePorts = self.get_port_static_info(portDict)
  532. Device.update_dev_control_cache(
  533. self._device["devNo"],
  534. {
  535. "allPorts": allPorts,
  536. "usedPorts": usedPorts,
  537. "usePorts": usePorts
  538. }
  539. )
  540. # 更新端口状态
  541. devCache = Device.get_dev_control_cache(self._device["devNo"])
  542. for port, info in portDict.items():
  543. if port in devCache and isinstance(info, dict):
  544. devCache[port].update({"status": info["status"]})
  545. else:
  546. devCache[port] = info
  547. Device.update_dev_control_cache(self._device["devNo"], devCache)
  548. return portDict
  549. def start_device(self, package, openId, attachParas):
  550. portStr = attachParas.get("chargeIndex")
  551. orderNo = attachParas.get("orderNo")
  552. if portStr is None:
  553. raise ServiceException({'result': 2, 'description': u'未知端口'})
  554. # 发送指令
  555. pw = self.password
  556. result = self._start(orderNo=orderNo, port=portStr, pw=pw, operate="02")
  557. portDict = {
  558. "status": Const.DEV_WORK_STATUS_WORKING,
  559. "vCardId": self._vcard_id,
  560. "isStart": True,
  561. "openId": openId,
  562. "orderNo": orderNo,
  563. "startTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  564. "pw": pw
  565. }
  566. Device.update_dev_control_cache(self._device["devNo"], {str(portStr): portDict})
  567. otherConf = self.device.get("otherConf") or dict()
  568. result["consumeOrderNo"] = orderNo
  569. result["servicedInfo"] = {"pw": pw, "chargeType": otherConf.get("chargeType", DefaultParams.DEFAULT_CHARGE_TYPE)}
  570. result["finishedTime"] = int(time.time()) + 7 * 24 * 60 * 60
  571. return result
  572. def stop(self, port=None):
  573. """
  574. 端口停止功能 用户主动停止了该端口
  575. 对于充电柜的业务来说 结束充电就意味着 需要打开柜门 同时结算订单
  576. 收到停止指令之后
  577. 服务器首先下发B2-12指令
  578. 然后根据返还的信息进行扣费处理
  579. 最后结存订单
  580. :param port:
  581. :return:
  582. """
  583. portStr = str(port)
  584. devCache = Device.get_dev_control_cache(self.device.devNo)
  585. portCache = devCache.get(portStr, dict())
  586. # 校验订单状态 非正在运行的订单不能结束 考虑 是否 加锁
  587. if not portCache:
  588. return
  589. if portCache.get("cardNo"):
  590. raise ServiceException({"result": 2, "description": u"刷卡启动的设备请使用刷卡结束"})
  591. orderNo = portCache.get("orderNo", "")
  592. consumeOrder = ConsumeRecord.objects.filter(orderNo=orderNo).first()
  593. if consumeOrder is None or not consumeOrder.is_running():
  594. return
  595. pw = portCache.get("pw")
  596. openId = portCache.get("openId")
  597. # 这个时候已经告诉主板停止了
  598. result = self._start(port, orderNo, pw, "12")
  599. # 然后去读取主板的的最后一次的数据
  600. data = result.get("data")
  601. curInfo = ChargeCabinet._parse_result_B2(data)
  602. DevicePortReport.create(
  603. devNo=self.device.devNo,
  604. port=curInfo.get("portStr"),
  605. orderNo=curInfo.get("orderNo"),
  606. openId=openId,
  607. voltage=curInfo.get("voltage"),
  608. power=curInfo.get("power"),
  609. elec=curInfo.get("elec"),
  610. chargeTime=curInfo.get("chargeTime"),
  611. stayTime=curInfo.get("stayTime"),
  612. isBilling=portCache.get("status", Const.DEV_WORK_STATUS_IDLE) == Const.DEV_WORK_STATUS_WORKING
  613. )
  614. consumeMoney = Calculater(self.device, consumeOrder).result
  615. consumeOrder.update(money=RMB(consumeMoney).mongo_amount, coin=VirtualCoin(consumeMoney).mongo_amount)
  616. consumeOrder.reload()
  617. consumeOrder.s_to_e()
  618. consumeDict = {
  619. "chargeIndex": portStr,
  620. 'actualNeedTime':curInfo.get("chargeTime"),
  621. 'elec': curInfo.get("elec"),
  622. 'stayTime': curInfo.get("stayTime"),
  623. 'chargeTime': curInfo.get("chargeTime")
  624. }
  625. if consumeOrder.servicedInfo and isinstance(consumeOrder.servicedInfo, dict):
  626. consumeDict.update(consumeOrder.servicedInfo)
  627. ServiceProgress.update_progress_and_consume_rcd(
  628. self._device["ownerId"],
  629. {
  630. "open_id": consumeOrder.openId,
  631. "device_imei": self.device["devNo"],
  632. "port": int(portStr),
  633. "isFinished": False
  634. },
  635. consumeDict
  636. )
  637. # 通知用户充电柜业务结束 如果订单并没有使用余额结算 还需要通知用户及时去付款
  638. try:
  639. openId = portCache.get("openId")
  640. user = MyUser.objects.filter(openId=openId, groupId=self.device.get("groupId")).first()
  641. self._notify_user_service_over(user.managerialOpenId, portStr, curInfo.get("chargeTime"), curInfo.get("stayTime"), consumeMoney, consumeOrder.is_finished())
  642. except Exception as e:
  643. logger.exception(e)
  644. Device.clear_port_control_cache(self.device.devNo, portStr)
  645. return
  646. @property
  647. def isHaveStopEvent(self):
  648. return True
  649. def dealer_get_port_status(self):
  650. showPortNum = self._device.get("otherConf", {}).get("actualPortNum", DefaultParams.DEFAULT_PORT_NUM)
  651. showStatusDict = dict()
  652. portInfo = self._get_port_status_detail()
  653. devCache = Device.get_dev_control_cache(self.device.devNo)
  654. for port, item in portInfo.items():
  655. if int(port) > showPortNum:
  656. continue
  657. portCache = devCache.get(port, dict())
  658. item.update(portCache)
  659. if item.get("status", Const.DEV_WORK_STATUS_IDLE) in (Const.DEV_WORK_STATUS_WORKING, Const.DEV_WORK_STATUS_OCCUPY):
  660. item["status"] = Const.DEV_WORK_STATUS_WORKING
  661. item["usedTime"] = item.get("chargeTime")
  662. showStatusDict[port] = item
  663. else:
  664. showStatusDict[port] = {"status": item["status"]}
  665. return showStatusDict
  666. def get_current_use(self, order): # type: (ConsumeRecord) -> dict
  667. group = Group.get_group(order.groupId)
  668. item = ServiceProgress.objects.filter(
  669. device_imei=order.devNo,
  670. open_id=order.openId,
  671. attachParas__orderNo=order.orderNo,
  672. isFinished=False
  673. ).first()
  674. if not item:
  675. return dict()
  676. data = {
  677. 'startTime': time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(item.start_time)),
  678. 'order': item.consumeOrder,
  679. 'address': group.get('address', ''),
  680. 'groupName': group.get('groupName', ''),
  681. 'devType': self.device['devType'].get('name'),
  682. 'devTypeCode': self.device['devType'].get('code'),
  683. 'logicalCode': self.device['logicalCode'],
  684. 'status': Const.DEV_WORK_STATUS_WORKING,
  685. 'devNo': self.devNo,
  686. "port": item.port
  687. }
  688. data.update(DeviceType.get_services_button(self.device['devType']['id']))
  689. devCache = Device.get_dev_control_cache(self.devNo)
  690. portCache = devCache.get(str(item.port)) or dict()
  691. data.update(portCache)
  692. return data
  693. def apiOpenCabinetDoor(self, record):
  694. portStr = record['chargeIndex']
  695. orderNo = record['orderNo']
  696. # 发送指令
  697. pw = self.password
  698. result = self._open(orderNo=orderNo, port=portStr, pw=pw, operate="03")
  699. result["consumeOrderNo"] = orderNo
  700. result["servicedInfo"] = {"pw": pw}
  701. if result['rst'] == 0:
  702. result["description"] = u'开门成功'
  703. else:
  704. result["description"] = u'开门失败'
  705. result.pop('cmd')
  706. result.pop('IMEI')
  707. result.pop('servicedInfo')
  708. result.pop('data')
  709. return result
  710. def apiStartCharging(self, record):
  711. portStr = record['chargeIndex']
  712. orderNo = record['orderNo']
  713. chargeTime = record['chargeTime']
  714. # 发送指令
  715. pw = self.password
  716. result = self._start(orderNo=orderNo, port=portStr, pw=pw, operate="01", chargeTime=chargeTime)
  717. result["consumeOrderNo"] = orderNo
  718. result["servicedInfo"] = {"pw": pw}
  719. if result['rst'] == 0:
  720. result["description"] = u'启动成功'
  721. else:
  722. result["description"] = u'启动失败'
  723. result.pop('cmd')
  724. result.pop('IMEI')
  725. result.pop('servicedInfo')
  726. result.pop('data')
  727. return result
  728. def apiOpenAndStartCharging(self, record):
  729. portStr = record['chargeIndex']
  730. orderNo = record['orderNo']
  731. chargeTime = record['chargeTime']
  732. # 发送指令
  733. pw = self.password
  734. result = self._start(orderNo=orderNo, port=portStr, pw=pw, operate="02", chargeTime=chargeTime)
  735. result["consumeOrderNo"] = orderNo
  736. result["servicedInfo"] = {"pw": pw}
  737. if result['rst'] == 0:
  738. result["description"] = u'启动成功'
  739. else:
  740. result["description"] = u'启动失败'
  741. result.pop('cmd')
  742. result.pop('IMEI')
  743. result.pop('servicedInfo')
  744. result.pop('data')
  745. return result
  746. def apiGetDevicePortInfo(self, record):
  747. result = self.get_port_status_from_dev()
  748. # 0 代表 空闲
  749. # 1 代表 繁忙
  750. # 2 代表 故障
  751. # 10 代表 占位
  752. return result
  753. def apiStopChargingWithOpenDoor(self, record):
  754. portStr = record['chargeIndex']
  755. orderNo = record['orderNo']
  756. # 发送指令
  757. pw = self.password
  758. result = self._stop(orderNo=orderNo, port=portStr, door=True)
  759. result["consumeOrderNo"] = orderNo
  760. result["servicedInfo"] = {"pw": pw}
  761. if result['rst'] == 0:
  762. result["description"] = u'停止充电成功'
  763. else:
  764. result["description"] = u'停止充电失败'
  765. result.pop('cmd')
  766. result.pop('IMEI')
  767. result.pop('servicedInfo')
  768. result.pop('data')
  769. return result
  770. def apiStopChargingWithCloseDoor(self, record):
  771. portStr = record['chargeIndex']
  772. orderNo = record['orderNo']
  773. # 发送指令
  774. pw = self.password
  775. result = self._stop(orderNo=orderNo, port=portStr, door=False)
  776. result["consumeOrderNo"] = orderNo
  777. result["servicedInfo"] = {"pw": pw}
  778. if result['rst'] == 0:
  779. result["description"] = u'停止充电成功'
  780. else:
  781. result["description"] = u'停止充电失败'
  782. result.pop('cmd')
  783. result.pop('IMEI')
  784. result.pop('servicedInfo')
  785. result.pop('data')
  786. return result
  787. def apiGetPortInfoYwt(self, record):
  788. return self._get_port_status_detail()