hedong.py 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. """
  4. 和动V3 主板:
  5. 特性:
  6. 1 扫码使用27指令的充满自停 (下发时间 则按时间严格执行, 无时间则最大充电时间充满自停) 无法做到续充
  7. 2 刷卡: 只支持充满自停在线卡(该卡片在一台设备上只能刷一次)
  8. """
  9. import datetime
  10. import logging
  11. import time
  12. from arrow import Arrow
  13. from django.conf import settings
  14. from typing import Optional, Dict, TYPE_CHECKING
  15. from apps.web.agent.models import Agent
  16. from apps.web.constant import DeviceCmdCode, Const
  17. from apps.web.core.device_define.hedongv3 import Cmd, CMD_CODE
  18. from apps.web.core.helpers import ActionDeviceBuilder
  19. from apps.web.core.adapter.base import SmartBox, fill_2_hexByte
  20. from apps.web.core.exceptions import ServiceException
  21. from apps.web.core.networking import MessageSender
  22. from apps.web.dealer.models import Dealer
  23. from apps.web.device.models import Device, Group, DeviceType
  24. from apps.web.eventer.base import AckEvent
  25. from apps.web.user.models import ConsumeRecord, Card
  26. from taskmanager.mediator import task_caller
  27. if TYPE_CHECKING:
  28. from apps.web.api.models import APIStartDeviceRecord
  29. logger = logging.getLogger(__name__)
  30. class ChargingHDBox(SmartBox):
  31. """
  32. 和动V3协议
  33. """
  34. def __init__(self, device):
  35. super(ChargingHDBox, self).__init__(device)
  36. def isHaveStopEvent(self):
  37. return True
  38. def translate_funcode(self, funCode):
  39. funCodeDict = {
  40. '01': u'获取端口数量',
  41. '02': u'获取端口数据',
  42. '14': u'移动支付',
  43. '07': u'获取刷卡投币统计数据',
  44. '15': u'获取设备端口详情',
  45. '0F': u'获取端口状态',
  46. '0C': u'端口锁操作',
  47. '0D': u'端口开关',
  48. '1E': u'获取设备设置',
  49. '18': u'设置设备参数',
  50. '10': u'回复卡余额',
  51. }
  52. return funCodeDict.get(funCode, '')
  53. def translate_event_cmdcode(self, cmdCode):
  54. cmdDict = {
  55. '06': u'充电结束',
  56. '16': u'充电结束',
  57. '0A': u'故障',
  58. '10': u'刷卡上报',
  59. '20': u'启动设备',
  60. }
  61. return cmdDict.get(cmdCode, '')
  62. def test(self, coins,port=1):
  63. hexElec = fill_2_hexByte(hex(0), 4)
  64. hexPort = fill_2_hexByte(hex(port), 2)
  65. hexCoins = fill_2_hexByte(hex(1))
  66. hexTime = fill_2_hexByte(hex(60))
  67. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  68. {'IMEI': self._device['devNo'], "funCode": '14',
  69. 'data': hexPort + hexCoins + hexTime + hexElec})
  70. return devInfo
  71. def stop(self, port = None):
  72. infoDict = self.stop_charging_port(port)
  73. infoDict['remainder_time'] = infoDict['leftTime']
  74. return infoDict
  75. def start_from_api(self, record):
  76. # type: (APIStartDeviceRecord)->Optional[Dict]
  77. if 'chargeIndex' not in record.attachParas:
  78. from apps.web.api.exceptions import ApiParameterError
  79. raise ApiParameterError(u'请传入充电桩端口号chargeIndex参数')
  80. return super(ChargingHDBox, self).start_from_api(record)
  81. def analyze_event_data(self, data):
  82. cmdCode = data[4:6]
  83. #: 提交充电结束状态 (06)
  84. #: 老版本
  85. if cmdCode == CMD_CODE.DEVICE_SUBMIT_CHARGE_FINISHED_06:
  86. port = int(data[8:10], 16)
  87. leftTime = int(data[10:14], 16)
  88. reasonCode = data[14:16]
  89. desc_map = {
  90. "00": u'购买的充电时间或电量用完了。',
  91. "01": u'可能是插头被拔掉,或者电瓶已经充满。系统判断为异常断电,由于电瓶车充电器种类繁多,可能存在误差。如有问题,请您及时联系商家协助解决问题并恢复充电。',
  92. "02": u'恭喜您!电池已经充满电!',
  93. "0B": u'设备或端口出现问题,为了安全起见,被迫停止工作。建议您根据已经充电的时间评估是否需要到现场换到其他端口充电。'
  94. }
  95. return {
  96. 'status': Const.DEV_WORK_STATUS_IDLE,
  97. 'cmdCode': cmdCode,
  98. 'port': port,
  99. 'leftTime': leftTime,
  100. 'reason': desc_map[reasonCode],
  101. 'reasonCode': reasonCode
  102. }
  103. #: (高版本的) 提交充电结束状态 (16)
  104. elif cmdCode == CMD_CODE.DEVICE_SUBMIT_CHARGE_FINISHED_v2_16:
  105. port = int(data[8:10], 16)
  106. leftTime = int(data[10:14], 16)
  107. elec = int(data[14:18], 16) / 100.0
  108. reasonCode = data[18:20]
  109. # 如果设备类型有自定义结束推送通知就走自定义
  110. devType = DeviceType.objects(id=self.device['devType']['id']).first()
  111. finishedReasonDict = devType.finishedReasonDict
  112. if finishedReasonDict != {}:
  113. desc_map = finishedReasonDict
  114. else:
  115. desc_map = {
  116. '00': u'购买的充电时间或电量用完了。',
  117. '01': u'可能是插头被拔掉,或者电瓶已经充满。系统判断为异常断电,由于电瓶车充电器种类繁多,可能存在误差。如有问题,请您及时联系商家协助解决问题并恢复充电。',
  118. '02': u'恭喜您!电池已经充满电!',
  119. '03': u'警告!您的电池超功率,已经停止充电,为了公共安全,不建议您在该充电桩充电!提醒您,为了安全大功率的电池不要放入楼道、室内等位置充电哦',
  120. '04': u'远程断电。',
  121. '0B': u'设备或端口出现问题,为了安全起见,被迫停止工作。建议您根据已经充电的时间评估是否需要到现场换到其他端口充电。'
  122. }
  123. desc = desc_map.get(reasonCode, u'电池没有充满!原因未知。')
  124. return {
  125. 'status': Const.DEV_WORK_STATUS_IDLE,
  126. 'cmdCode': cmdCode,
  127. 'port': port,
  128. 'leftTime': leftTime,
  129. 'elec': elec,
  130. 'reason': desc,
  131. 'reasonCode': reasonCode
  132. }
  133. #: 上传设备故障
  134. elif cmdCode == CMD_CODE.DEVICE_SUBMIT_DEVICE_FAULT_0A:
  135. port = int(data[8:10], 16)
  136. errCode = int(data[10:14], 16)
  137. if errCode == 1:
  138. statusInfo = '端口{}:继电器粘连'.format(port)
  139. else:
  140. statusInfo = '设备故障'
  141. return {
  142. 'status': Const.DEV_WORK_STATUS_FAULT,
  143. 'statusInfo': statusInfo,
  144. 'cmdCode': cmdCode,
  145. 'port': port,
  146. 'FaultCode': errCode
  147. }
  148. # 用户刷卡上报的信息
  149. #: 在线卡上传卡号,预扣费
  150. #: 示例:
  151. #: CARD_ID CARD_CST CARD_OPE
  152. #: 0x00000000 0x00 0x00
  153. #: 参数
  154. #: CARD_ID : IC 卡卡号
  155. #: CARD_SURP: 卡片需要改变的金额信息(以角为单位)
  156. #: CARD_OPE: 0x00 是扣费(减少),0x01 是充值(增加)。
  157. #: 回复
  158. #: RES: 0x00,表示扣费成功,0x01 表示余额不足,0x02 表示非法卡。
  159. #: CARD_SURP: 表示卡余额(以角为单位)。
  160. elif cmdCode == CMD_CODE.SWIPE_CARD_10:
  161. group = Group.get_group(self._device['groupId'])
  162. dealer = Dealer.get_dealer(group['ownerId'])
  163. agent = Agent.objects(id = dealer['agentId']).first()
  164. device = Device.objects(devNo=self._device['devNo']).first()
  165. devType = DeviceType.objects(id=device['devType']['id']).first()
  166. cardNo = str(int(data[8:16], 16))
  167. # 兼容以前的老代码, 先走以前的老代码, 如果有新代码再走新的.
  168. if agent is not None and 'cardNoReverse' in agent.features:
  169. cardData = data[14:16] + data[12:14] + data[10:12] + data[8:10]
  170. cardNo = str(int(cardData, 16))
  171. if 'cardNoReverse' in devType.features:
  172. if devType.features['cardNoReverse'] is True:
  173. cardData = data[14:16] + data[12:14] + data[10:12] + data[8:10]
  174. cardNo = str(int(cardData, 16))
  175. else:
  176. cardNo = str(int(data[8:16], 16))
  177. else:
  178. pass
  179. #: 卡里的余额,以角为单位
  180. preFee = int(data[16:18], 16) / 10.0
  181. #: 操作符 00(增加) | 01(减少)
  182. oper = data[18:20]
  183. return {'cardNo': cardNo, 'preFee': preFee, 'cmdCode': cmdCode, 'oper': oper, "sourceData": data}
  184. #: 上报投币打开的信息
  185. elif cmdCode == CMD_CODE.DEVICE_SUBMIT_OFFLINE_COINS_20: # 启动设备
  186. port = int(data[8:10], 16)
  187. needTime = int(data[10:14], 16)
  188. elec = int(data[14:18], 16) / 100.0
  189. consumeTypeTemp = data[18:20]
  190. if consumeTypeTemp == '00':
  191. consumeType = 'coin'
  192. elif consumeTypeTemp == '01':
  193. consumeType = 'card'
  194. elif consumeTypeTemp == '03':
  195. consumeType = 'server'
  196. else:
  197. consumeType = 'other'
  198. money = int(data[20:22], 16) / 10.0
  199. return {
  200. 'cmdCode': cmdCode,
  201. 'port': port,
  202. 'needTime': needTime,
  203. 'elec': elec,
  204. 'consumeType': consumeType,
  205. 'coins': money
  206. }
  207. # 4个故障的告警
  208. elif cmdCode == CMD_CODE.DEVICE_FAULT_FIRE:
  209. return {"cmdCode": cmdCode, "fault": u"火灾报警"}
  210. elif cmdCode == CMD_CODE.DEVICE_FAULT_SMOKE:
  211. return {"cmdCode": cmdCode, "fault": "烟雾报警"}
  212. elif cmdCode == CMD_CODE.DEVICE_FAULT_TEMPERATURE:
  213. return {"cmdCode": cmdCode, "fault": "温度超限告警"}
  214. elif cmdCode == CMD_CODE.DEVICE_FAULT_POWER:
  215. return {"cmdCode": cmdCode, "fault": "功率超线告警"}
  216. elif cmdCode == CMD_CODE.DEVICE_FAULT_ALTER:
  217. fire = int(data[8:10],16)
  218. smoke = int(data[10:12],16)
  219. overload = int(data[12:14],16)
  220. overheat = int(data[14:16],16)
  221. result = {"cmdCode": cmdCode}
  222. fault = []
  223. if fire:
  224. fault.append("火灾报警")
  225. if smoke:
  226. fault.append("烟雾报警")
  227. if overload:
  228. fault.append("温度超限告警")
  229. if overheat:
  230. fault.append("功率超线告警")
  231. if fault:
  232. fault = '--'.join(fault)
  233. result.update({"fault": fault})
  234. return result
  235. # 2个状态量上报
  236. elif cmdCode == CMD_CODE.DEVICE_FAULT_POWER:
  237. maxPower = int(data[8:12],16)
  238. return {"cmdCode": cmdCode, "maxPower": maxPower}
  239. elif cmdCode == CMD_CODE.DEVICE_FAULT_TEMPERATURE:
  240. maxTemperature = int(data[8:10],16)
  241. return {"cmdCode": cmdCode, "maxTemperature": maxTemperature}
  242. elif cmdCode == CMD_CODE.DEVICE_ELEC:
  243. elec = int(data[8:12], 16)
  244. return {"cmdCode": cmdCode, "elec": elec}
  245. elif cmdCode == CMD_CODE.DEVICE_TEMPERATURE:
  246. temperature = int(data[10:12], 16)
  247. if data[8:10] == "00":
  248. temperature = temperature * -1
  249. return {"cmdCode": cmdCode, "temperature": temperature}
  250. elif cmdCode == CMD_CODE.DEVICE_CARD_CHARGE_2D:
  251. _result = data[6: 8]
  252. _port = str(int(data[8: 10], 16))
  253. _time = int(data[10: 14], 16)
  254. _elec = int(data[14: 18], 16) / 100.0
  255. _type = data[18: 20]
  256. _coins = int(data[20: 22], 16)
  257. _cardNo = str(int(data[22: 30], 16))
  258. _card_cst = int(data[30: 34], 16) / 10.0
  259. _operation = data[34: 36]
  260. _card_type = data[36: 38]
  261. _session_id = data[38: 46]
  262. return {
  263. "portStr": _port,
  264. "time": _time,
  265. "elec": _elec,
  266. "chargeType": _type,
  267. "coins": _coins,
  268. "cardNo": _cardNo,
  269. "cardCst": _card_cst,
  270. "operation": _operation,
  271. "cardType": _card_type,
  272. "sessionId": _session_id,
  273. "cmdCode": cmdCode,
  274. "result": _result,
  275. "sourceData": data
  276. }
  277. elif cmdCode == CMD_CODE.DEVICE_REAL_TIME_REPORT_21:
  278. _result = data[6: 8]
  279. port_num = int(data[8:10],16)
  280. port_info_urat_info = data[10:170]
  281. port_status_desc = {
  282. "01": "端口空闲",
  283. "02": "端口正在使用",
  284. "03": "端口禁用",
  285. "04": "端口故障",
  286. }
  287. port_info = {}
  288. for index in xrange(0, 160, 16):
  289. item = port_info_urat_info[index: index + 16]
  290. one_port = {}
  291. one_port["port"] = int(item[:2], 16)
  292. one_port["portStatus"] = item[2:4]
  293. one_port["portStatusDesc"] = port_status_desc.get(item[2:4])
  294. one_port["leftTime"] = int(item[4:8], 16)
  295. one_port["power"] = int(item[8:12], 16)
  296. one_port["elec"] = int(item[8:12], 16) * 0.01
  297. port_info[str(one_port["port"])] = one_port
  298. return {"cmdCode": cmdCode, "result": _result, "sourceData": data, "portNum": port_num,
  299. "portInfo": port_info}
  300. elif cmdCode == CMD_CODE.DEVICE_FINISHED_REPORT_2C:
  301. result = data[6: 8]
  302. port = int(data[8:10], 16)
  303. leftTime = int(data[10:14], 16)
  304. leftElec = round(int(data[14:18], 16) * 0.01, 2)
  305. cardNoHex = data[18:26]
  306. cardCst = round(int(data[26:30], 16) * 0.1, 2)
  307. cardOpe = data[30:32]
  308. cardType = data[32:34]
  309. reason = data[34:36]
  310. sessionId = data[36:44]
  311. devType = DeviceType.objects(id=self.device['devType']['id']).first()
  312. finishedReasonDict = devType.finishedReasonDict
  313. if finishedReasonDict != {}:
  314. desc_map = finishedReasonDict
  315. else:
  316. desc_map = {
  317. '00': u'购买的充电时间或电量用完了。',
  318. '01': u'可能是插头被拔掉,或者电瓶已经充满。系统判断为异常断电,由于电瓶车充电器种类繁多,可能存在误差。如有问题,请您及时联系商家协助解决问题并恢复充电。',
  319. '02': u'恭喜您!电池已经充满电!',
  320. '03': u'警告!您的电池超功率,已经停止充电,为了公共安全,不建议您在该充电桩充电!提醒您,为了安全大功率的电池不要放入楼道、室内等位置充电哦',
  321. '04': u'远程断电。',
  322. '0B': u'设备或端口出现问题,为了安全起见,被迫停止工作。建议您根据已经充电的时间评估是否需要到现场换到其他端口充电。'
  323. }
  324. reasonDesc = desc_map.get(reason, u'电池没有充满!原因未知。')
  325. return {
  326. 'sourceData': data,
  327. 'result': result,
  328. 'port': port,
  329. 'leftTime': leftTime,
  330. 'leftElec': leftElec,
  331. 'cardNoHex': cardNoHex,
  332. 'cardCst': cardCst,
  333. 'cardOpe': cardOpe,
  334. 'cardType': cardType,
  335. 'reason': reason,
  336. 'sessionId': sessionId,
  337. 'cmdCode': CMD_CODE.DEVICE_FINISHED_REPORT_2C,
  338. 'reasonDesc': reasonDesc
  339. }
  340. else:
  341. logger.info("receive data <{}>, cmd is invalid".format(data))
  342. def get_dev_consume_count(self):
  343. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  344. {'IMEI': self._device['devNo'], "funCode": '07', 'data': '00'})
  345. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  346. if devInfo['rst'] == -1:
  347. raise ServiceException(
  348. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  349. elif devInfo['rst'] == 1:
  350. raise ServiceException(
  351. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  352. data = devInfo['data'][6::]
  353. if data[0:2] == '01': # 表示成功
  354. pass
  355. else:
  356. raise ServiceException({'result': 2, 'description': u'获取统计数据失败,请重试看能否解决'})
  357. cardFee = int(data[2:6], 16) / 10.0 # 以角为单位
  358. coinFee = int(data[6:10], 16) # 以元为单位
  359. return {'cardFee': cardFee, 'coinFee': coinFee}
  360. def get_port_info(self, line):
  361. data = fill_2_hexByte(hex(int(line)), 2)
  362. devInfo = MessageSender.send(self.device, self.make_random_cmdcode(),
  363. {'IMEI': self._device['devNo'], "funCode": '15', 'data': data})
  364. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  365. if devInfo['rst'] == -1:
  366. raise ServiceException(
  367. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  368. elif devInfo['rst'] == 1:
  369. raise ServiceException(
  370. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  371. data = devInfo['data'][6::]
  372. if data[0:2] == '01': # 表示成功
  373. pass
  374. else:
  375. raise ServiceException({'result': 2, 'description': u'获取统计数据失败,请重试看能否解决'})
  376. leftTime = int(data[4:8], 16)
  377. if data[8:12] == 'FFFF':
  378. power = 0
  379. else:
  380. power = int(data[8:12], 16)
  381. if data[12:16] == 'FFFF':
  382. elec = 0
  383. else:
  384. elec = round(int(data[12:16], 16) * 0.01, 2)
  385. if data[16:20] == 'FFFF':
  386. surp = 0
  387. else:
  388. surp = int(data[16:20], 16)
  389. result = {}
  390. result['power'] = power
  391. port = "%.2X" % int(line)
  392. try:
  393. payload = {"IMEI": self._device['devNo'],
  394. "data": port,
  395. "funCode": "88"}
  396. data = self.send_mqtt(data=payload)
  397. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  398. lineInfo =ctrInfo.get(str(line), {})
  399. orderType = data.get('order_type')
  400. if orderType == 'com_start':
  401. # result['usedElec'] = round(data.get('elec', 0) / 3600000.0, 4) or 0
  402. # result['needElec'] = round(data['elecMax'] / 3600000.0, 2)
  403. timeFee = data['balance'] * 0.1 * round(float(data.get('time', 0)) / float(data['timeMax']), 2)
  404. elecFee = data['balance'] * 0.1 * round(float(data.get('elec', 0)) / float(data['elecMax']), 2)
  405. result['consumeMoney'] = round(max(timeFee, elecFee), 2)
  406. result['leftMoney'] = round(data['balance'] * 0.1 - result['consumeMoney'], 2)
  407. result['usedTime'] = round((time.time() - data['sts'] + 59) / 60)
  408. elif orderType == 'id_start':
  409. result['usedElec'] = round(data.get('elec', 0) / 3600000.0, 4) or 0
  410. # result['needElec'] = round(data['elecMax'] / 3600000.0, 2)
  411. # result['leftElec'] = round(data['elecMax'] / 3600000.0, 2)
  412. order = ConsumeRecord.objects.filter(startKey=data.get('order_id')).first() # type:ConsumeRecord
  413. if order:
  414. coins = round(order.coin, 2)
  415. timeFee = coins * round(float(data.get('time', 0)) / float(data['timeMax']), 2)
  416. elecFee = coins * round(float(data.get('elec', 0)) / float(data['elecMax']), 2)
  417. result['consumeMoney'] = round(max(timeFee, elecFee), 2)
  418. result['leftMoney'] = round(coins - result['consumeMoney'], 2)
  419. result['usedTime'] = round((time.time() - data['sts'] + 59) / 60)
  420. result['cardNo'] = '{}'.format(int(data.get('cardNo'), 16))
  421. card = Card.objects.filter(cardNo=result['cardNo']).first()
  422. if card:
  423. result['cardName'] = card.cardName or card.nickName
  424. result['usedTime'] = round((time.time() - data['sts'] + 59) / 60)
  425. result['startTime'] = Arrow.utcfromtimestamp(data['sts']).format('MM-DD HH:mm:ss')
  426. # 跳过后面的流水处理
  427. # result['skipPipelineProcessing'] = True
  428. else:
  429. if lineInfo['consumeType'] == 'coin':
  430. result['leftTime'] = leftTime
  431. result['leftElec'] = elec
  432. except Exception:
  433. import traceback
  434. logger.info(traceback.format_exc())
  435. return result
  436. def get_port_status(self, force = False):
  437. if force:
  438. return self.get_port_status_from_dev()
  439. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  440. statusDict = {}
  441. if not ctrInfo.has_key('allPorts'):
  442. self.get_port_status_from_dev()
  443. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  444. allPorts = ctrInfo.get('allPorts', 10)
  445. # allPorts 有的时候会为0, 这个时候强制设置为10
  446. if allPorts == 0:
  447. allPorts = 10
  448. for ii in range(allPorts):
  449. tempDict = ctrInfo.get(str(ii + 1), {})
  450. if tempDict.has_key('status'):
  451. statusDict[str(ii + 1)] = {'status': tempDict.get('status')}
  452. elif tempDict.has_key('isStart'):
  453. if tempDict['isStart']:
  454. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_WORKING}
  455. else:
  456. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  457. else:
  458. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  459. allPorts, usedPorts, usePorts = self.get_port_static_info(statusDict)
  460. Device.update_dev_control_cache(self._device['devNo'],
  461. {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  462. return statusDict
  463. def get_port_status_from_dev(self):
  464. devInfo = MessageSender.send(self.device, self.make_random_cmdcode(),
  465. {'IMEI': self._device['devNo'], "funCode": '0F', 'data': '00'})
  466. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  467. if devInfo['rst'] == -1:
  468. raise ServiceException(
  469. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  470. elif devInfo['rst'] == 1:
  471. raise ServiceException(
  472. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  473. data = devInfo['data'][6::]
  474. if data[0:2] == '01': # 表示成功
  475. pass
  476. else:
  477. raise ServiceException({'result': 2, 'description': u'获取统计数据失败,请重试看能否解决'})
  478. result = {}
  479. portNum = int(data[2:4], 16)
  480. portData = data[4::]
  481. ii = 0
  482. while ii < portNum:
  483. port = int(portData[ii * 4:ii * 4 + 2], 16)
  484. statusTemp = portData[ii * 4 + 2:ii * 4 + 4]
  485. if statusTemp == '01':
  486. status = {'status': Const.DEV_WORK_STATUS_IDLE}
  487. elif statusTemp == '02':
  488. status = {'status': Const.DEV_WORK_STATUS_WORKING}
  489. elif statusTemp == '03':
  490. status = {'status': Const.DEV_WORK_STATUS_FORBIDDEN}
  491. elif statusTemp == '04':
  492. status = {'status': Const.DEV_WORK_STATUS_FAULT}
  493. ii += 1
  494. result[str(port)] = status
  495. allPorts, usedPorts, usePorts = self.get_port_static_info(result)
  496. Device.update_dev_control_cache(self._device['devNo'],
  497. {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  498. # 这里存在多线程更新缓存的场景,可能性比较低,但是必须先取出来,然后逐个更新状态,然后再记录缓存
  499. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  500. for strPort, info in result.items():
  501. if ctrInfo.has_key(strPort):
  502. ctrInfo[strPort].update({'status': info['status']})
  503. else:
  504. ctrInfo[strPort] = info
  505. Device.update_dev_control_cache(self._device['devNo'], ctrInfo)
  506. return result
  507. def lock_unlock_port(self, port, lock = True):
  508. lockStr = '00' if lock else '01'
  509. portStr = fill_2_hexByte(hex(int(port)), 2)
  510. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  511. {'IMEI': self._device['devNo'], "funCode": '0C', 'data': portStr + lockStr})
  512. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  513. if devInfo['rst'] == -1:
  514. raise ServiceException(
  515. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  516. elif devInfo['rst'] == 1:
  517. raise ServiceException(
  518. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  519. data = devInfo['data'][6::]
  520. if data[0:2] == '01': # 表示成功
  521. pass
  522. else:
  523. raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'})
  524. if lock:
  525. Device.update_dev_control_cache(self._device['devNo'],
  526. {str(port): {'status': Const.DEV_WORK_STATUS_FORBIDDEN}})
  527. else:
  528. Device.update_dev_control_cache(self._device['devNo'], {str(port): {'status': Const.DEV_WORK_STATUS_IDLE}})
  529. def active_deactive_port(self, port, active):
  530. if not active:
  531. self.stop_charging_port(port)
  532. devInfo = Device.get_dev_control_cache(self._device['devNo'])
  533. portCtrInfo = devInfo.get(str(port), {})
  534. portCtrInfo.update({'isStart': False, 'status': Const.DEV_WORK_STATUS_IDLE,
  535. 'endTime': datetime.datetime.now().strftime(Const.DATETIME_FMT)})
  536. newValue = {str(port): portCtrInfo}
  537. Device.update_dev_control_cache(self._device['devNo'], newValue)
  538. else:
  539. raise ServiceException({'result': 2, 'description': u'此设备不支持直接打开端口'})
  540. def stop_charging_port(self, port):
  541. portStr = fill_2_hexByte(hex(int(port)), 2)
  542. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  543. {'IMEI': self._device['devNo'], "funCode": '0D', 'data': portStr + '00'}, timeout=7)
  544. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  545. if devInfo['rst'] == -1:
  546. raise ServiceException(
  547. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  548. elif devInfo['rst'] == 1:
  549. raise ServiceException(
  550. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  551. data = devInfo['data'][6::]
  552. if data[0:2] == '01': # 表示成功
  553. pass
  554. else:
  555. raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'})
  556. port = int(data[2:4], 16)
  557. leftTime = int(data[4:8], 16)
  558. result = {'port': port, 'leftTime': leftTime}
  559. self._postpaidProcessing(result)
  560. return result
  561. # 获取设备配置参数
  562. def get_dev_setting(self):
  563. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  564. {'IMEI': self._device['devNo'], "funCode": '1E', 'data': '00'})
  565. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  566. if devInfo['rst'] == -1:
  567. raise ServiceException(
  568. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  569. elif devInfo['rst'] == 1:
  570. raise ServiceException(
  571. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  572. data = devInfo['data'][6::]
  573. if data[0:2] == '01': # 表示成功
  574. pass
  575. else:
  576. raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'})
  577. confData = data[2:-2]
  578. coinMin = int(confData[0:4], 16)
  579. cardMin = int(confData[4:8], 16)
  580. coinElec = int(confData[8:10], 16)
  581. cardElec = int(confData[10:12], 16)
  582. cst = int(confData[12:14], 16)
  583. powerMax1 = int(confData[14:18], 16)
  584. powerMax2 = int(confData[18:22], 16)
  585. powerMax3 = int(confData[22:26], 16)
  586. powerMax4 = int(confData[26:30], 16)
  587. power2Ti = int(confData[30:32], 16)
  588. power3Ti = int(confData[32:34], 16)
  589. power4Ti = int(confData[34:36], 16)
  590. spRecMon = int(confData[36:38], 16)
  591. spFullEmpty = int(confData[38:40], 16)
  592. fullPowerMin = int(confData[40:42], 16)
  593. fullChargeTime = int(confData[42:44], 16)
  594. elecTimeFirst = int(confData[44:46], 16)
  595. dev = Device.objects.get(devNo = self._device['devNo'])
  596. billingType = dev.otherConf.get('billingType', 'time')
  597. elecFee = dev.otherConf.get('elecFee', 0.9)
  598. # refundProtection = dev.otherConf.get('refundProtection', 0)
  599. refundProtectionTime = dev.otherConf.get('refundProtectionTime', 5)
  600. # 为了预防万一,这里顺便把数据库的数据也更新一下,主要是以设备为准的数据,更新下
  601. dev.otherConf.update({'cardMin': cardMin})
  602. try:
  603. dev.save()
  604. except Exception, e:
  605. pass
  606. deviceConf = dev.otherConf.get('deviceConfigs', {})
  607. resultDict = {
  608. 'coinMin': coinMin,
  609. 'cardMin': cardMin,
  610. 'remoteMin': deviceConf.get('remoteMin', 240),
  611. 'coinElec': coinElec,
  612. 'cardElec': cardElec,
  613. 'remoteElec': deviceConf.get('remoteElec', 10),
  614. 'cst': cst,
  615. 'powerMax1': powerMax1,
  616. 'powerMax2': powerMax2,
  617. 'powerMax3': powerMax3,
  618. 'powerMax4': powerMax4,
  619. 'power2Ti': power2Ti,
  620. 'power3Ti': power3Ti,
  621. 'power4Ti': power4Ti,
  622. 'spRecMon': spRecMon,
  623. 'spFullEmpty': spFullEmpty,
  624. 'fullPowerMin': fullPowerMin,
  625. 'fullChargeTime': fullChargeTime,
  626. 'elecTimeFirst': elecTimeFirst,
  627. 'billingType': billingType,
  628. 'elecFee': elecFee,
  629. 'refundProtectionTime': refundProtectionTime
  630. }
  631. self.do_update_configs(resultDict)
  632. consumeInfo = self.get_dev_consume_count()
  633. resultDict.update(consumeInfo)
  634. deviceInfo = self.get_real_time_device_info()
  635. resultDict.update(deviceInfo)
  636. postpaid_price_rules = self.get_postpaid_price_rules()
  637. resultDict.update(postpaid_price_rules)
  638. return resultDict
  639. # 设置设备配置参数
  640. def set_dev_setting(self, setConf):
  641. data = ''
  642. data += fill_2_hexByte(hex(int(setConf['coinMin'])), 4)
  643. data += fill_2_hexByte(hex(int(setConf['cardMin'])), 4)
  644. data += fill_2_hexByte(hex(int(setConf['coinElec'])), 2)
  645. data += fill_2_hexByte(hex(int(setConf['cardElec'])), 2)
  646. data += fill_2_hexByte(hex(int(setConf['cst'])), 2)
  647. data += fill_2_hexByte(hex(int(setConf['powerMax1'])), 4)
  648. data += fill_2_hexByte(hex(int(setConf['powerMax2'])), 4)
  649. data += fill_2_hexByte(hex(int(setConf['powerMax3'])), 4)
  650. data += fill_2_hexByte(hex(int(setConf['powerMax4'])), 4)
  651. data += fill_2_hexByte(hex(int(setConf['power2Ti'])), 2)
  652. data += fill_2_hexByte(hex(int(setConf['power3Ti'])), 2)
  653. data += fill_2_hexByte(hex(int(setConf['power4Ti'])), 2)
  654. data += fill_2_hexByte(hex(int(setConf['spRecMon'])), 2)
  655. data += fill_2_hexByte(hex(int(setConf['spFullEmpty'])), 2)
  656. data += fill_2_hexByte(hex(int(setConf['fullPowerMin'])), 2)
  657. data += fill_2_hexByte(hex(int(setConf['fullChargeTime'])), 2)
  658. data += fill_2_hexByte(hex(int(setConf['elecTimeFirst'])), 2)
  659. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  660. {'IMEI': self._device['devNo'], "funCode": '18', 'data': data})
  661. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  662. if devInfo['rst'] == -1:
  663. raise ServiceException(
  664. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  665. elif devInfo['rst'] == 1:
  666. raise ServiceException(
  667. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  668. data = devInfo['data'][6::]
  669. if data[0:2] == '01': # 表示成功
  670. dev = Device.objects.get(devNo = self._device['devNo'])
  671. dev.otherConf.update({'billingType': setConf['billingType']})
  672. dev.otherConf.update({'elecFee': setConf['elecFee']})
  673. dev.otherConf.update({'refundProtectionTime': setConf['refundProtectionTime']})
  674. dev.otherConf.update({'cardMin': setConf['cardMin']})
  675. dev.otherConf.update({'remoteMin': setConf['remoteMin']})
  676. dev.otherConf.update({'remoteElec': setConf['remoteElec']})
  677. dev.otherConf.get('deviceConfigs', {}).update({'remoteMin': setConf['remoteMin']})
  678. dev.otherConf.get('deviceConfigs', {}).update({'remoteElec': setConf['remoteElec']})
  679. dev.save()
  680. Device.invalid_device_cache(self.device.devNo)
  681. else:
  682. raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'})
  683. # 更新一次参数到数据库中
  684. self.get_dev_setting()
  685. def response_use_card(self, res, balance):
  686. data = '55061001'
  687. data = data + res + fill_2_hexByte(hex(int(balance * 10)), 4)
  688. devInfo = MessageSender.send(self.device, DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE,
  689. {'IMEI': self._device['devNo'], "funCode": '10', 'data': data})
  690. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  691. if devInfo['rst'] == -1:
  692. raise ServiceException(
  693. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  694. elif devInfo['rst'] == 1: # 等于1的时候,说明服务器和远程模块通讯OK,响应已经收到,不需要异常处理
  695. pass
  696. def set_device_function_param(self, request, lastSetConf):
  697. coinMin = request.POST.get('coinMin', None)
  698. cardMin = request.POST.get('cardMin', None)
  699. remoteMin = request.POST.get('remoteMin', None)
  700. coinElec = request.POST.get('coinElec', None)
  701. cardElec = request.POST.get('cardElec', None)
  702. remoteElec = request.POST.get('remoteElec', None)
  703. cst = request.POST.get('cst', None)
  704. powerMax1 = request.POST.get('powerMax1', None)
  705. powerMax2 = request.POST.get('powerMax2', None)
  706. powerMax3 = request.POST.get('powerMax3', None)
  707. powerMax4 = request.POST.get('powerMax4', None)
  708. power2Ti = request.POST.get('power2Ti', None)
  709. power3Ti = request.POST.get('power3Ti', None)
  710. power4Ti = request.POST.get('power4Ti', None)
  711. spRecMon = request.POST.get('spRecMon', None)
  712. spFullEmpty = request.POST.get('spFullEmpty', None)
  713. fullPowerMin = request.POST.get('fullPowerMin', None)
  714. fullChargeTime = request.POST.get('fullChargeTime', None)
  715. elecTimeFirst = request.POST.get('elecTimeFirst', None)
  716. billingType = request.POST.get('billingType', None)
  717. elecFee = request.POST.get('elecFee', None)
  718. refundProtection = request.POST.get('refundProtection', None)
  719. refundProtectionTime = request.POST.get('refundProtectionTime', None)
  720. lowPowerDetectionTime = request.POST.get('lowPowerDetectionTime', None)
  721. lowPowerDetectionSwitch = request.POST.get('lowPowerDetectionSwitch', None)
  722. lowPowerDetectionPower = request.POST.get('lowPowerDetectionPower', None)
  723. # 这几个参数是墨小智V3特有的
  724. stWTime = request.POST.get('stWTime', None)
  725. temThre = request.POST.get('temThre', None)
  726. freeUse = request.POST.get('freeUse', None)
  727. relayMasterSwitch = request.POST.get('relayMasterSwitch', None)
  728. if coinMin:
  729. lastSetConf.update({'coinMin': int(coinMin)})
  730. if cardMin:
  731. lastSetConf.update({'cardMin': int(cardMin)})
  732. if remoteMin:
  733. lastSetConf.update({'remoteMin': int(remoteMin)})
  734. if coinElec:
  735. lastSetConf.update({'coinElec': int(coinElec)})
  736. if cardElec:
  737. lastSetConf.update({'cardElec': int(cardElec)})
  738. if remoteElec:
  739. lastSetConf.update({'remoteElec': int(remoteElec)})
  740. if cst:
  741. lastSetConf.update({'cst': int(cst)})
  742. if powerMax1:
  743. lastSetConf.update({'powerMax1': int(powerMax1)})
  744. if powerMax2:
  745. lastSetConf.update({'powerMax2': int(powerMax2)})
  746. if powerMax3:
  747. lastSetConf.update({'powerMax3': int(powerMax3)})
  748. if powerMax4:
  749. lastSetConf.update({'powerMax4': int(powerMax4)})
  750. if power2Ti:
  751. lastSetConf.update({'power2Ti': int(power2Ti)})
  752. if power3Ti:
  753. lastSetConf.update({'power3Ti': int(power3Ti)})
  754. if power4Ti:
  755. lastSetConf.update({'power4Ti': int(power4Ti)})
  756. if spRecMon:
  757. lastSetConf.update({'spRecMon': int(spRecMon)})
  758. if spFullEmpty:
  759. lastSetConf.update({'spFullEmpty': int(spFullEmpty)})
  760. if fullPowerMin:
  761. lastSetConf.update({'fullPowerMin': int(fullPowerMin)})
  762. if fullChargeTime:
  763. lastSetConf.update({'fullChargeTime': int(fullChargeTime)})
  764. if elecTimeFirst:
  765. lastSetConf.update({'elecTimeFirst': int(elecTimeFirst)})
  766. if billingType:
  767. lastSetConf.update({'billingType': billingType})
  768. if elecFee:
  769. lastSetConf.update({'elecFee': elecFee})
  770. if refundProtection:
  771. lastSetConf.update({'refundProtection': int(refundProtection)})
  772. if refundProtectionTime:
  773. lastSetConf.update({'refundProtectionTime': int(refundProtectionTime)})
  774. if lowPowerDetectionTime:
  775. lastSetConf.update({'lowPowerDetectionTime': int(lowPowerDetectionTime)})
  776. if lowPowerDetectionSwitch:
  777. lastSetConf.update({'lowPowerDetectionSwitch': int(lowPowerDetectionSwitch)})
  778. if lowPowerDetectionPower:
  779. lastSetConf.update({'lowPowerDetectionPower': int(lowPowerDetectionPower)})
  780. # 这几个参数是墨小智V3特有的
  781. if stWTime:
  782. lastSetConf.update({'stWTime': int(stWTime)})
  783. if temThre:
  784. lastSetConf.update({'temThre': int(temThre)})
  785. if freeUse:
  786. lastSetConf.update({'freeUse': int(freeUse)})
  787. self.set_dev_setting(lastSetConf)
  788. # 新增总继电器开关
  789. if relayMasterSwitch:
  790. try:
  791. self.relay_master_switch(relayMasterSwitch)
  792. except Exception as e:
  793. pass
  794. def handle_clear_start_error(self, port):
  795. dealer = Dealer.objects.get(id = self._device["ownerId"])
  796. if not dealer or not dealer.managerialOpenId:
  797. return
  798. group = Group.get_group(self._device["groupId"])
  799. self.lock_unlock_port(port, lock = False)
  800. notifyData = {
  801. "title": "设备故障报警解除",
  802. "device": u"{gNum}组-{lc}-{port}端口".format(gNum = self._device["groupNumber"],
  803. lc = self._device["logicalCode"],
  804. port = port),
  805. "location": u"{address}-{groupName}".format(address = group["address"], groupName = group["groupName"]),
  806. "fault": u"当前端口已被正常启动,端口故障解除",
  807. "notifyTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  808. }
  809. task_caller(
  810. func_name = 'report_to_dealer_via_wechat',
  811. openId = dealer.managerialOpenId,
  812. dealerId = str(dealer.id),
  813. templateName = "device_fault",
  814. **notifyData
  815. )
  816. self.lock_unlock_port(port, lock = False)
  817. def handle_out_start_error(self, port):
  818. dealer = Dealer.objects.get(id = self._device["ownerId"])
  819. if not dealer or not dealer.managerialOpenId:
  820. return
  821. group = Group.get_group(self._device["groupId"])
  822. times = Device.get_error_start_times(self._device["devNo"], port)
  823. self.lock_unlock_port(port, lock = True)
  824. notifyData = {
  825. "title": u"注意,您的设备可能发生故障!",
  826. "device": u"{gNum}组-{lc}-{port}端口".format(gNum = self._device["groupNumber"],
  827. lc = self._device["logicalCode"],
  828. port = port),
  829. "location": u"{address}-{groupName}".format(address = group["address"], groupName = group["groupName"]),
  830. "fault": u"当前设备端口连续启动 {times} 失败,目前该端口已经被自动禁用,请注意该端口是否发生故障".format(times = times),
  831. "notifyTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  832. }
  833. task_caller(
  834. func_name = 'report_to_dealer_via_wechat',
  835. openId = dealer.managerialOpenId,
  836. dealerId = str(dealer.id),
  837. templateName = "device_fault",
  838. **notifyData
  839. )
  840. # 麦总的告警3次触发,同时锁定设备,为避免解锁设备之后 再次触发 这个地方需要将其缓存清空
  841. Device.delete_error_start_times(self._device["devNo"], port)
  842. @SmartBox.check_device_features(device_features=["fun_code_30_31"])
  843. def relay_master_switch(self,relayMasterSwitch=1):
  844. if relayMasterSwitch == 1:
  845. data = "01"
  846. else:
  847. data = "00"
  848. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  849. {'IMEI': self._device['devNo'], 'funCode': '31', 'data': data},timeout=7)
  850. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  851. if devInfo['rst'] == -1:
  852. raise ServiceException(
  853. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  854. elif devInfo['rst'] == 1:
  855. raise ServiceException(
  856. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  857. dev = Device.objects.get(devNo=self._device['devNo'])
  858. dev.otherConf.update({'relayMasterSwitch': relayMasterSwitch})
  859. dev.save()
  860. Device.invalid_device_cache(dev.devNo)
  861. @SmartBox.check_device_features(device_features=["fun_code_30_31"], no_features_to_return={"haveStatus": False})
  862. def get_real_time_device_info(self):
  863. try:
  864. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  865. {'IMEI': self._device['devNo'], 'funCode': '30', 'data': '00'},timeout=7)
  866. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  867. if devInfo['rst'] == -1:
  868. raise ServiceException(
  869. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  870. elif devInfo['rst'] == 1:
  871. raise ServiceException(
  872. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  873. except Exception:
  874. return {"haveStatus": False}
  875. data = devInfo.get("data")
  876. maxPower = int(data[8:12], 16)
  877. elec = int(data[12:16], 16)
  878. maxTemperature = int(data[16:18], 16)
  879. base = -1 if int(data[18:20], 16) == 1 else 1
  880. temperature = int(data[20:22], 16) * base
  881. fire = int(data[22:24], 16)
  882. smoke = int(data[24:26], 16)
  883. overheat = int(data[26:28], 16)
  884. overload = int(data[28:30], 16)
  885. relayMasterSwitch = int(data[30:32], 16)
  886. return {"haveStatus": True, "maxPower": maxPower, "elec": elec, "maxTemperature": maxTemperature,
  887. "temperature": temperature,
  888. "fire": fire, "smoke": smoke, "overheat": overheat, "overload": overload,
  889. "relayMasterSwitch": relayMasterSwitch, }
  890. def _response_to_2D(self, sessionId):
  891. """
  892. 回复 充电桩主板的2D指令 否则会重复上报
  893. :param sessionId:
  894. :return:
  895. """
  896. result = MessageSender.send(
  897. device=self.device,
  898. cmd=DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE,
  899. payload={
  900. "funCode": "2D",
  901. "data": '55082D01' + sessionId,
  902. "IMEI": self.device.devNo
  903. }
  904. )
  905. def _response_to_27(self,obj,balance):
  906. data = obj.event_data
  907. cardNoHex = data["sourceData"][22: 30]
  908. port = data["sourceData"][8: 10]
  909. money = balance
  910. time = '0005'
  911. elec = '0000'
  912. session_id = data["sourceData"][38: 46]
  913. result = MessageSender.send(
  914. device=self.device,
  915. cmd=DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE,
  916. payload={
  917. "funCode": "27",
  918. "data": 'AA0F2701' + port + money + time + elec + session_id + '03',
  919. "IMEI": self.device.devNo
  920. }
  921. )
  922. def _check_package(self, package):
  923. """
  924. 获取设备启动的发送数据 根据设备的当前模式以及套餐获取
  925. :param package:
  926. :return:
  927. """
  928. # 按时间计费
  929. coins = float(package.get('coins', 0))
  930. if coins == 0:
  931. raise ServiceException({"result": 2, "description": u"经销商套餐配置错误, 金币不能为0"})
  932. packageTime = float(package.get("time", 0))
  933. if packageTime == 0:
  934. raise ServiceException({"result": 2, "description": u"经销商套餐配置错误, 时间参数或电量参数不能为0"})
  935. device_configs = self.get_device_configs()
  936. remoteMin = device_configs.get('remoteMin', 240)
  937. remoteElec = device_configs.get('remoteElec', 10)
  938. elec = remoteElec * 0.1 * coins
  939. unit = package.get("unit", u"分钟")
  940. if unit == u"小时":
  941. _time = int(packageTime * 60)
  942. elif unit == u"天":
  943. _time = int(packageTime * 24 * 60)
  944. elif unit == u"秒":
  945. _time = int(packageTime / 60)
  946. elif unit == u"分钟":
  947. _time = int(packageTime)
  948. elif unit == u'度':
  949. _time = int(remoteMin * package.get('coins'))
  950. elec = package.get('time')
  951. else:
  952. raise ServiceException({"result": 2, "description": u"经销商套餐单位配置错误,请联系经销商修改套餐设置"})
  953. return _time, elec
  954. def send_mqtt(self, data=None, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, otherData=None):
  955. """
  956. 发送mqtt 指令默认210 返回data
  957. """
  958. if 'cmd' not in data:
  959. data.update({'cmd': cmd})
  960. if 'IMEI' not in data:
  961. data.update({'IMEI': self.device.devNo})
  962. result = MessageSender.send(self.device, cmd,data)
  963. if "rst" in result and result["rst"] != 0:
  964. if result["rst"] == -1:
  965. raise ServiceException(
  966. {"result": 2, "description": u"该设备正在玩命找网络,请您稍候再试", "rst": -1})
  967. elif result["rst"] == 1:
  968. raise ServiceException(
  969. {"result": 2, "description": u"该设备忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能", "rst": 1})
  970. else:
  971. if cmd in [DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE]:
  972. return
  973. return result
  974. def get_account_rule(self, timeMax, elecMax):
  975. #秒
  976. timeMax = int(timeMax * 60)
  977. # 度 > 瓦
  978. elecMax = int(elecMax * 3600000)
  979. device_configs = self.get_device_configs()
  980. powerMax1 = device_configs.get('powerMax1', 200)
  981. power1Ti = device_configs.get('power1Ti', 100)
  982. powerMax2 = device_configs.get('powerMax2', 300)
  983. power2Ti = device_configs.get('power2Ti', 75)
  984. powerMax3 = device_configs.get('powerMax3', 400)
  985. power3Ti = device_configs.get('power3Ti', 50)
  986. powerMax4 = device_configs.get('powerMax4', 500)
  987. power4Ti = device_configs.get('power4Ti', 25)
  988. return {
  989. 'timeMax': timeMax,
  990. 'elecMax': elecMax,
  991. 'accountRule': {
  992. 'powerStep': [
  993. {'max': powerMax1,
  994. 'ratio': power1Ti},
  995. {'max': powerMax2,
  996. 'ratio': power2Ti},
  997. {'max': powerMax3,
  998. 'ratio': power3Ti},
  999. {'max': powerMax4,
  1000. 'ratio': power4Ti},
  1001. ]
  1002. }
  1003. }
  1004. def start_device_realiable(self, order):
  1005. if order.orderNo[:10] == self.device.ownerId[-10:]: # 此时为远程上分 先停一次
  1006. self.stop_charging_port(order.used_port)
  1007. attachParas = order.attachParas
  1008. package = order.package
  1009. if attachParas is None:
  1010. raise ServiceException({"result": 2, "description": u"请您选择合适的充电线路、电池类型信息"})
  1011. if not attachParas.has_key("chargeIndex"):
  1012. raise ServiceException({"result": 2, "description": u"请您选择合适的充电线路"})
  1013. port = int(attachParas["chargeIndex"])
  1014. coins = package.get("coins", 0)
  1015. _time, elec = self._check_package(package)
  1016. rule = self.get_account_rule(_time, elec)
  1017. data = {
  1018. 'order_type': 'com_start',
  1019. 'funCode': '27',
  1020. 'port': port,
  1021. 'result': Cmd.SUCCESS_00,
  1022. 'balance': int(coins * 10.0),
  1023. 'order_id': order.orderNo,
  1024. 'packageTime': _time,
  1025. }
  1026. data.update(rule)
  1027. result = MessageSender.send(device = self.device,
  1028. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  1029. payload = data,
  1030. timeout = 120)
  1031. try:
  1032. order.servicedInfo.update(startData=data)
  1033. order.save()
  1034. except:
  1035. pass
  1036. return result
  1037. @property
  1038. @SmartBox.check_device_features(device_features=["Postpaid"],no_features_to_return=False)
  1039. def is_postpaid(self):
  1040. return True
  1041. @SmartBox.check_device_features(device_features = ["Postpaid"])
  1042. def check_order_state(self, openId):
  1043. """
  1044. 通过 openId 以及设备来鉴别 订单
  1045. :param openId:
  1046. :return:
  1047. """
  1048. dealerId = self.device.ownerId
  1049. devTypeCode = self.device.devType.get("code")
  1050. return ConsumeRecord.objects.filter(ownerId = dealerId,
  1051. openId = openId,
  1052. devTypeCode = devTypeCode,
  1053. status__ne = "finished",
  1054. isNormal = True,
  1055. attachParas__isPostpaid = True).first()
  1056. @SmartBox.check_device_features(device_features=["Postpaid"])
  1057. def _postpaidProcessing(self, result):
  1058. "leftTime" in result and result.pop("leftTime")
  1059. if "power" in result: # 刚启动power为0,serverProcess校验会直接结束掉,需要等设备计量的数据正常上报上来,此处改为字符串"0"
  1060. if result["power"] == 0:
  1061. result["power"] = "0"
  1062. @SmartBox.check_device_features(device_features=["Postpaid"])
  1063. def set_postpaid_price_rules(self, configs):
  1064. postpaidConfigs = {}
  1065. postpaidConfigs["postpaidBaseTime"] = configs.get("postpaidBaseTime", 30)
  1066. postpaidConfigs["postpaidMaxTime"] = configs.get("postpaidMaxTime", 720)
  1067. postpaidConfigs["postpaidPower1"] = configs.get("postpaidPower1", 150)
  1068. postpaidConfigs["postpaidPower2"] = configs.get("postpaidPower2", 250)
  1069. postpaidConfigs["postpaidPower3"] = configs.get("postpaidPower3", 350)
  1070. postpaidConfigs["postpaidPower4"] = configs.get("postpaidPower4", 500)
  1071. postpaidConfigs["postpaidPowerPrice1"] = configs.get("postpaidPowerPrice1", 0.28)
  1072. postpaidConfigs["postpaidPowerPrice2"] = configs.get("postpaidPowerPrice2", 0.35)
  1073. postpaidConfigs["postpaidPowerPrice3"] = configs.get("postpaidPowerPrice3", 0.48)
  1074. postpaidConfigs["postpaidPowerPrice4"] = configs.get("postpaidPowerPrice4", 0.66)
  1075. dev = Device.objects.get(devNo=self.device.devNo)
  1076. dev.otherConf.update({"postpaidConfigs":postpaidConfigs})
  1077. dev.save()
  1078. Device.invalid_device_cache(self.device.devNo)
  1079. @SmartBox.check_device_features(device_features=["Postpaid"],no_features_to_return={})
  1080. def get_postpaid_price_rules(self):
  1081. otherConf = self.device["otherConf"]
  1082. postpaidConfigs = otherConf.get("postpaidConfigs", {})
  1083. if not postpaidConfigs:
  1084. self.set_postpaid_price_rules({})
  1085. dev = Device.objects.get(devNo=self.device.devNo)
  1086. otherConf = dev.otherConf
  1087. postpaidConfigs = otherConf.get("postpaidConfigs", {})
  1088. return postpaidConfigs
  1089. def stop_by_order(self, port, orderNo):
  1090. order = ConsumeRecord.objects.filter(orderNo=orderNo, isNormal=True, status__ne="finished").first()
  1091. if order:
  1092. try:
  1093. self.stop_charging_port(port)
  1094. except Exception:
  1095. pass
  1096. self.deal_order_finish(order)
  1097. def do_postpaid_finished_event(self, order):
  1098. now = datetime.datetime.now()
  1099. start_time = order.startTime
  1100. duration = int((now-start_time).total_seconds())
  1101. device_event = {
  1102. "order_type": "com_start",
  1103. "leftElec": 0,
  1104. "duration": duration,
  1105. "reason": "90",
  1106. "order_id": order.orderNo,
  1107. "leftTime": 0,
  1108. "sts": Arrow.fromdatetime(order.startTime, tzinfo=settings.TIME_ZONE).timestamp,
  1109. "fts": int(time.time()),
  1110. "elec": 0,
  1111. "status": "finished",
  1112. "port": order.used_port,
  1113. }
  1114. eventer = ActionDeviceBuilder.create_eventer(self.device)
  1115. event = eventer.getEvent(device_event)
  1116. event.do()
  1117. def deal_order_finish(self, order):
  1118. if self.is_postpaid: # 后付费订单 后付费数据处理过 没有leftTime
  1119. self.do_postpaid_finished_event(order)
  1120. else:
  1121. self.do_finished_event(order)
  1122. def do_finished_event(self, order):
  1123. pass
  1124. def response_card(self, res, balance, fee, oper, cardNoHex, isVir=False, virtual_card_id=None):
  1125. if isVir:
  1126. res = int(res)
  1127. count = int(balance) * 10
  1128. fee = int(fee) * 10
  1129. oper = int(oper)
  1130. MessageSender.send(
  1131. device=self.device,
  1132. cmd=220,
  1133. payload={
  1134. "res": res,
  1135. "balance": count,
  1136. "card_cst": fee,
  1137. "card_ope": oper,
  1138. "card_id": cardNoHex,
  1139. "funCode": "10",
  1140. "isVir": isVir,
  1141. "virtual_card_id": virtual_card_id
  1142. }
  1143. )
  1144. else:
  1145. res = int(res)
  1146. balance = int(balance) * 10
  1147. fee = int(fee) * 10
  1148. oper = int(oper)
  1149. MessageSender.send(
  1150. device=self.device,
  1151. cmd=220,
  1152. payload={
  1153. "res": res,
  1154. "balance": balance,
  1155. "card_cst": fee,
  1156. "card_ope": oper,
  1157. "card_id": cardNoHex,
  1158. "funCode": "10",
  1159. "isVir": isVir
  1160. }
  1161. )
  1162. def get_device_configs(self):
  1163. dev = Device.get_dev(self.device.devNo)
  1164. deviceConfigs = dev.get("otherConf", {}).get("deviceConfigs", {})
  1165. return deviceConfigs
  1166. def do_update_configs(self, updateDict):
  1167. dev = Device.objects.get(devNo=self.device.devNo)
  1168. deviceConfigs = dev.otherConf.get("deviceConfigs", {})
  1169. deviceConfigs.update(updateDict)
  1170. dev.otherConf['deviceConfigs'] = deviceConfigs
  1171. dev.save()
  1172. Device.invalid_device_cache(self.device.devNo)
  1173. def check_dev_status(self, attachParas = None):
  1174. port = attachParas.get('chargeIndex')
  1175. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  1176. lineInfo = ctrInfo.get(port)
  1177. if lineInfo.get('status') == Const.DEV_WORK_STATUS_WORKING:
  1178. raise ServiceException({"result": 2, "description": "不支持续充"})
  1179. elif lineInfo.get('status') == Const.DEV_WORK_STATUS_FAULT:
  1180. raise ServiceException({"result": 2, "description": "端口故障"})
  1181. elif lineInfo.get('status') == Const.DEV_WORK_STATUS_FORBIDDEN:
  1182. raise ServiceException({"result": 2, "description": "该端口已被禁用"})
  1183. class IdCardStartAckEvent(AckEvent):
  1184. pass