dianchuan_CarCharging.py 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import binascii
  4. import datetime
  5. import logging
  6. import time
  7. from apilib.monetary import VirtualCoin
  8. from apps.web.core.adapter.base import SmartBox, fill_2_hexByte
  9. from apps.web.core.device_define.dianchuan_CarCharging import ChargeMode, StopReason
  10. from apps.web.core.networking import MessageSender
  11. from apps.web.core.exceptions import ServiceException
  12. from apps.web.constant import DeviceCmdCode, Const
  13. from apps.web.user.models import ConsumeRecord
  14. from apps.web.device.models import Device, DeviceType
  15. from apps.web.utils import concat_user_login_entry_url
  16. logger = logging.getLogger(__name__)
  17. class DianchuanCarCharging(SmartBox):
  18. def _send_data(self, funCode, data, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, timeout=30):
  19. """
  20. 调用MessageSender类中的send方法,向主板发送指令信息,并获得其响应
  21. :param funCode: 功能码,具体内容参考主板协议和驱动
  22. :param data:
  23. :param cmd:经典命令码,默认210 命令方向:服务器->驱动->主板,主板->驱动->服务器
  24. :param timeout:访问超时时间
  25. :return:主板收到命令后的响应,具体内容参考主板协议和驱动
  26. """
  27. # result 中的信息有:cmd,ts (发送响应瞬间的时间戳),IMEI,data,rst
  28. result = MessageSender.send(
  29. device=self.device,
  30. cmd=cmd,
  31. payload={"IMEI": self.device.devNo, "funCode": funCode, "data": data},
  32. timeout=timeout
  33. )
  34. # 检测rst,-1为网络不通,1为串口不同,-1会发生在任何传输过程,1指挥发生在驱动到主板端
  35. if result.get('rst') == -1:
  36. raise ServiceException({'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  37. if result.get('rst') == 1:
  38. raise ServiceException({'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  39. return result
  40. @staticmethod
  41. def _suit_package(package):
  42. '''
  43. 返回充电模式以及充电参数
  44. '''
  45. unit = package["unit"]
  46. coins = VirtualCoin(package['coins'])
  47. userAcquire = package["time"]
  48. nowTime = int(time.time())
  49. if unit == u"天":
  50. mode = ChargeMode.TIME_QUOTA
  51. chargeParam = userAcquire * 24 * 60
  52. finishedTime = nowTime + chargeParam * 60
  53. return mode, chargeParam, finishedTime
  54. elif unit == u"小时":
  55. mode = ChargeMode.TIME_QUOTA
  56. chargeParam = userAcquire * 60
  57. finishedTime = nowTime + chargeParam * 60
  58. return mode, chargeParam, finishedTime
  59. elif unit == u"分钟":
  60. mode = ChargeMode.TIME_QUOTA
  61. chargeParam = userAcquire
  62. finishedTime = nowTime + chargeParam * 60
  63. return mode, chargeParam, finishedTime
  64. elif unit == u"度":
  65. mode = ChargeMode.ELEC_QUOTA
  66. chargeParam = userAcquire * 100
  67. finishedTime = nowTime + 12 * 60 * 60
  68. return mode, chargeParam, finishedTime
  69. elif unit == u"次": # 充满自停
  70. mode = ChargeMode.COIN_QUOTA
  71. chargeParam = int((coins * 100).amount)
  72. finishedTime = nowTime + 12 * 60 * 60
  73. return mode, chargeParam, finishedTime
  74. @staticmethod
  75. def _to_ascii(s):
  76. '''
  77. 将数据转化为二进制数据的十六进制表示
  78. :param s:
  79. :return:
  80. '''
  81. return binascii.hexlify(s).upper()
  82. @staticmethod
  83. def _to_str(h):
  84. """
  85. 将 16进制ascii 转换成 字符串
  86. :param h:
  87. :return:
  88. """
  89. return binascii.unhexlify(h).lower()
  90. # 状态查询
  91. def _status_check(self):
  92. network_status = fill_2_hexByte(hex(int(1)), 2)
  93. network_quality = fill_2_hexByte(hex(int(10)), 2)
  94. data = network_status + network_quality + '{:0>12}'.format(0)
  95. result = self._send_data("41", data=data)
  96. data = result['data']
  97. port_count = data[8:10],
  98. port_status = [data[10:14][i:i + 2] for i in range(len(data[12:16])) if i % 2 == 0]
  99. pile_status = [str(binascii.hexlify(binascii.unhexlify(fill_2_hexByte(bin(int(data[14:18])), 16))[::-1]))[i] for
  100. i in range(-1, -6, -1)]
  101. statusInfo = {
  102. 'port_count': port_count,
  103. 'port_status': port_status,
  104. 'pile_status': pile_status
  105. }
  106. result['statusInfo'] = statusInfo
  107. return result #
  108. # 生成seqNo
  109. def _make_seqNo(self, port, logicNo):
  110. """
  111. 生成流水号
  112. :param port:充电端口
  113. :param logicNo:充电桩的设备码
  114. :return:特征码+桩编号后 4 位+ 月日时分秒+端口号+充电计数
  115. """
  116. characteristic_code = '55' # 本地启动特征码为 0x99,远程启动特征码为 0x55
  117. logicNo_four_digits = logicNo[-4:]
  118. date_MDHMS = time.strftime('%m%d%H%M%S')
  119. portHex = fill_2_hexByte(hex(int(port)), 2)
  120. # charge_count = fill_2_hexByte(hex(random.randint(0, 255)),2)
  121. charge_count = fill_2_hexByte(logicNo[:2], 2)
  122. seqNo = characteristic_code + logicNo_four_digits + date_MDHMS + portHex + charge_count
  123. return seqNo
  124. # 生成配置表单
  125. def _make_config_list(self, top_price_rate, peak_price_rate, normal_price_rate, valley_price_rate,
  126. half_hour_price_list):
  127. top_price_rateHex = binascii.hexlify(binascii.unhexlify(fill_2_hexByte(hex(int(top_price_rate)), 4))[::-1])
  128. peak_price_rateHex = binascii.hexlify(binascii.unhexlify(fill_2_hexByte(hex(int(peak_price_rate)), 4))[::-1])
  129. normal_price_rateHex = binascii.hexlify(
  130. binascii.unhexlify(fill_2_hexByte(hex(int(normal_price_rate)), 4))[::-1])
  131. valley_price_rateHex = binascii.hexlify(
  132. binascii.unhexlify(fill_2_hexByte(hex(int(valley_price_rate)), 4))[::-1])
  133. half_hour_priceHex = ''.join(
  134. fill_2_hexByte(hex(int(half_hour_price)), 2) for half_hour_price in half_hour_price_list)
  135. data = top_price_rateHex + peak_price_rateHex + normal_price_rateHex + valley_price_rateHex + half_hour_priceHex + '{:0>16}'.format(
  136. 0)
  137. return data
  138. # 查询当前的配置表
  139. def _config_list_check(self):
  140. result = self._send_data('48', '55')
  141. code = result['data'][8:10]
  142. if code == '55':
  143. return self._parse_config_change_upload(result['data'])
  144. raise ServiceException({'result': 2, 'description': u'信息查询错误'})
  145. # 配置表主动下发
  146. def _config_list_issue(self, top_price_rate, peak_price_rate, normal_price_rate, valley_price_rate,
  147. half_hour_price_list):
  148. data = self._make_config_list(top_price_rate, peak_price_rate, normal_price_rate, valley_price_rate,
  149. half_hour_price_list)
  150. result = self._send_data('49', data=data)
  151. code = result['data'][8:10]
  152. if code == '00':
  153. return result
  154. else:
  155. raise ServiceException({'result': 2, 'description': u'请求失败,请重试'})
  156. # 控制设备
  157. def _control_device(self, pile_instruct, port_instruct):
  158. pile_instructHex = binascii.hexlify(binascii.unhexlify(fill_2_hexByte(hex(pile_instruct), 4))[::-1])
  159. port_instructHex = fill_2_hexByte(hex(port_instruct), 4)
  160. data = pile_instructHex + port_instructHex + '{:0>16}'.format(0)
  161. result = self._send_data('47', data=data)
  162. code = result['data'][8:10]
  163. if code == "00":
  164. return result
  165. else:
  166. raise ServiceException({'result': 2, 'description': u'设备操作失败'})
  167. # 下发二维码以及模块编号
  168. def _qr_code_devNo_issue(self):
  169. deviceNo = self._to_ascii(self.device.devNo)
  170. if deviceNo[0:1] == 'G':
  171. deviceNo = self._to_ascii(deviceNo[1:])
  172. qr_code_url = self._to_ascii(concat_user_login_entry_url(l=self._device["logicalCode"])).ljust(128, "0")
  173. data = qr_code_url + deviceNo
  174. self._send_data('89', data=data)
  175. # 远程启动
  176. def _start_remote(self, port, seqNo, mode, chargeParam, balance):
  177. """
  178. 拼接远程启动指令并发送到主板,获取启动响应
  179. :param port:设备端口号
  180. :param seqNo: 设备logic码
  181. :param mode:停止充电条件
  182. :param chargeParam:充电参数:充电金额,充电时间,充电电量等
  183. :param balance:充电支付的总金额
  184. :return:主板返回的远程启动响应
  185. """
  186. seqNo = seqNo
  187. card_No = '00000000'
  188. portHex = fill_2_hexByte(hex(int(port)), 2)
  189. modeHex = fill_2_hexByte(hex(int(mode)), 2)
  190. chargeParamHex = binascii.hexlify(binascii.unhexlify(fill_2_hexByte(hex(int(chargeParam)), 4))[::-1])
  191. balanceHex = binascii.hexlify(binascii.unhexlify(fill_2_hexByte(hex(int(balance)), 8))[::-1]).upper()
  192. data = seqNo + card_No + portHex + modeHex + chargeParamHex + balanceHex + '{:0>36}'.format(0)
  193. result = self._send_data("42", data=data)
  194. code = result["data"][10:-4]
  195. if code == "00":
  196. return result
  197. if code == '11':
  198. raise ServiceException({'result': 2, 'description': u'您使用的充电枪已经在充电了,请您换其他空闲的充电枪吧!'})
  199. if code == '12':
  200. raise ServiceException({'result': 2, 'description': u'您没有插充电枪,请先插好充电枪,然后再操作哦!'})
  201. if code == '13':
  202. raise ServiceException({'result': 2, 'description': u'充电故障,暂时无法使用,您本次操作不会扣费,您可以试试其他可用的设备。'})
  203. if code == '14':
  204. raise ServiceException({'result': 2, 'description': u'待机故障,暂时无法使用,您本次操作不会扣费,您可以试试其他可用的设备。'})
  205. if code == '15':
  206. raise ServiceException({'result': 2, 'description': u'车辆未连接,请先插好充电枪,然后再操作哦!'})
  207. if code == '16':
  208. raise ServiceException({'result': 2, 'description': u'设备紧急按钮已被按下,请确认设备是否正常工作,然后再操作哦!'})
  209. if code == '17':
  210. raise ServiceException({'result': 2, 'description': u'正在等待上次充电结算,请您换其他空闲的充电枪吧!'})
  211. if code == '18':
  212. raise ServiceException({'result': 2, 'description': u'无此端口,本次操作不会扣费,请您重试看是否能够解决'})
  213. if code == '19':
  214. raise ServiceException({'result': 2, 'description': u'设备已暂停服务,请您换其他空闲的充电枪吧!'})
  215. raise ServiceException({'result': 2, 'description': u'设备返回一个未知的错误,本次操作不会扣费,请您重试看是否能够解决'})
  216. # 远程停止
  217. def _stop_remote(self, port, seqNo):
  218. """
  219. 远程停止
  220. """
  221. portHex = fill_2_hexByte(hex(int(port)), 2)
  222. seqNo = seqNo
  223. data = seqNo + portHex
  224. result = self._send_data('45', data)
  225. if result['data'][30:-4] != "00":
  226. if result['data'][30:-4] == "01":
  227. raise ServiceException({'result': 2, 'description': u'该充电枪已经空闲,没有充电'})
  228. else:
  229. raise ServiceException({'result': 2, 'description': u'断电失败'})
  230. return result
  231. # 配置表变化上传
  232. @staticmethod
  233. def _parse_config_change_upload(data):
  234. top_rate = int(binascii.hexlify(binascii.unhexlify(data[10:14])[::-1]), 16)
  235. top_price_rate = str(float(top_rate) / 100)
  236. peak_rate = int(binascii.hexlify(binascii.unhexlify(data[14:18])[::-1]), 16)
  237. peak_price_rate = str(float(peak_rate) / 100)
  238. normal_rate = int(binascii.hexlify(binascii.unhexlify(data[18:22])[::-1]), 16)
  239. normal_price_rate = str(float(normal_rate) / 100)
  240. valley_rate = int(binascii.hexlify(binascii.unhexlify(data[22:26])[::-1]), 16)
  241. valley_price_rate = str(float(valley_rate) / 100)
  242. half_hour_price_list = [str(int(i)) for i in
  243. [data[26:-20][i:i + 2] for i in range(len(data[26:-20]) - 1) if i % 2 == 0]]
  244. return {
  245. 'top_price_rate': top_price_rate,
  246. 'peak_price_rate': peak_price_rate,
  247. 'normal_price_rate': normal_price_rate,
  248. 'valley_price_rate': valley_price_rate,
  249. 'half_hour_price_list': half_hour_price_list
  250. }
  251. # 充电实时数据上报
  252. @staticmethod
  253. def _charge_data_report(data):
  254. current_rate = data[8:10]
  255. port_count = data[10:12] # 正在充电的端口数量
  256. port_data = {}
  257. tag = 12
  258. for i in range(port_count):
  259. port_data[i] = {
  260. 'chargeIndex': data[tag:tag + 2],
  261. 'output_voltage': str(int(binascii.hexlify(binascii.unhexlify(data[tag + 2:tag + 6])[::-1]), 16)),
  262. 'output_current': str(int(binascii.hexlify(binascii.unhexlify(data[tag + 6:tag + 10])[::-1]), 16)),
  263. 'output_power': str(int(binascii.hexlify(binascii.unhexlify(data[tag + 10:tag + 14])[::-1]), 16)),
  264. 'used_time': str(int(binascii.hexlify(binascii.unhexlify(data[tag + 14:tag + 18])[::-1]), 16)),
  265. 'top_elec': str(int(binascii.hexlify(binascii.unhexlify(data[tag + 18:tag + 22])[::-1]), 16)),
  266. 'peak_elec': str(int(binascii.hexlify(binascii.unhexlify(data[tag + 22:tag + 26])[::-1]), 16)),
  267. 'normal_elec': str(int(binascii.hexlify(binascii.unhexlify(data[tag + 26:tag + 30])[::-1]), 16)),
  268. 'vallery_elec': str(int(binascii.hexlify(binascii.unhexlify(data[tag + 30:tag + 34])[::-1]), 16)),
  269. 'total_elec': str(int(binascii.hexlify(binascii.unhexlify(data[tag + 34:tag + 38])[::-1]), 16)),
  270. 'top_expense': str(int(binascii.hexlify(binascii.unhexlify(data[tag + 38:tag + 42])[::-1]), 16)),
  271. 'peak_expense': str(int(binascii.hexlify(binascii.unhexlify(data[tag + 42:tag + 46])[::-1]), 16)),
  272. 'normal_expense': str(int(binascii.hexlify(binascii.unhexlify(data[tag + 46:tag + 50])[::-1]), 16)),
  273. 'vallery_expense': str(int(binascii.hexlify(binascii.unhexlify(data[tag + 50:tag + 54])[::-1]), 16)),
  274. 'total_expense': str(int(binascii.hexlify(binascii.unhexlify(data[tag + 54:tag + 58])[::-1]), 16)),
  275. }
  276. tag = tag + 58
  277. return {
  278. 'current_rate': current_rate,
  279. 'port_count': port_count,
  280. 'port_data': port_data,
  281. }
  282. def _response_finished(self, seqNo):
  283. seqNo = seqNo
  284. self._send_data("AK", seqNo, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
  285. # 获取二维码以及设备编号
  286. def _parse_get_qr_code_devNo(self, data):
  287. time = int(data[8:-4], 16)
  288. self._qr_code_devNo_issue()
  289. @property
  290. def isHaveStopEvent(self):
  291. return True
  292. # 分析上报事件数据
  293. def analyze_event_data(self, data):
  294. cmdcode = data[6:8]
  295. if cmdcode == '36':
  296. seqNo = data[8:28]
  297. port = int(data[28:30], 16)
  298. mode = int(data[30:32], 16)
  299. startTime = data[40:54]
  300. finishTime = data[54:68]
  301. eDuration = int(binascii.hexlify(binascii.unhexlify(data[68:72])[::-1]), 16)
  302. top_spend_elec = float(int(binascii.hexlify(binascii.unhexlify(data[72:76])[::-1]), 16))
  303. peak_spend_elec = float(int(binascii.hexlify(binascii.unhexlify(data[76:80])[::-1]), 16))
  304. normal_spend_elec = float(int(binascii.hexlify(binascii.unhexlify(data[80:84])[::-1]), 16))
  305. valley_spend_elec = float(int(binascii.hexlify(binascii.unhexlify(data[84:88])[::-1]), 16))
  306. top_spend_money = int(binascii.hexlify(binascii.unhexlify(data[88:92])[::-1]), 16)
  307. peak_spend_money = int(binascii.hexlify(binascii.unhexlify(data[92:96])[::-1]), 16)
  308. normal_spend_money = int(binascii.hexlify(binascii.unhexlify(data[96:100])[::-1]), 16)
  309. valley_spend_money = int(binascii.hexlify(binascii.unhexlify(data[100:104])[::-1]), 16)
  310. total_spend_elec = float(int(binascii.hexlify(binascii.unhexlify(data[104:108])[::-1]), 16))
  311. total_spend_money = float(int(binascii.hexlify(binascii.unhexlify(data[108:112])[::-1]), 16))
  312. reason = str(int(data[120:122], 16))
  313. eDuration = eDuration / 60
  314. top_spend_elec = top_spend_elec / 100
  315. peak_spend_elec = peak_spend_elec / 100
  316. normal_spend_elec = normal_spend_elec / 100
  317. valley_spend_elec = valley_spend_elec / 100
  318. top_spend_money = top_spend_money / 100
  319. peak_spend_money = peak_spend_money / 100
  320. normal_spend_money = normal_spend_money / 100
  321. valley_spend_money = valley_spend_money / 100
  322. total_spend_elec = total_spend_elec / 100
  323. total_spend_money = total_spend_money / 100
  324. if reason == StopReason.NO_SUFFICIENT_BALANCE:
  325. desc = '余额不足'
  326. elif reason == StopReason.CHARGE_TIME_DONE:
  327. desc = '已充电到指定时间'
  328. elif reason == StopReason.CHARGE_ELEC_DONE:
  329. desc = '已充电到指定电量'
  330. elif reason == StopReason.CHARGE_MONEY_DONE:
  331. desc = '已充电到指定金额'
  332. elif reason == StopReason.SCRAM_BUTTON_STOP:
  333. desc = '急停按钮被按下,请联系管理员或客服'
  334. elif reason == StopReason.CC_DISCONNECT:
  335. desc = '枪把断开连接,请稍候再试'
  336. elif reason == StopReason.CP_SIGNAL_ABNORMAL:
  337. desc = '电压信号异常,请稍候再试'
  338. elif reason == StopReason.CHARGE_DONE:
  339. desc = '充电已完成'
  340. elif reason == StopReason.CURRENT_OVERLOAD:
  341. desc = '电流超载,请稍候再试'
  342. elif reason == StopReason.ELECTRIC_METER_FAILURE:
  343. desc = '电表故障,请联系管理员或客服'
  344. elif reason == StopReason.CHARGING_PILE_DISABLE:
  345. desc = '充电桩被远程停止已停用'
  346. elif reason == StopReason.CP_DISCONNECT:
  347. desc = '充电枪断开连接'
  348. elif reason == StopReason.USER_CARD_STOP:
  349. desc = '刷卡停止'
  350. elif reason == StopReason.CHARGING_PILE_POWER_OUTAGES:
  351. desc = '充电桩断电,请联系管理员或客服'
  352. elif reason == StopReason.ELEC_PRICE_EQUAL_ZERO:
  353. desc = '电价设置异常,请联系管理员或客服'
  354. else:
  355. desc = ''
  356. return {
  357. 'cmdCode': '36',
  358. 'seqNo': seqNo,
  359. 'port': port,
  360. 'mode': mode,
  361. 'startTime': startTime,
  362. 'finishTime': finishTime,
  363. 'eDuration': eDuration,
  364. 'top_spend_elec': top_spend_elec,
  365. 'peak_spend_elec': peak_spend_elec,
  366. 'normal_spend_elec': normal_spend_elec,
  367. 'valley_spend_elec': valley_spend_elec,
  368. 'top_spend_money': top_spend_money,
  369. 'peak_spend_money': peak_spend_money,
  370. 'normal_spend_money': normal_spend_money,
  371. 'valley_spend_money': valley_spend_money,
  372. 'total_spend_elec': total_spend_elec,
  373. 'total_spend_money': total_spend_money,
  374. 'reason': desc
  375. }
  376. if cmdcode == '79':
  377. return {
  378. 'cmdCode': '79'
  379. }
  380. if cmdcode == '34':
  381. return {
  382. 'cmdCode': '34'
  383. }
  384. # 启动设备
  385. def start_device(self, package, openId, attachParas):
  386. chargeIndex = attachParas.get("chargeIndex")
  387. if not chargeIndex:
  388. raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路'})
  389. if not openId:
  390. raise ServiceException({"result": 2, "description": u"本设备暂不支持上分"})
  391. devStatus = self.get_port_status_from_dev()
  392. if devStatus[chargeIndex]['status'] != Const.DEV_WORK_STATUS_CONNECTED:
  393. raise ServiceException(
  394. {'result': 2, 'description': u'车辆未连接,请先连接车辆!'})
  395. mode, chargeParam, finishedTime = self._suit_package(package)
  396. seqNo = str(self._make_seqNo(chargeIndex, self.device.devNo))
  397. order = ConsumeRecord.objects.get(orderNo=attachParas["orderNo"]) # type: ConsumeRecord
  398. order.sequanceNo = seqNo
  399. order.save()
  400. balance = int((VirtualCoin(package['price']) * 100))
  401. result = self._start_remote(chargeIndex, seqNo, mode, chargeParam, balance)
  402. portDict = {
  403. 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  404. 'status': Const.DEV_WORK_STATUS_WORKING,
  405. 'finishedTime': finishedTime,
  406. 'coins': float(balance) / 100,
  407. 'isStart': True,
  408. 'sequanceNo': seqNo,
  409. 'orderNo': attachParas['orderNo'],
  410. 'useType': 'mobile',
  411. 'openId': openId,
  412. 'refunded': False,
  413. 'chargeMode': mode,
  414. 'need': chargeParam,
  415. }
  416. Device.update_dev_control_cache(self._device['devNo'], {str(chargeIndex): portDict})
  417. result["finishedTime"] = finishedTime
  418. return result
  419. # 停止设备
  420. def stop(self, port=None):
  421. if not port:
  422. raise ServiceException({"result": 2, "description": u"请选择需要结束的充电端口"})
  423. portCache = Device.get_port_control_cache(self.device.devNo, str(port))
  424. seqNo = portCache.get("sequanceNo")
  425. if not seqNo:
  426. raise ServiceException({"result": 2, "description": u"当前端口无工作"})
  427. return self._stop_remote(port, seqNo)
  428. # 获取端口运行信息
  429. def get_port_info(self, line):
  430. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  431. value = ctrInfo.get(line, {})
  432. data = self.check_real_elec(line)
  433. data_list = {'port': line, 'sequanceNo': value['sequanceNo'], 'usedTime': data['eduration'],
  434. 'elec': data['total_elec'], 'power': data['output_power'], 'outputVoltage': data['output_voltage'],
  435. 'elecFee':data['total_spend']}
  436. serviceChargeOn = self.device.bill_as_service_feature.on
  437. if serviceChargeOn:
  438. serviceFee = self.caculate_consume(data)
  439. data_list.update({'serviceFee':serviceFee})
  440. consumeMoney = data['total_spend'] + serviceFee
  441. data_list.update({'consumeMoney':consumeMoney})
  442. else:
  443. data_list.update({'consumeMoney': data['total_spend']})
  444. return data_list
  445. # 获取端口状态
  446. def get_port_status_from_dev(self):
  447. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  448. {"IMEI": self.device.devNo, "funCode": '41', "data": '00'})
  449. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  450. if devInfo['rst'] == -1:
  451. raise ServiceException(
  452. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  453. elif devInfo['rst'] == 1:
  454. raise ServiceException(
  455. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  456. result = {}
  457. data = devInfo['data'][8:14]
  458. portNum = int(data[0:2], 16)
  459. portData = data[2::]
  460. ii = 0
  461. portNo = 1
  462. while ii < portNum:
  463. port = portNo
  464. statusTemp = portData[ii * 2: ii * 2 + 2]
  465. if statusTemp == '00':
  466. status = {'status': Const.DEV_WORK_STATUS_FAULT}
  467. elif statusTemp == '55':
  468. status = {'status': Const.DEV_WORK_STATUS_PAUSE}
  469. elif statusTemp == '10':
  470. status = {'status': Const.DEV_WORK_STATUS_IDLE}
  471. elif statusTemp == '11':
  472. status = {'status': Const.DEV_WORK_STATUS_CONNECTED}
  473. elif statusTemp == '12':
  474. status = {'status': Const.DEV_WORK_STATUS_WORKING}
  475. elif statusTemp == '13':
  476. status = {'status': Const.DEV_WORK_STATUS_FINISHED}
  477. elif statusTemp == '14':
  478. status = {'status': Const.DEV_WORK_STATUS_FAULT}
  479. elif statusTemp == '15':
  480. status = {'status': Const.DEV_WORK_STATUS_FAULT}
  481. elif statusTemp == '16':
  482. status = {'status': Const.DEV_WORK_STATUS_WORKING}
  483. else:
  484. status = {'status': Const.DEV_WORK_STATUS_FAULT}
  485. ii += 1
  486. portNo += 1
  487. result[str(port)] = status
  488. allPorts, usedPorts, usePorts = self.get_port_static_info(result)
  489. Device.update_dev_control_cache(self._device['devNo'],
  490. {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  491. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  492. for strPort, info in result.items():
  493. if ctrInfo.has_key(strPort):
  494. ctrInfo[strPort].update({'status': info['status']})
  495. else:
  496. ctrInfo[strPort] = info
  497. Device.update_dev_control_cache(self._device['devNo'], ctrInfo)
  498. return result
  499. def set_device_function_param(self, request, lastSetConf):
  500. if request.POST.has_key('top_price_rate') and request.POST.has_key('peak_price_rate') and request.POST.has_key(
  501. 'normal_price_rate') and request.POST.has_key('valley_price_rate') and request.POST.has_key(
  502. 'half_hour_price_list'):
  503. if request.POST.has_key('pile_pause'):
  504. self._control_device(pile_instruct=1, port_instruct=1)
  505. return
  506. elif request.POST.has_key('pile_continue'):
  507. self._control_device(pile_instruct=0, port_instruct=1)
  508. return
  509. elif request.POST.has_key('pile_reboot'):
  510. self._control_device(pile_instruct=2, port_instruct=1)
  511. return
  512. lastSetConf.update({'top_price_rate': float(request.POST.get('top_price_rate', 0))})
  513. lastSetConf.update({'peak_price_rate': float(request.POST.get('peak_price_rate', 0))})
  514. lastSetConf.update({'normal_price_rate': float(request.POST.get('normal_price_rate', 0))})
  515. lastSetConf.update({'valley_price_rate': float(request.POST.get('valley_price_rate', 0))})
  516. lastSetConf.update({'half_hour_price_list': request.POST.get('half_hour_price_list', 0)})
  517. self.set_elec_conf(lastSetConf)
  518. # self.set_elec_charge_by_device(lastSetConf)
  519. def set_elec_conf(self, infoDic):
  520. top_price_rateHex = binascii.hexlify(
  521. binascii.unhexlify(fill_2_hexByte(hex(int(infoDic['top_price_rate'] * 100)), 4))[::-1])
  522. peak_price_rateHex = binascii.hexlify(
  523. binascii.unhexlify(fill_2_hexByte(hex(int(infoDic['peak_price_rate'] * 100)), 4))[::-1])
  524. normal_price_rateHex = binascii.hexlify(
  525. binascii.unhexlify(fill_2_hexByte(hex(int(infoDic['normal_price_rate'] * 100)), 4))[::-1])
  526. valley_price_rateHex = binascii.hexlify(
  527. binascii.unhexlify(fill_2_hexByte(hex(int(infoDic['valley_price_rate'] * 100)), 4))[::-1])
  528. half_hour_priceHex = ''.join(
  529. fill_2_hexByte(hex(int(half_hour_price)), 2) for half_hour_price in infoDic['half_hour_price_list'])
  530. data = top_price_rateHex + peak_price_rateHex + normal_price_rateHex + valley_price_rateHex + half_hour_priceHex + '{:0>16}'.format(
  531. 0)
  532. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  533. {'IMEI': self._device['devNo'], "funCode": '49', 'data': data})
  534. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  535. if devInfo['rst'] == -1:
  536. raise ServiceException(
  537. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  538. elif devInfo['rst'] == 1:
  539. raise ServiceException(
  540. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试'})
  541. # 获取设备配置参数
  542. def get_dev_setting(self):
  543. result = MessageSender.send(
  544. device=self.device,
  545. cmd=DeviceCmdCode.OPERATE_DEV_SYNC,
  546. payload={
  547. "IMEI": self._device["devNo"],
  548. "data": "55",
  549. "funCode": "48"
  550. }
  551. )
  552. rst = result.get("rst")
  553. if rst == -1:
  554. raise ServiceException({'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  555. elif rst == 1:
  556. raise ServiceException({'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  557. elif rst == 0:
  558. resultDict = self._parse_config_change_upload(result['data'])
  559. driverCode = self._device.get("devType", dict()).get("code", "")
  560. resultDict.update({'driverCode': driverCode})
  561. return resultDict
  562. def get_port_status(self, force=False):
  563. if force:
  564. return self.get_port_status_from_dev()
  565. statusDict = {}
  566. # self.async_update_portinfo_from_dev()
  567. self.get_port_status_from_dev()
  568. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  569. allPorts = ctrInfo.get('allPorts', 2)
  570. for ii in range(allPorts):
  571. tempDict = ctrInfo.get(str(ii + 1), {})
  572. if tempDict.has_key('status'):
  573. statusDict[str(ii + 1)] = {'status': tempDict.get('status')}
  574. elif tempDict.has_key('isStart'):
  575. if tempDict['isStart']:
  576. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_WORKING}
  577. else:
  578. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  579. else:
  580. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  581. allPorts, usedPorts, usePorts = self.get_port_static_info(statusDict)
  582. Device.update_dev_control_cache(self._device['devNo'],
  583. {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  584. return statusDict
  585. def active_deactive_port(self, port, active):
  586. if not active:
  587. self.stop(port)
  588. devInfo = Device.get_dev_control_cache(self._device['devNo'])
  589. portCtrInfo = devInfo.get(str(port), {})
  590. portCtrInfo.update({'isStart': False, 'status': Const.DEV_WORK_STATUS_IDLE,
  591. 'endTime': datetime.datetime.now().strftime(Const.DATETIME_FMT)})
  592. newValue = {str(port): portCtrInfo}
  593. Device.update_dev_control_cache(self._device['devNo'], newValue)
  594. else:
  595. raise ServiceException({'result': 2, 'description': u'此设备不支持直接打开端口'})
  596. # 查询当前电量
  597. def check_real_elec(self, port):
  598. data = "{:0>2X}".format(int(port))
  599. result = self._send_data('44', data)
  600. rst = result.get("rst")
  601. if rst != 1:
  602. if rst == -1:
  603. raise ServiceException({'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  604. elif rst == 1:
  605. raise ServiceException({'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  606. elec_info = result.get("data")[8:]
  607. ii = 0
  608. port_str = str(int(elec_info[ii:ii + 2]))
  609. output_voltage = float(int(binascii.hexlify(binascii.unhexlify(elec_info[ii + 2:ii + 6])[::-1]), 16))
  610. output_current = float(int(binascii.hexlify(binascii.unhexlify(elec_info[ii + 6:ii + 10])[::-1]), 16))
  611. output_power = float(int(binascii.hexlify(binascii.unhexlify(elec_info[ii + 10:ii + 14])[::-1]), 16))
  612. eduration = int(binascii.hexlify(binascii.unhexlify(elec_info[ii + 14:ii + 18])[::-1]), 16)
  613. top_elec = float(int(binascii.hexlify(binascii.unhexlify(elec_info[ii + 18:ii + 22])[::-1]), 16))
  614. peak_elec = float(int(binascii.hexlify(binascii.unhexlify(elec_info[ii + 22:ii + 26])[::-1]), 16))
  615. normal_elec = float(int(binascii.hexlify(binascii.unhexlify(elec_info[ii + 26:ii + 30])[::-1]), 16))
  616. valley_elec = float(int(binascii.hexlify(binascii.unhexlify(elec_info[ii + 30:ii + 34])[::-1]), 16))
  617. total_elec = float(int(binascii.hexlify(binascii.unhexlify(elec_info[ii + 34:ii + 38])[::-1]), 16))
  618. top_spend = float(int(binascii.hexlify(binascii.unhexlify(elec_info[ii + 38:ii + 42])[::-1]), 16))
  619. peak_spend = float(int(binascii.hexlify(binascii.unhexlify(elec_info[ii + 42:ii + 46])[::-1]), 16))
  620. normal_spend = float(int(binascii.hexlify(binascii.unhexlify(elec_info[ii + 46:ii + 50])[::-1]), 16))
  621. valley_spend = float(int(binascii.hexlify(binascii.unhexlify(elec_info[ii + 50:ii + 54])[::-1]), 16))
  622. total_spend = float(int(binascii.hexlify(binascii.unhexlify(elec_info[ii + 54:ii + 58])[::-1]), 16))
  623. output_voltage = int(output_voltage * 0.1)
  624. output_current = output_current / 100
  625. output_power = output_power
  626. eduration = eduration / 60
  627. top_elec = top_elec / 100
  628. peak_elec = peak_elec / 100
  629. normal_elec = normal_elec / 100
  630. valley_elec = valley_elec / 100
  631. total_elec = total_elec / 100
  632. top_spend = top_spend / 100
  633. peak_spend = peak_spend / 100
  634. normal_spend = normal_spend / 100
  635. valley_spend = valley_spend / 100
  636. total_spend = total_spend / 100
  637. new_data = {
  638. 'port': port_str,
  639. 'output_voltage': output_voltage,
  640. 'output_current': output_current,
  641. 'output_power': output_power,
  642. 'eduration': eduration,
  643. 'top_elec': top_elec,
  644. 'peak_elec': peak_elec,
  645. 'normal_elec': normal_elec,
  646. 'valley_elec': valley_elec,
  647. 'total_elec': total_elec,
  648. 'top_spend': top_spend,
  649. 'peak_spend': peak_spend,
  650. 'normal_spend': normal_spend,
  651. 'valley_spend': valley_spend,
  652. 'total_spend': total_spend,
  653. }
  654. return new_data
  655. def get_elec_charge(self):
  656. # 从主板获取到各时段费费率
  657. resultDict = self.get_dev_setting()
  658. # 查找当前时段的费率
  659. nowTime = datetime.datetime.now().strftime('%H:%M')
  660. timeNowList = nowTime.split(':')
  661. index = int(timeNowList[0])*2 + 2 if int(timeNowList[1])>30 else 1 - 1
  662. nowElecChargeRate = resultDict['half_hour_price_list'][index]
  663. if nowElecChargeRate == '1':
  664. nowElecCharge = resultDict['valley_price_rate']
  665. elif nowElecChargeRate == '2':
  666. nowElecCharge = resultDict['normal_price_rate']
  667. elif nowElecChargeRate == '3':
  668. nowElecCharge = resultDict['peak_price_rate']
  669. elif nowElecChargeRate == '4':
  670. nowElecCharge = resultDict['top_price_rate']
  671. return nowElecCharge
  672. def set_elec_charge_by_dealer(self,elecCharge):
  673. IntElecCharge = float(elecCharge)
  674. infoDic = {
  675. 'top_price_rate': IntElecCharge,
  676. 'peak_price_rate': IntElecCharge,
  677. 'normal_price_rate': IntElecCharge,
  678. 'valley_price_rate': IntElecCharge,
  679. 'half_hour_price_list': [IntElecCharge] * 48
  680. }
  681. self.set_elec_conf(infoDic)
  682. def set_elec_charge_by_device(self,lastSetConf):
  683. half_hour_price_list = lastSetConf['half_hour_price_list']
  684. for i in range(len(half_hour_price_list)):
  685. if half_hour_price_list[i] == '01':
  686. half_hour_price_list[i] = 'top_price_rate'
  687. elif half_hour_price_list[i] == '02':
  688. half_hour_price_list[i] = 'peak_price_rate'
  689. elif half_hour_price_list[i] == '03':
  690. half_hour_price_list[i] = 'normal_price_rate'
  691. elif half_hour_price_list[i] == '04':
  692. half_hour_price_list[i] = 'valley_price_rate'
  693. elecCharge = {
  694. 'top_price_rate' :lastSetConf['top_price_rate'],
  695. 'peak_price_rate' :lastSetConf['peak_price_rate'],
  696. 'normal_price_rate' :lastSetConf['normal_price_rate'],
  697. 'valley_price_rate' :lastSetConf['valley_price_rate'],
  698. 'half_hour_price_list': half_hour_price_list,
  699. }
  700. Device.get_collection().update_one({'devNo': self._device['devNo']}, {
  701. '$set': {
  702. 'devType.features.billAsService.elecCharge':elecCharge
  703. }
  704. })
  705. Device.invalid_device_cache(self._device['devNo'])
  706. def caculate_consume(self,data):
  707. serviceCharge = self._device.bill_as_service_feature.service_charge
  708. elec = data['total_elec']
  709. serviceFee = round(serviceCharge * elec,2)
  710. return serviceFee
  711. # def get_current_elec_charge_by_database(self):
  712. # device = None
  713. # para = device.bill_as_service_feature
  714. # half_hour_price_list = para.elecCharge.half_hour_price_list
  715. # nowTime = datetime.datetime.now().strftime('%H:%M')
  716. # timeNowList = nowTime.split(':')
  717. # index = int(timeNowList[0]) * 2 + 2 if int(timeNowList[1]) > 30 else 1 - 1
  718. # currentElecChargeRate = half_hour_price_list['half_hour_price_list'][index]
  719. # if currentElecChargeRate == '01':
  720. # elecCharge = para.elecCharge.top_price_rate
  721. # elif currentElecChargeRate == '02':
  722. # elecCharge = para.elecCharge.peak_price_rate
  723. # elif currentElecChargeRate == '03':
  724. # elecCharge = para.elecCharge.normal_price_rate
  725. # elif currentElecChargeRate == '04':
  726. # elecCharge = para.elecCharge.valley_price_rate
  727. # return elecCharge
  728. # 从参数设置中配置电费时电费信息存入数据库
  729. # 获取参数设置时电费信息存入数据库
  730. # 经销商配置充电费时直接下发到设备
  731. # 绑定设备时,默认信息直接下发到设备(避免出bug)
  732. # 获取从数据库中拿,不走设备
  733. # 查询当前服务费
  734. # 推送信息