jndz.py 80 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import logging
  5. import random
  6. import time
  7. from typing import Optional, Dict, TYPE_CHECKING
  8. from apilib.monetary import RMB
  9. from apilib.utils_datetime import timestamp_to_dt
  10. from apps.web.agent.models import Agent
  11. from apps.web.constant import DeviceCmdCode, Const, MQTT_TIMEOUT, DeviceErrorCodeDesc, ErrorCode
  12. from apps.web.core.adapter.base import SmartBox, fill_2_hexByte, start_error_timer
  13. from apps.web.core.device_define.jndz import CMD_CODE
  14. from apps.web.core.exceptions import ServiceException, InvalidParameter
  15. from apps.web.core.networking import MessageSender
  16. from apps.web.dealer.models import Dealer
  17. from apps.web.device.models import Device, Group, DeviceType, GroupDict
  18. from apps.web.eventer.base import AckEvent
  19. from apps.web.user.models import ConsumeRecord
  20. from taskmanager.mediator import task_caller
  21. from apps.web.common.models import ExceptionLog
  22. if TYPE_CHECKING:
  23. from apps.web.api.models import APIStartDeviceRecord
  24. logger = logging.getLogger(__name__)
  25. class ChargingJNDZBox(SmartBox):
  26. """
  27. 久恒协议和劲能一致,业务修改和功能扩充需要确定是否两边都修改
  28. """
  29. def __init__(self, device):
  30. super(ChargingJNDZBox, self).__init__(device)
  31. def async_update_portinfo_from_dev(self):
  32. """
  33. 避免频繁刷新状态,并且是并发的。状态如果不对,用户可以强制刷新
  34. :return:
  35. """
  36. pass
  37. @property
  38. def isHaveStopEvent(self):
  39. return True
  40. def translate_funcode(self, funCode):
  41. funCodeDict = {
  42. '01': u'获取端口数量',
  43. '02': u'获取端口数据',
  44. '14': u'移动支付',
  45. '07': u'获取刷卡投币统计数据',
  46. '15': u'获取设备端口详情',
  47. '0F': u'获取端口状态',
  48. '0C': u'端口锁操作',
  49. '0D': u'端口开关',
  50. '1E': u'获取设备设置',
  51. '18': u'设置设备参数',
  52. '10': u'回复卡余额',
  53. }
  54. return funCodeDict.get(funCode, '')
  55. def translate_event_cmdcode(self, cmdCode):
  56. cmdDict = {
  57. '06': u'充电结束',
  58. '16': u'充电结束',
  59. '0A': u'故障',
  60. '10': u'刷卡上报',
  61. '20': u'启动设备',
  62. }
  63. return cmdDict.get(cmdCode, '')
  64. def check_dev_status(self, attachParas=None):
  65. """
  66. 如果超过两个心跳周期没有报心跳,并且最后一次更新时间在2个小时内,需要从设备获取状态
  67. 否则以缓存状态为准。
  68. :param attachParas:
  69. :return:
  70. """
  71. if attachParas is None:
  72. raise ServiceException({'result': 0, 'description': u'请您选择合适的充电端口、电池类型信息'})
  73. if 'chargeIndex' not in attachParas:
  74. raise ServiceException({'result': 0, 'description': u'请您选择合适的充电端口'})
  75. if not self.device.need_fetch_online:
  76. raise ServiceException(
  77. {'result': 2, 'description': DeviceErrorCodeDesc.get(ErrorCode.DEVICE_CONN_CHECK_FAIL)})
  78. group = self.device.group # type: GroupDict
  79. if group.is_free:
  80. logger.debug('{} is free. no need to check continue pay.'.format(repr(self.device)))
  81. return
  82. port = str(attachParas['chargeIndex'])
  83. rv = self.get_port_info(port)
  84. # isCanAdd = self.device['devType'].get('payableWhileBusy', False)
  85. if rv['status'] == Const.DEV_WORK_STATUS_IDLE:
  86. return
  87. elif rv['status'] == Const.DEV_WORK_STATUS_FAULT:
  88. raise ServiceException({'result': 0, 'description': u'该端口故障,暂时不能使用'})
  89. elif rv['status'] == Const.DEV_WORK_STATUS_WORKING:
  90. # if isCanAdd:
  91. # return
  92. # raise ServiceException({'result': 0, 'description': u'该端口正在工作不能使用,请您使用其他端口'})
  93. return
  94. def is_port_can_use(self, port, canAdd = False):
  95. return True, ''
  96. def test(self, coins,port=1):
  97. hexElec = fill_2_hexByte(hex(0), 4)
  98. hexPort = fill_2_hexByte(hex(port), 2)
  99. hexCoins = fill_2_hexByte(hex(1))
  100. hexTime = fill_2_hexByte(hex(60))
  101. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  102. {'IMEI': self._device['devNo'], "funCode": '14',
  103. 'data': hexPort + hexCoins + hexTime + hexElec})
  104. return devInfo
  105. def stop(self, port = None):
  106. infoDict = self.stop_charging_port(port)
  107. infoDict['remainder_time'] = infoDict['leftTime']
  108. return infoDict
  109. def check_serial_port_for_startcmd(self, port):
  110. data = fill_2_hexByte(hex(int(port)), 2)
  111. devInfo = MessageSender.send(self.device, self.make_random_cmdcode(),
  112. {'IMEI': self._device['devNo'], "funCode": '15', 'data': data})
  113. if devInfo['rst'] != 0:
  114. raise ServiceException(
  115. {'result': 2, 'description': u'设备忙无响应。本次操作没有扣除您的金额,您可以稍后重试或者试试附近其他设备。'})
  116. data = devInfo['data'][6::]
  117. ExceptionLog.log(user = self.device.devNo, exception = '',
  118. extra = {
  119. 'action': 'check_serial_port_for_startcmd:get_port_status_from_dev',
  120. 'data': data
  121. })
  122. if data[0:2] == '01': # 表示成功
  123. if data[2:4] == '00':
  124. raise ServiceException({
  125. 'result': 2, 'description': u'设备忙无响应。本次操作没有扣除您的金额,您可以稍后重试或者试试附近其他设备。'})
  126. else:
  127. left_time = int(data[4:8], 16)
  128. if left_time <= 0 or left_time == 65535:
  129. raise ServiceException({
  130. 'result': 2, 'description': u'设备忙无响应。本次操作没有扣除您的金额,您可以稍后重试或者试试附近其他设备。'})
  131. else:
  132. raise ServiceException({
  133. 'result': 2, 'description': u'设备忙无响应。本次操作没有扣除您的金额,您可以稍后重试或者试试附近其他设备。'})
  134. @start_error_timer(missMessages = [u"请您选择合适的充电线路、电池类型信息", u"请您选择合适的充电线路", u"该端口正在使用中"])
  135. def start_device(self, package, openId, attachParas):
  136. if attachParas is None:
  137. raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路、电池类型信息'})
  138. if 'chargeIndex' not in attachParas:
  139. raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路'})
  140. coinElec = self.device.get_other_conf_item('coinElec')
  141. if coinElec is None:
  142. conf = self.get_dev_setting_1E()
  143. coinElec = int(conf.get('coinElec', 10))
  144. else:
  145. coinElec = int(coinElec)
  146. price = float(package['price'])
  147. port = hex(int(attachParas['chargeIndex']))
  148. hexPort = fill_2_hexByte(port, 2)
  149. coins = float(package['coins'])
  150. hexCoins = fill_2_hexByte(hex(int(coins * 10))) # 注意单位是角
  151. needElec = round((coins * coinElec) / 10.0, 2) # 单位是0.01度电(配置中每次投币的最大用电量单位却是0.1度电,这里要乘以10)
  152. hexElec = fill_2_hexByte(hex(int(needElec * 100)), 4)
  153. unit = package.get('unit', u'分钟')
  154. needTime = int(package['time'])
  155. if unit in [u'分钟', u'小时', u'天']:
  156. billingType = 'time'
  157. if unit == u'小时':
  158. needTime = int(package['time']) * 60
  159. elif unit == u'天':
  160. needTime = int(package['time']) * 1440
  161. hexTime = fill_2_hexByte(hex(needTime))
  162. elif unit == u'度':
  163. billingType = 'elec'
  164. needElec = round((package['time']), 2)
  165. hexElec = fill_2_hexByte(hex(int(needElec * 100)), 4)
  166. hexTime = fill_2_hexByte(hex(Device.get_elec_static_time(self._device['devNo'])))
  167. else:
  168. billingType = 'elec'
  169. devObj = Device.objects.get(devNo=self.device.devNo)
  170. if 'isTempPackage' in attachParas:
  171. elecFee = float(devObj.otherConf.get('tempElecPrice', 0.9))
  172. else:
  173. elecFee = float(devObj.otherConf.get('elecFee', 0.9))
  174. needElec = round(min(float(coins / elecFee), needElec), 2)
  175. hexElec = fill_2_hexByte(hex(int(needElec * 100)), 4)
  176. hexTime = fill_2_hexByte(hex(Device.get_elec_static_time(self._device['devNo'])))
  177. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {
  178. 'IMEI': self._device['devNo'],
  179. "funCode": '14',
  180. 'data': hexPort + hexCoins + hexTime + hexElec
  181. }, timeout=MQTT_TIMEOUT.START_DEVICE)
  182. usePort = int(attachParas['chargeIndex'])
  183. if devInfo['rst'] != ErrorCode.SUCCESS:
  184. if devInfo['rst'] == ErrorCode.DEVICE_CONN_FAIL:
  185. raise ServiceException(
  186. {
  187. 'result': 2,
  188. 'description': u'当前设备信号弱无响应。本次操作没有扣除您的金额,您可以稍后重试或者试试附近其他设备。'
  189. }
  190. )
  191. elif devInfo['rst'] == ErrorCode.BOARD_UART_TIMEOUT:
  192. if devInfo.get('checked', 0) == 0:
  193. self.check_serial_port_for_startcmd(attachParas['chargeIndex'])
  194. else:
  195. logger.debug('{} checked value is: {}'.format(self.device, devInfo.get('checked')))
  196. raise ServiceException(
  197. {'result': 2, 'description': u'设备忙无响应。本次操作没有扣除您的金额,您可以稍后重试或者试试附近其他设备。'})
  198. elif devInfo.get('checked', 0) == 0:
  199. logger.debug('{} apply start success.'.format(self.device))
  200. data = devInfo['data'][6::]
  201. if data[0:2] == '01': # 表示成功
  202. pass
  203. else:
  204. raise ServiceException({'result': 2, 'description': u'获取端口数据失败,请重试看能否解决'})
  205. result = data[4:6]
  206. if result == '01': # 成功
  207. pass
  208. elif result == '0B':
  209. newValue = {str(usePort): {'status': Const.DEV_WORK_STATUS_FAULT, 'statusInfo': u'充电站故障'}}
  210. Device.update_dev_control_cache(self._device['devNo'], newValue)
  211. raise ServiceException({'result': 2, 'description': u'充电站故障'})
  212. elif result == '0C':
  213. newValue = {str(usePort): {'status': Const.DEV_WORK_STATUS_WORKING, 'statusInfo': u''}}
  214. Device.update_dev_control_cache(self._device['devNo'], newValue)
  215. raise ServiceException({'result': 2, 'description': u'该端口正在使用中'})
  216. else:
  217. logger.warning('{} check 15 result is: {}.'.format(self.device, devInfo.get('checked')))
  218. start_timestamp = int(time.time())
  219. portDict = {
  220. 'startTime': timestamp_to_dt(start_timestamp).strftime('%Y-%m-%d %H:%M:%S'),
  221. 'status': Const.DEV_WORK_STATUS_WORKING,
  222. 'coins': coins,
  223. 'isStart': True,
  224. 'price': price,
  225. 'openId': openId,
  226. 'refunded': False,
  227. 'needElec': needElec,
  228. 'billingType': billingType,
  229. 'vCardId': self._vcard_id,
  230. 'consumeType': 'server'
  231. }
  232. lastPortInfo = Device.get_port_control_cache(self.device.devNo, str(usePort))
  233. if lastPortInfo and \
  234. lastPortInfo.get('status', '') == Const.DEV_WORK_STATUS_WORKING and \
  235. lastPortInfo.get('openId') == openId and \
  236. lastPortInfo.get('billingType') == billingType and \
  237. ('consumeType' not in lastPortInfo or (lastPortInfo['consumeType'] == 'server')):
  238. is_continus = True
  239. else:
  240. is_continus = False
  241. if is_continus:
  242. if 'coins' in lastPortInfo:
  243. portDict['coins'] = coins + lastPortInfo['coins']
  244. if 'price' in lastPortInfo:
  245. portDict['price'] = price + lastPortInfo['price']
  246. if 'startTime' in lastPortInfo:
  247. portDict['startTime'] = lastPortInfo['startTime']
  248. if billingType == 'time':
  249. portDict.update({'needTime': needTime, 'needElec': needElec})
  250. if is_continus:
  251. if lastPortInfo.has_key('needTime'):
  252. portDict['needTime'] = needTime + lastPortInfo['needTime']
  253. if lastPortInfo.has_key('needElec'):
  254. portDict['needElec'] = needElec + lastPortInfo['needElec']
  255. finishedTime = int(time.time()) + portDict['needTime'] * 60
  256. else:
  257. portDict.update({'needElec': needElec})
  258. if is_continus:
  259. lastPortInfo['needElec'] = needElec + lastPortInfo['needElec']
  260. finishedTime = int(time.time()) + 12 * 60 * 60
  261. if attachParas.has_key('orderNo'):
  262. portDict.update({'orderNo': attachParas['orderNo']})
  263. if attachParas.get('redpackId'):
  264. if is_continus:
  265. redpackInfo = lastPortInfo.get("redpackInfo", list())
  266. if not redpackInfo:
  267. logger.warning("miss redpackInfo! {}-{}".format(self._device["devNo"], usePort))
  268. else:
  269. redpackInfo = list()
  270. redpackInfo.append({'redpackId': str(attachParas['redpackId'])})
  271. portDict['redpackInfo'] = redpackInfo
  272. else:
  273. if is_continus:
  274. portDict['redpackInfo'] = lastPortInfo.get("redpackInfo", list())
  275. else:
  276. portDict['redpackInfo'] = list()
  277. if 'linkedRechargeRecordId' in attachParas and attachParas.get('isQuickPay', False):
  278. item = {
  279. 'rechargeRcdId': str(attachParas['linkedRechargeRecordId'])
  280. }
  281. if is_continus:
  282. pay_info = lastPortInfo.get('payInfo', list())
  283. else:
  284. pay_info = list()
  285. pay_info.append(item)
  286. portDict['payInfo'] = pay_info
  287. if 'extOrderNo' in attachParas:
  288. portDict['extOrderNo'] = attachParas.get('extOrderNo')
  289. if 'isTempPackage' in attachParas:
  290. portDict['isTempPackage'] = attachParas['isTempPackage']
  291. devInfo['finishedTime'] = finishedTime
  292. portDict.update({'finishedTime': finishedTime})
  293. Device.update_dev_control_cache(self.device.devNo, {str(usePort): portDict})
  294. if self.device.owner.supports("supportBeiJingFengTai"):
  295. from apps.web.south_intf.bj_north.api import push_port_info,post_charging_meta_info
  296. push_port_info(self.device.devNo, attachParas['chargeIndex'])
  297. post_charging_meta_info(self.device.devNo, attachParas['chargeIndex'])
  298. return devInfo
  299. def start_from_api(self, record):
  300. # type: (APIStartDeviceRecord)->Optional[Dict]
  301. if 'chargeIndex' not in record.attachParas:
  302. from apps.web.api.exceptions import ApiParameterError
  303. raise ApiParameterError(u'请传入充电桩端口号chargeIndex参数')
  304. return super(ChargingJNDZBox, self).start_from_api(record)
  305. def analyze_event_data(self, device_event):
  306. # 升级兼容. 避免重启过程中老的消息
  307. if 'data' in device_event:
  308. data = device_event['data']
  309. else:
  310. data = device_event
  311. cmdCode = data[4:6]
  312. #: 提交充电结束状态 (06)
  313. #: 老版本
  314. if cmdCode == CMD_CODE.DEVICE_SUBMIT_CHARGE_FINISHED_06:
  315. port = int(data[8:10], 16)
  316. leftTime = int(data[10:14], 16)
  317. reasonCode = data[14:16]
  318. orderPower = device_event.get("orderPower")
  319. desc_map = {
  320. "00": u'购买的充电时间或电量用完了。',
  321. "01": u'可能是插头被拔掉,或者电瓶已经充满。系统判断为异常断电,由于电瓶车充电器种类繁多,可能存在误差。如有问题,请您及时联系商家协助解决问题并恢复充电。',
  322. "02": u'恭喜您!电池已经充满电!',
  323. "0B": u'设备或端口出现问题,为了安全起见,被迫停止工作。建议您根据已经充电的时间评估是否需要到现场换到其他端口充电。'
  324. }
  325. return {
  326. 'status': Const.DEV_WORK_STATUS_IDLE,
  327. 'cmdCode': cmdCode,
  328. 'port': port,
  329. 'leftTime': leftTime,
  330. 'reason': desc_map[reasonCode],
  331. 'reasonCode': reasonCode,
  332. 'uartData': data,
  333. 'orderPower': orderPower
  334. }
  335. #: (高版本的) 提交充电结束状态 (16)
  336. elif cmdCode == CMD_CODE.DEVICE_SUBMIT_CHARGE_FINISHED_v2_16:
  337. port = int(data[8:10], 16)
  338. leftTime = int(data[10:14], 16)
  339. elec = int(data[14:18], 16) / 100.0
  340. reasonCode = data[18:20]
  341. orderPower = device_event.get("orderPower")
  342. # 如果设备类型有自定义结束推送通知就走自定义
  343. devType = DeviceType.objects(id=self.device['devType']['id']).first()
  344. finishedReasonDict = devType.finishedReasonDict
  345. if finishedReasonDict != {}:
  346. desc_map = finishedReasonDict
  347. else:
  348. desc_map = {
  349. '00': u'购买的充电时间或电量用完了。',
  350. '01': u'可能是插头被拔掉,或者电瓶已经充满。',
  351. '02': u'恭喜您!电池已经充满电!',
  352. '03': u'警告!您的充电功率超过充电桩的最大使用功率,已经停止充电!!',
  353. '04': u'远程断电。',
  354. '0B': u'设备或端口出现问题,被迫停止工作。建议您根据已经充电的时间评估是否需要到现场换到其他端口充电。'
  355. }
  356. desc = desc_map.get(reasonCode, u'电池没有充满!原因未知。')
  357. return {
  358. 'status': Const.DEV_WORK_STATUS_IDLE,
  359. 'cmdCode': cmdCode,
  360. 'port': port,
  361. 'leftTime': leftTime,
  362. 'elec': elec,
  363. 'reason': desc,
  364. 'reasonCode': reasonCode,
  365. 'uartData': data,
  366. 'orderPower': orderPower
  367. }
  368. # todo 0A的告警不要了,先注释掉,后续删除
  369. #: 上传设备故障
  370. # elif cmdCode == CMD_CODE.DEVICE_SUBMIT_DEVICE_FAULT_0A:
  371. # port = int(data[8:10], 16)
  372. # errCode = data[10:12]
  373. #
  374. # FaultDict = {
  375. # 'A0': ['充电异常,继电器粘连,短路', FAULT_LEVEL.FATAL],
  376. # 'A1': ['高温异常', FAULT_LEVEL.CRITICAL],
  377. # '21': ['高温状态恢复', FAULT_LEVEL.NORMAL],
  378. # 'A2': ['低温异常', FAULT_LEVEL.NORMAL],
  379. # '22': ['低温状态恢复', FAULT_LEVEL.NORMAL],
  380. # 'A3': ['空载,充电头脱落、充电器拔出', FAULT_LEVEL.NORMAL],
  381. # '23': ['负载恢复正常', FAULT_LEVEL.NORMAL],
  382. # 'A4': ['设备消防(烟感)异常', FAULT_LEVEL.CRITICAL],
  383. # '24': ['设备消防(烟感)恢复正常', FAULT_LEVEL.CRITICAL],
  384. # 'A5': ['总功率过载' if port == 255 else '{}号端口过载'.format(port), FAULT_LEVEL.FATAL],
  385. # '25': ['总功率过载恢复正常' if port == 255 else '{}号端口过载恢复正常'.format(port), FAULT_LEVEL.NORMAL],
  386. # 'A6': ['设备倾斜异常', FAULT_LEVEL.FATAL],
  387. # '26': ['设备倾斜恢复正常', FAULT_LEVEL.NORMAL],
  388. # 'A7': ['水压高异常', FAULT_LEVEL.NORMAL],
  389. # '27': ['水压(从高)恢复正常', FAULT_LEVEL.NORMAL],
  390. # 'A8': ['水压低异常', FAULT_LEVEL.NORMAL],
  391. # '28': ['水压(从低)恢复正常', FAULT_LEVEL.NORMAL],
  392. # 'A9': ['设备过压异常', FAULT_LEVEL.NORMAL],
  393. # '29': ['设备过压恢复正常', FAULT_LEVEL.NORMAL],
  394. # 'AA': ['设备欠压异常', FAULT_LEVEL.NORMAL],
  395. # '2A': ['设备欠压恢复正常', FAULT_LEVEL.NORMAL],
  396. #
  397. # '00': ['继电器粘黏(端口: {})'.format(port), FAULT_LEVEL.NORMAL], # 久恒老设备 100206
  398. # }
  399. #
  400. # return {
  401. # 'status': Const.DEV_WORK_STATUS_FAULT,
  402. # 'statusInfo': FaultDict.get(errCode, ['设备故障', FAULT_LEVEL.NORMAL])[0],
  403. # 'cmdCode': cmdCode,
  404. # 'port': port,
  405. # 'errCode': errCode,
  406. # 'faultContent': FaultDict.get(errCode, [errCode, FAULT_LEVEL.NORMAL])[0],
  407. # 'level': FaultDict.get(errCode, [errCode, FAULT_LEVEL.NORMAL])[1]
  408. # }
  409. # 用户刷卡上报的信息
  410. #: 在线卡上传卡号,预扣费
  411. #: 示例:
  412. #: CARD_ID CARD_CST CARD_OPE
  413. #: 0x00000000 0x00 0x00
  414. #: 参数
  415. #: CARD_ID : IC 卡卡号
  416. #: CARD_SURP: 卡片需要改变的金额信息(以角为单位)
  417. #: CARD_OPE: 0x00 是扣费(减少),0x01 是充值(增加)。
  418. #: 回复
  419. #: RES: 0x00,表示扣费成功,0x01 表示余额不足,0x02 表示非法卡。
  420. #: CARD_SURP: 表示卡余额(以角为单位)。
  421. elif cmdCode == CMD_CODE.SWIPE_CARD_10:
  422. # 过滤掉长度不够的10命令
  423. if data[2:4] == '04':
  424. return None
  425. group = Group.get_group(self._device['groupId'])
  426. dealer = Dealer.get_dealer(group['ownerId'])
  427. agent = Agent.objects(id = dealer['agentId']).first()
  428. device = Device.objects(devNo=self._device['devNo']).first()
  429. devType = DeviceType.objects(id=device['devType']['id']).first()
  430. cardNo = str(int(data[8:16], 16))
  431. # 兼容以前的老代码, 先走以前的老代码, 如果有新代码再走新的.
  432. if agent is not None and 'cardNoReverse' in agent.features:
  433. cardData = data[14:16] + data[12:14] + data[10:12] + data[8:10]
  434. cardNo = str(int(cardData, 16))
  435. if 'cardNoReverse' in devType.features:
  436. if devType.features['cardNoReverse'] is True:
  437. cardData = data[14:16] + data[12:14] + data[10:12] + data[8:10]
  438. cardNo = str(int(cardData, 16))
  439. else:
  440. cardNo = str(int(data[8:16], 16))
  441. else:
  442. pass
  443. #: 卡里的余额,以角为单位
  444. preFee = int(data[16:18], 16) / 10.0
  445. #: 操作符 00(增加) | 01(减少)
  446. oper = data[18:20]
  447. return {'cardNo': cardNo, 'preFee': preFee, 'cmdCode': cmdCode, 'oper': oper, "sourceData": data}
  448. elif cmdCode == CMD_CODE.SWIPE_CARD_50:
  449. data = data[8:-2]
  450. cardNo = str(int(data, 16))
  451. return {
  452. 'cardNo' : cardNo,
  453. 'command' : 'query',
  454. 'cmdCode': cmdCode
  455. }
  456. elif cmdCode == CMD_CODE.SWIPE_CARD_52:
  457. data = data[8:-2]
  458. port = str(int(data[:2], 16))
  459. coins = str(int(data[2:6], 16) * 0.1)
  460. needtime = str(int(data[6:10], 16))
  461. cardNo = str(int(data[10:18], 16))
  462. deductType = str(int(data[18:20], 16))
  463. ackId = device_event['ack_id']
  464. return {'port': port, 'coins': coins, 'needTime': needtime, 'cardNo': cardNo,
  465. 'deductType': deductType, 'cmdCode': cmdCode, 'command': 'strat','ack_id':ackId}
  466. elif cmdCode == CMD_CODE.SWIPE_CARD_56:
  467. data = data[8:-2]
  468. port = str(int(data[:2], 16))
  469. usedTime = str(int(data[2:6], 16))
  470. usedElec = str(int(data[6:10], 16))
  471. reasonCode = str(int(data[10:12], 16))
  472. refundMoney = str(int(data[12:16], 16))
  473. refundTime = str(int(data[16:20], 16))
  474. refundType = str(int(data[20:22], 16))
  475. desc_map = {
  476. '00': u'购买的充电时间或电量用完了。',
  477. '01': u'可能是插头被拔掉,或者电瓶已经充满。',
  478. '02': u'恭喜您!电池已经充满电!',
  479. '03': u'警告!您的充电功率超过充电桩的最大使用功率,已经停止充电!!',
  480. '04': u'远程断电。',
  481. '0B': u'设备或端口出现问题,被迫停止工作。建议您根据已经充电的时间评估是否需要到现场换到其他端口充电。'
  482. }
  483. desc = desc_map.get(reasonCode, u'电池没有充满!原因未知。')
  484. ackId = device_event['ack_id']
  485. return {'port': port, 'usedTime': usedTime, 'usedElec': usedElec, 'reason': desc, 'reasonCode': reasonCode,
  486. 'refundMoney': refundMoney, 'refundTime': refundTime, 'refundType': refundType, 'cmdCode': cmdCode,
  487. 'command': 'finished','ack_id':ackId}
  488. #: 上报投币打开的信息
  489. elif cmdCode == CMD_CODE.DEVICE_SUBMIT_OFFLINE_COINS_20: # 启动设备
  490. port = int(data[8:10], 16)
  491. needTime = int(data[10:14], 16)
  492. elec = int(data[14:18], 16) / 100.0
  493. consumeTypeTemp = data[18:20]
  494. if consumeTypeTemp == '00':
  495. consumeType = 'coin'
  496. elif consumeTypeTemp == '01':
  497. consumeType = 'card'
  498. elif consumeTypeTemp == '03':
  499. consumeType = 'server'
  500. else:
  501. consumeType = 'other'
  502. money = int(data[20:22], 16) / 10.0
  503. rv = {
  504. 'cmdCode': cmdCode,
  505. 'port': port,
  506. 'needTime': needTime,
  507. 'elec': elec,
  508. 'consumeType': consumeType,
  509. 'coins': money,
  510. 'uartData': data
  511. }
  512. if 'today_coins' in device_event and 'ts' in device_event:
  513. rv.update({
  514. 'today_coins': device_event['today_coins'],
  515. 'ts': device_event['ts']
  516. })
  517. return rv
  518. # 4个故障的告警
  519. elif cmdCode == CMD_CODE.DEVICE_FAULT_FIRE:
  520. return {"cmdCode": cmdCode, "fault": u"火灾报警"}
  521. elif cmdCode == CMD_CODE.DEVICE_FAULT_SMOKE:
  522. return {"cmdCode": cmdCode, "fault": "烟雾报警"}
  523. elif cmdCode == CMD_CODE.DEVICE_FAULT_TEMPERATURE:
  524. return {"cmdCode": cmdCode, "fault": "温度超限告警"}
  525. elif cmdCode == CMD_CODE.DEVICE_FAULT_POWER:
  526. return {"cmdCode": cmdCode, "fault": "功率超线告警"}
  527. elif cmdCode == CMD_CODE.DEVICE_FAULT_ALTER:
  528. def getBin8Str(binData):
  529. bin8Str = str(bin(binData))[2:]
  530. while len(bin8Str) < 8:
  531. bin8Str = '0' + bin8Str
  532. return bin8Str
  533. fire = int(data[8:10],16)
  534. smoke = int(data[10:12],16)
  535. overload = int(data[12:14],16)
  536. overheat = int(data[14:16],16)
  537. relayAdhesion = data[16:24]
  538. relayAdhesionDetail = (getBin8Str(int(relayAdhesion[0:2], 16)) + getBin8Str(int(relayAdhesion[2:4], 16)) + getBin8Str(int(relayAdhesion[4:6], 16)) + getBin8Str(int(relayAdhesion[6:8], 16)))[::-1]
  539. portRelayAdhesion = {}
  540. for _ in range(0, len(relayAdhesionDetail)):
  541. if int(relayAdhesionDetail[_]):
  542. portRelayAdhesion.update({str(_ + 1): int(relayAdhesionDetail[_])})
  543. result = {"cmdCode": cmdCode, "fire": bool(fire), "smoke": bool(smoke), "overload": bool(overload), "overheat":bool(overheat), "portRelayAdhesion": portRelayAdhesion}
  544. dealer = Dealer.objects(id=self.device['ownerId']).first()
  545. if dealer is None:
  546. return {}
  547. if 'supportAdhesionAlert' not in dealer.features:
  548. result.pop('portRelayAdhesion')
  549. fault = []
  550. desc = []
  551. if result.get('fire', False):
  552. fault.append("火灾报警")
  553. result["errorCode"] = "B202"
  554. if result.get('smoke', False):
  555. fault.append("烟雾报警")
  556. result["errorCode"] = "B202"
  557. if result.get('overload', False):
  558. fault.append("功率超限告警")
  559. result["errorCode"] = "B203"
  560. if result.get('overheat', False):
  561. fault.append("温度超限告警")
  562. result["errorCode"] = "B201"
  563. if result.get('portRelayAdhesion', {}):
  564. fault.append("继电器粘连告警")
  565. result["errorCode"] = "B204"
  566. for _ in portRelayAdhesion.keys():
  567. desc.append("{}号继电器粘连告警 ".format(_))
  568. if desc:
  569. desc = '--'.join(desc)
  570. result.update({"desc": desc})
  571. if fault:
  572. fault = '--'.join(fault)
  573. result.update({"fault": fault})
  574. else:
  575. result = {}
  576. return result
  577. # 2个状态量上报
  578. elif cmdCode == CMD_CODE.DEVICE_FAULT_POWER:
  579. maxPower = int(data[8:12],16)
  580. return {"cmdCode": cmdCode, "maxPower": maxPower}
  581. elif cmdCode == CMD_CODE.DEVICE_FAULT_TEMPERATURE:
  582. maxTemperature = int(data[8:10],16)
  583. return {"cmdCode": cmdCode, "maxTemperature": maxTemperature}
  584. elif cmdCode == CMD_CODE.DEVICE_ELEC:
  585. elec = int(data[8:12], 16)
  586. return {"cmdCode": cmdCode, "elec": elec}
  587. elif cmdCode == CMD_CODE.DEVICE_TEMPERATURE:
  588. temperature = int(data[10:12], 16)
  589. if data[8:10] == "00":
  590. temperature = temperature * -1
  591. return {"cmdCode": cmdCode, "temperature": temperature}
  592. elif cmdCode == CMD_CODE.DEVICE_CARD_CHARGE_2D:
  593. _result = data[6: 8]
  594. _port = str(int(data[8: 10], 16))
  595. _time = int(data[10: 14], 16)
  596. _elec = int(data[14: 18], 16) / 100.0
  597. _type = data[18: 20]
  598. _coins = int(data[20: 22], 16)
  599. _cardNo = str(int(data[22: 30], 16))
  600. _card_cst = int(data[30: 34], 16) / 10.0
  601. _operation = data[34: 36]
  602. _card_type = data[36: 38]
  603. _session_id = data[38: 46]
  604. return {
  605. "portStr": _port,
  606. "time": _time,
  607. "elec": _elec,
  608. "chargeType": _type,
  609. "coins": _coins,
  610. "cardNo": _cardNo,
  611. "cardCst": _card_cst,
  612. "operation": _operation,
  613. "cardType": _card_type,
  614. "sessionId": _session_id,
  615. "cmdCode": cmdCode,
  616. "result": _result,
  617. "sourceData": data
  618. }
  619. elif cmdCode == CMD_CODE.DEVICE_REAL_TIME_REPORT_21:
  620. _result = data[6: 8]
  621. port_num = int(data[8:10],16)
  622. port_info_urat_info = data[10:170]
  623. port_status_desc = {
  624. "01": "端口空闲",
  625. "02": "端口正在使用",
  626. "03": "端口禁用",
  627. "04": "端口故障",
  628. }
  629. port_info = {}
  630. for index in xrange(0, 160, 16):
  631. item = port_info_urat_info[index: index + 16]
  632. one_port = {}
  633. one_port["port"] = int(item[:2], 16)
  634. one_port["portStatus"] = item[2:4]
  635. one_port["portStatusDesc"] = port_status_desc.get(item[2:4])
  636. one_port["leftTime"] = int(item[4:8], 16)
  637. one_port["power"] = int(item[8:12], 16)
  638. one_port["elec"] = int(item[12:16], 16) * 0.01
  639. port_info[str(one_port["port"])] = one_port
  640. return {"cmdCode": cmdCode, "result": _result, "sourceData": data, "portNum": port_num,
  641. "portInfo": port_info}
  642. else:
  643. logger.info("receive data <{}>, cmd is invalid".format(data))
  644. def get_dev_consume_count(self):
  645. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  646. {'IMEI': self._device['devNo'], "funCode": '07', 'data': '00'})
  647. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  648. if devInfo['rst'] == -1:
  649. raise ServiceException(
  650. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  651. elif devInfo['rst'] == 1:
  652. raise ServiceException(
  653. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  654. data = devInfo['data'][6::]
  655. if data[0:2] == '01': # 表示成功
  656. pass
  657. else:
  658. raise ServiceException({'result': 2, 'description': u'获取统计数据失败,请重试看能否解决'})
  659. cardFee = str(RMB(int(data[2:6], 16) / 10.0)) # 以角为单位
  660. coinFee = int(data[6:10], 16) # 以元为单位
  661. return {'cardFee': cardFee, 'coinFee': coinFee}
  662. def get_port_info(self, line):
  663. data = fill_2_hexByte(hex(int(line)), 2)
  664. devInfo = MessageSender.send(self.device, self.make_random_cmdcode(),
  665. {"funCode": '15', 'data': data})
  666. if devInfo['rst'] != 0:
  667. if devInfo['rst'] == -1:
  668. raise ServiceException(
  669. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  670. elif devInfo['rst'] == 1:
  671. raise ServiceException(
  672. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  673. else:
  674. raise ServiceException(
  675. {'result': 2, 'description': u'和充电桩通讯失败({}),请您稍候再试'.format(devInfo['rst'])})
  676. data = devInfo['data'][6::]
  677. if data[0:2] == '01': # 表示成功
  678. pass
  679. else:
  680. raise ServiceException({'result': 2, 'description': u'获取端口信息失败,请重试看能否解决'})
  681. if data[2:4] == '00':
  682. status = Const.DEV_WORK_STATUS_IDLE
  683. else:
  684. left_time = int(data[4:8], 16)
  685. if left_time <= 0 or left_time == 65535:
  686. status = Const.DEV_WORK_STATUS_IDLE
  687. else:
  688. status = Const.DEV_WORK_STATUS_WORKING
  689. # 这里存在多线程更新缓存的场景,可能性比较低,但是必须先取出来,然后逐个更新状态,然后再记录缓存
  690. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  691. old_status = ctrInfo.get(str(line), {}).get('status', Const.DEV_WORK_STATUS_IDLE)
  692. if old_status != status:
  693. if str(line) in ctrInfo:
  694. ctrInfo[str(line)]['status'] = status
  695. else:
  696. ctrInfo[str(line)] = {'status': status}
  697. Device.update_dev_control_cache(self.device.devNo, ctrInfo)
  698. leftTime = int(data[4:8], 16)
  699. if data[8:12] == 'FFFF':
  700. power = 0
  701. else:
  702. power = int(data[8:12], 16)
  703. if data[12:16] == 'FFFF':
  704. elec = 0
  705. else:
  706. elec = int(data[12:16], 16) * 0.01
  707. if data[16:20] == 'FFFF':
  708. surp = 0
  709. else:
  710. surp = int(data[16:20], 16)
  711. rv = {'port': line, 'leftTime': leftTime, 'power': power, 'surp': surp, 'leftElec': elec, 'status': status}
  712. if self.device.my_obj.bill_as_service_feature.on:
  713. rv.pop('leftTime', None)
  714. return rv
  715. def get_port_status(self, force = False):
  716. if force:
  717. return self.get_port_status_from_dev()
  718. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  719. statusDict = {}
  720. if 'allPorts' not in ctrInfo:
  721. self.get_port_status_from_dev()
  722. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  723. allPorts = ctrInfo.get('allPorts', 10)
  724. # allPorts 有的时候会为0, 这个时候强制设置为10
  725. if allPorts == 0:
  726. allPorts = 10
  727. for ii in range(allPorts):
  728. tempDict = ctrInfo.get(str(ii + 1), {})
  729. if tempDict.has_key('status'):
  730. statusDict[str(ii + 1)] = {'status': tempDict.get('status')}
  731. elif tempDict.has_key('isStart'):
  732. if tempDict['isStart']:
  733. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_WORKING}
  734. else:
  735. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  736. else:
  737. statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  738. allPorts, usedPorts, usePorts = self.get_port_static_info(statusDict)
  739. Device.update_dev_control_cache(self._device['devNo'],
  740. {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  741. return statusDict
  742. def get_port_status_from_dev(self):
  743. devInfo = MessageSender.send(self.device, self.make_random_cmdcode(),
  744. {'IMEI': self._device['devNo'], "funCode": '0F', 'data': '00'}, timeout=MQTT_TIMEOUT.CHECK_DEVICE_STATUS)
  745. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  746. if devInfo['rst'] == -1:
  747. raise ServiceException(
  748. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  749. elif devInfo['rst'] == 1:
  750. raise ServiceException(
  751. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  752. data = devInfo['data'][6::]
  753. if data[0:2] == '01': # 表示成功
  754. pass
  755. else:
  756. raise ServiceException({'result': 2, 'description': u'获取统计数据失败,请重试看能否解决'})
  757. result = {}
  758. portNum = int(data[2:4], 16)
  759. portData = data[4::]
  760. ii = 0
  761. while ii < portNum:
  762. port = int(portData[ii * 4:ii * 4 + 2], 16)
  763. statusTemp = portData[ii * 4 + 2:ii * 4 + 4]
  764. if statusTemp == '01':
  765. status = {'status': Const.DEV_WORK_STATUS_IDLE}
  766. elif statusTemp == '02':
  767. status = {'status': Const.DEV_WORK_STATUS_WORKING}
  768. elif statusTemp == '03':
  769. status = {'status': Const.DEV_WORK_STATUS_FORBIDDEN}
  770. elif statusTemp == '04':
  771. status = {'status': Const.DEV_WORK_STATUS_FAULT}
  772. else:
  773. status = {'status': Const.DEV_WORK_STATUS_IDLE}
  774. ii += 1
  775. result[str(port)] = status
  776. allPorts, usedPorts, usePorts = self.get_port_static_info(result)
  777. Device.update_dev_control_cache(self._device['devNo'],
  778. {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  779. # 这里存在多线程更新缓存的场景,可能性比较低,但是必须先取出来,然后逐个更新状态,然后再记录缓存
  780. ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
  781. for strPort, info in result.items():
  782. if ctrInfo.has_key(strPort):
  783. ctrInfo[strPort].update({'status': info['status']})
  784. else:
  785. ctrInfo[strPort] = info
  786. Device.update_dev_control_cache(self._device['devNo'], ctrInfo)
  787. return result
  788. def lock_unlock_port(self, port, lock = True):
  789. lockStr = '00' if lock else '01'
  790. portStr = fill_2_hexByte(hex(int(port)), 2)
  791. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  792. {'IMEI': self._device['devNo'], "funCode": '0C', 'data': portStr + lockStr})
  793. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  794. if devInfo['rst'] == -1:
  795. raise ServiceException(
  796. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  797. elif devInfo['rst'] == 1:
  798. raise ServiceException(
  799. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  800. data = devInfo['data'][6::]
  801. if data[0:2] == '01': # 表示成功
  802. pass
  803. else:
  804. raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'})
  805. if lock:
  806. Device.update_dev_control_cache(self._device['devNo'],
  807. {str(port): {'status': Const.DEV_WORK_STATUS_FORBIDDEN}})
  808. else:
  809. Device.update_dev_control_cache(self._device['devNo'], {str(port): {'status': Const.DEV_WORK_STATUS_IDLE}})
  810. def active_deactive_port(self, port, active):
  811. if not active:
  812. self.stop_charging_port(port)
  813. devInfo = Device.get_dev_control_cache(self._device['devNo'])
  814. portCtrInfo = devInfo.get(str(port), {})
  815. portCtrInfo.update({'isStart': False, 'status': Const.DEV_WORK_STATUS_IDLE,
  816. 'endTime': datetime.datetime.now().strftime(Const.DATETIME_FMT)})
  817. newValue = {str(port): portCtrInfo}
  818. Device.update_dev_control_cache(self._device['devNo'], newValue)
  819. else:
  820. raise ServiceException({'result': 2, 'description': u'此设备不支持直接打开端口'})
  821. def stop_charging_port(self, port):
  822. portStr = fill_2_hexByte(hex(int(port)), 2)
  823. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  824. {'IMEI': self._device['devNo'], "funCode": '0D', 'data': portStr + '00'}, timeout=7)
  825. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  826. if devInfo['rst'] == -1:
  827. raise ServiceException(
  828. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  829. elif devInfo['rst'] == 1:
  830. raise ServiceException(
  831. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  832. data = devInfo['data'][6::]
  833. if data[0:2] == '01': # 表示成功
  834. pass
  835. else:
  836. raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'})
  837. port = int(data[2:4], 16)
  838. leftTime = int(data[4:8], 16)
  839. result = {'port': port, 'leftTime': leftTime}
  840. return result
  841. def get_dev_setting_1E(self):
  842. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  843. {'IMEI': self._device['devNo'], "funCode": '1E', 'data': '00'})
  844. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  845. if devInfo['rst'] == -1:
  846. raise ServiceException(
  847. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  848. elif devInfo['rst'] == 1:
  849. raise ServiceException(
  850. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  851. data = devInfo['data'][6::]
  852. if data[0:2] == '01': # 表示成功
  853. pass
  854. else:
  855. raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'})
  856. if len(data) > 62:
  857. confData = data[2:-2]
  858. coinMin = int(confData[0:4], 16)
  859. cardMin = int(confData[4:8], 16)
  860. coinElec = int(confData[8:10], 16)
  861. cardElec = int(confData[10:12], 16)
  862. cst = int(confData[12:14], 16)
  863. powerMax1 = int(confData[14:18], 16)
  864. powerMax2 = int(confData[18:22], 16)
  865. powerMax3 = int(confData[22:26], 16)
  866. powerMax4 = int(confData[26:30], 16)
  867. powerMax5 = int(confData[30:34], 16)
  868. powerMax6 = int(confData[34:38], 16)
  869. powerMax7 = int(confData[38:42], 16)
  870. powerMax8 = int(confData[42:46], 16)
  871. power2Ti = int(confData[46:48], 16)
  872. power3Ti = int(confData[48:50], 16)
  873. power4Ti = int(confData[50:52], 16)
  874. power5Ti = int(confData[52:54], 16)
  875. power6Ti = int(confData[54:56], 16)
  876. power7Ti = int(confData[56:58], 16)
  877. power8Ti = int(confData[58:60], 16)
  878. spRecMon = int(confData[60:62], 16)
  879. spFullEmpty = int(confData[62:64], 16)
  880. fullPowerMin = int(confData[64:66], 16)
  881. fullChargeTime = int(confData[66:68], 16)
  882. elecTimeFirst = int(confData[68:70], 16)
  883. firstCoinTime = 0
  884. secondCoinTime = 0
  885. thirdCoinTime = 0
  886. resultDict = {
  887. 'coinMin': coinMin,
  888. 'cardMin': cardMin,
  889. 'coinElec': coinElec,
  890. 'cardElec': cardElec,
  891. 'cst': cst,
  892. 'powerMax1': powerMax1,
  893. 'powerMax2': powerMax2,
  894. 'powerMax3': powerMax3,
  895. 'powerMax4': powerMax4,
  896. 'powerMax5': powerMax5,
  897. 'powerMax6': powerMax6,
  898. 'powerMax7': powerMax7,
  899. 'powerMax8': powerMax8,
  900. 'power2Ti': power2Ti,
  901. 'power3Ti': power3Ti,
  902. 'power4Ti': power4Ti,
  903. 'power5Ti': power5Ti,
  904. 'power6Ti': power6Ti,
  905. 'power7Ti': power7Ti,
  906. 'power8Ti': power8Ti,
  907. 'spRecMon': spRecMon,
  908. 'spFullEmpty': spFullEmpty,
  909. 'fullPowerMin': fullPowerMin,
  910. 'fullChargeTime': fullChargeTime,
  911. 'elecTimeFirst': elecTimeFirst,
  912. 'firstCoinTime': firstCoinTime,
  913. 'secondCoinTime': secondCoinTime,
  914. 'thirdCoinTime': thirdCoinTime
  915. }
  916. resultDict.update({'support8PowerTi': True})
  917. else:
  918. confData = data[2:-2]
  919. coinMin = int(confData[0:4], 16)
  920. cardMin = int(confData[4:8], 16)
  921. coinElec = int(confData[8:10], 16)
  922. cardElec = int(confData[10:12], 16)
  923. cst = int(confData[12:14], 16)
  924. powerMax1 = int(confData[14:18], 16)
  925. powerMax2 = int(confData[18:22], 16)
  926. powerMax3 = int(confData[22:26], 16)
  927. powerMax4 = int(confData[26:30], 16)
  928. power2Ti = int(confData[30:32], 16)
  929. power3Ti = int(confData[32:34], 16)
  930. power4Ti = int(confData[34:36], 16)
  931. spRecMon = int(confData[36:38], 16)
  932. spFullEmpty = int(confData[38:40], 16)
  933. fullPowerMin = int(confData[40:42], 16)
  934. fullChargeTime = int(confData[42:44], 16)
  935. elecTimeFirst = int(confData[44:46], 16)
  936. try:
  937. firstCoinTime = int(confData[46:50], 16)
  938. secondCoinTime = int(confData[50:54], 16)
  939. thirdCoinTime = int(confData[54:58], 16)
  940. except Exception as e:
  941. firstCoinTime = 0
  942. secondCoinTime = 0
  943. thirdCoinTime = 0
  944. resultDict = {
  945. 'coinMin': coinMin,
  946. 'cardMin': cardMin,
  947. 'coinElec': coinElec,
  948. 'cardElec': cardElec,
  949. 'cst': cst,
  950. 'powerMax1': powerMax1,
  951. 'powerMax2': powerMax2,
  952. 'powerMax3': powerMax3,
  953. 'powerMax4': powerMax4,
  954. 'power2Ti': power2Ti,
  955. 'power3Ti': power3Ti,
  956. 'power4Ti': power4Ti,
  957. 'spRecMon': spRecMon,
  958. 'spFullEmpty': spFullEmpty,
  959. 'fullPowerMin': fullPowerMin,
  960. 'fullChargeTime': fullChargeTime,
  961. 'elecTimeFirst': elecTimeFirst,
  962. 'firstCoinTime': firstCoinTime,
  963. 'secondCoinTime': secondCoinTime,
  964. 'thirdCoinTime': thirdCoinTime
  965. }
  966. try:
  967. self.device.update_other_conf(
  968. cst = cst, coinMin = coinMin, coinElec = coinElec, cardMin = cardMin, cardElec = cardElec)
  969. except Exception, e:
  970. pass
  971. return resultDict
  972. def get_dev_setting(self):
  973. """
  974. 获取设备配置参数
  975. :return:
  976. """
  977. resultDict = self.get_dev_setting_1E()
  978. consumeInfo = self.get_dev_consume_count()
  979. resultDict.update(consumeInfo)
  980. deviceInfo = self.get_real_time_device_info()
  981. resultDict.update(deviceInfo)
  982. return resultDict
  983. def set_shenlin_8_gear_settings(self, setConf):
  984. data = ''
  985. data += fill_2_hexByte(hex(int(setConf['coinMin'])), 4)
  986. data += fill_2_hexByte(hex(int(setConf['cardMin'])), 4)
  987. data += fill_2_hexByte(hex(int(setConf['coinElec'])), 2)
  988. data += fill_2_hexByte(hex(int(setConf['cardElec'])), 2)
  989. data += fill_2_hexByte(hex(int(setConf['cst'])), 2)
  990. data += fill_2_hexByte(hex(int(setConf['powerMax1'])), 4)
  991. data += fill_2_hexByte(hex(int(setConf['powerMax2'])), 4)
  992. data += fill_2_hexByte(hex(int(setConf['powerMax3'])), 4)
  993. data += fill_2_hexByte(hex(int(setConf['powerMax4'])), 4)
  994. data += fill_2_hexByte(hex(int(setConf['powerMax5'])), 4)
  995. data += fill_2_hexByte(hex(int(setConf['powerMax6'])), 4)
  996. data += fill_2_hexByte(hex(int(setConf['powerMax7'])), 4)
  997. data += fill_2_hexByte(hex(int(setConf['powerMax8'])), 4)
  998. data += fill_2_hexByte(hex(int(setConf['power2Ti'])), 2)
  999. data += fill_2_hexByte(hex(int(setConf['power3Ti'])), 2)
  1000. data += fill_2_hexByte(hex(int(setConf['power4Ti'])), 2)
  1001. data += fill_2_hexByte(hex(int(setConf['power5Ti'])), 2)
  1002. data += fill_2_hexByte(hex(int(setConf['power6Ti'])), 2)
  1003. data += fill_2_hexByte(hex(int(setConf['power7Ti'])), 2)
  1004. data += fill_2_hexByte(hex(int(setConf['power8Ti'])), 2)
  1005. data += fill_2_hexByte(hex(int(setConf['spRecMon'])), 2)
  1006. data += fill_2_hexByte(hex(int(setConf['spFullEmpty'])), 2)
  1007. data += fill_2_hexByte(hex(int(setConf['fullPowerMin'])), 2)
  1008. data += fill_2_hexByte(hex(int(setConf['fullChargeTime'])), 2)
  1009. data += fill_2_hexByte(hex(int(setConf['elecTimeFirst'])), 2)
  1010. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  1011. {'IMEI': self._device['devNo'], "funCode": '18', 'data': data})
  1012. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  1013. if devInfo['rst'] == -1:
  1014. raise ServiceException(
  1015. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  1016. elif devInfo['rst'] == 1:
  1017. raise ServiceException(
  1018. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  1019. data = devInfo['data'][6::]
  1020. if data[0:2] == '01': # 表示成功
  1021. self.device.update_other_conf(
  1022. cst=int(setConf['cst']), coinMin=int(setConf['coinMin']),
  1023. coinElec=int(setConf['coinElec']), cardMin=int(setConf['cardMin']),
  1024. cardElec=int(setConf['cardElec']))
  1025. else:
  1026. raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'})
  1027. def set_dev_setting(self, setConf):
  1028. """
  1029. 设置设备配置参数
  1030. :param setConf:
  1031. :return:
  1032. """
  1033. data = ''
  1034. data += fill_2_hexByte(hex(int(setConf['coinMin'])), 4)
  1035. data += fill_2_hexByte(hex(int(setConf['cardMin'])), 4)
  1036. data += fill_2_hexByte(hex(int(setConf['coinElec'])), 2)
  1037. data += fill_2_hexByte(hex(int(setConf['cardElec'])), 2)
  1038. data += fill_2_hexByte(hex(int(setConf['cst'])), 2)
  1039. data += fill_2_hexByte(hex(int(setConf['powerMax1'])), 4)
  1040. data += fill_2_hexByte(hex(int(setConf['powerMax2'])), 4)
  1041. data += fill_2_hexByte(hex(int(setConf['powerMax3'])), 4)
  1042. data += fill_2_hexByte(hex(int(setConf['powerMax4'])), 4)
  1043. data += fill_2_hexByte(hex(int(setConf['power2Ti'])), 2)
  1044. data += fill_2_hexByte(hex(int(setConf['power3Ti'])), 2)
  1045. data += fill_2_hexByte(hex(int(setConf['power4Ti'])), 2)
  1046. data += fill_2_hexByte(hex(int(setConf['spRecMon'])), 2)
  1047. data += fill_2_hexByte(hex(int(setConf['spFullEmpty'])), 2)
  1048. data += fill_2_hexByte(hex(int(setConf['fullPowerMin'])), 2)
  1049. data += fill_2_hexByte(hex(int(setConf['fullChargeTime'])), 2)
  1050. data += fill_2_hexByte(hex(int(setConf['elecTimeFirst'])), 2)
  1051. if self.device['devType']['code'] == Const.DEVICE_TYPE_CODE_CHARGING_JH_TOUBI:
  1052. data += fill_2_hexByte(hex(int(setConf['firstCoinTime'])), 4)
  1053. data += fill_2_hexByte(hex(int(setConf['secondCoinTime'])), 4)
  1054. data += fill_2_hexByte(hex(int(setConf['thirdCoinTime'])), 4)
  1055. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  1056. {'IMEI': self._device['devNo'], "funCode": '18', 'data': data})
  1057. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  1058. if devInfo['rst'] == -1:
  1059. raise ServiceException(
  1060. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  1061. elif devInfo['rst'] == 1:
  1062. raise ServiceException(
  1063. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  1064. data = devInfo['data'][6::]
  1065. if data[0:2] == '01': # 表示成功
  1066. self.device.update_other_conf(
  1067. cst=int(setConf['cst']), coinMin=int(setConf['coinMin']),
  1068. coinElec=int(setConf['coinElec']), cardMin=int(setConf['cardMin']),
  1069. cardElec=int(setConf['cardElec']))
  1070. else:
  1071. raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'})
  1072. def response_use_card(self, res, balance, uartId = None):
  1073. logger.info("ready to send response to dev <{}> funcode 10".format(self.device.devNo))
  1074. if not uartId:
  1075. data = '55061001'
  1076. data = data + res + fill_2_hexByte(hex(int(balance * 10)), 4)
  1077. devInfo = MessageSender.send(self.device, DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE,
  1078. {'IMEI': self._device['devNo'], "funCode": '10', 'data': data})
  1079. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  1080. if devInfo['rst'] == -1:
  1081. raise ServiceException(
  1082. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  1083. elif devInfo['rst'] == 1: # 等于1的时候,说明服务器和远程模块通讯OK,响应已经收到,不需要异常处理
  1084. pass
  1085. else:
  1086. data = '55061001'
  1087. data = data + res + fill_2_hexByte(hex(int(balance * 10)), 4)
  1088. MessageSender.send(
  1089. self.device,
  1090. DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE,
  1091. {
  1092. "IMEI": self.device.devNo,
  1093. "funCode": '10',
  1094. "data": data,
  1095. "uartId": int(uartId)
  1096. }
  1097. )
  1098. def set_device_function_param(self, request, lastSetConf):
  1099. coinMin = request.POST.get('coinMin', None)
  1100. cardMin = request.POST.get('cardMin', None)
  1101. coinElec = request.POST.get('coinElec', None)
  1102. cardElec = request.POST.get('cardElec', None)
  1103. cst = request.POST.get('cst', None)
  1104. powerMax1 = request.POST.get('powerMax1', None)
  1105. powerMax2 = request.POST.get('powerMax2', None)
  1106. powerMax3 = request.POST.get('powerMax3', None)
  1107. powerMax4 = request.POST.get('powerMax4', None)
  1108. power2Ti = request.POST.get('power2Ti', None)
  1109. power3Ti = request.POST.get('power3Ti', None)
  1110. power4Ti = request.POST.get('power4Ti', None)
  1111. spRecMon = request.POST.get('spRecMon', None)
  1112. spFullEmpty = request.POST.get('spFullEmpty', None)
  1113. fullPowerMin = request.POST.get('fullPowerMin', None)
  1114. fullChargeTime = request.POST.get('fullChargeTime', None)
  1115. elecTimeFirst = request.POST.get('elecTimeFirst', None)
  1116. # 这几个参数是墨小智V3特有的
  1117. stWTime = request.POST.get('stWTime', None)
  1118. temThre = request.POST.get('temThre', None)
  1119. freeUse = request.POST.get('freeUse', None)
  1120. relayMasterSwitch = request.POST.get('relayMasterSwitch', None)
  1121. # 这几个参数是久恒保险丝版本特有的
  1122. firstCoinTime = request.POST.get('firstCoinTime', None)
  1123. secondCoinTime = request.POST.get('secondCoinTime', None)
  1124. thirdCoinTime = request.POST.get('thirdCoinTime', None)
  1125. # 这几个参数是申林电子8档久恒板特有的
  1126. powerMax5 = request.POST.get('powerMax5', None)
  1127. powerMax6 = request.POST.get('powerMax6', None)
  1128. powerMax7 = request.POST.get('powerMax7', None)
  1129. powerMax8 = request.POST.get('powerMax8', None)
  1130. power5Ti = request.POST.get('power5Ti', None)
  1131. power6Ti = request.POST.get('power6Ti', None)
  1132. power7Ti = request.POST.get('power7Ti', None)
  1133. power8Ti = request.POST.get('power8Ti', None)
  1134. if coinMin:
  1135. lastSetConf.update({'coinMin': int(coinMin)})
  1136. if cardMin:
  1137. lastSetConf.update({'cardMin': int(cardMin)})
  1138. if coinElec:
  1139. lastSetConf.update({'coinElec': int(coinElec)})
  1140. if cardElec:
  1141. lastSetConf.update({'cardElec': int(cardElec)})
  1142. if cst:
  1143. lastSetConf.update({'cst': int(cst)})
  1144. if powerMax1:
  1145. lastSetConf.update({'powerMax1': int(powerMax1)})
  1146. if powerMax2:
  1147. lastSetConf.update({'powerMax2': int(powerMax2)})
  1148. if powerMax3:
  1149. lastSetConf.update({'powerMax3': int(powerMax3)})
  1150. if powerMax4:
  1151. lastSetConf.update({'powerMax4': int(powerMax4)})
  1152. if power2Ti:
  1153. lastSetConf.update({'power2Ti': int(power2Ti)})
  1154. if power3Ti:
  1155. lastSetConf.update({'power3Ti': int(power3Ti)})
  1156. if power4Ti:
  1157. lastSetConf.update({'power4Ti': int(power4Ti)})
  1158. if spRecMon:
  1159. lastSetConf.update({'spRecMon': int(spRecMon)})
  1160. if spFullEmpty:
  1161. lastSetConf.update({'spFullEmpty': int(spFullEmpty)})
  1162. if fullPowerMin:
  1163. lastSetConf.update({'fullPowerMin': int(fullPowerMin)})
  1164. if fullChargeTime:
  1165. lastSetConf.update({'fullChargeTime': int(fullChargeTime)})
  1166. if elecTimeFirst:
  1167. lastSetConf.update({'elecTimeFirst': int(elecTimeFirst)})
  1168. # 这几个参数是墨小智V3特有的
  1169. if stWTime:
  1170. lastSetConf.update({'stWTime': int(stWTime)})
  1171. if temThre:
  1172. lastSetConf.update({'temThre': int(temThre)})
  1173. if freeUse:
  1174. lastSetConf.update({'freeUse': int(freeUse)})
  1175. # 这几个参数是久恒保险丝版本特有的
  1176. if firstCoinTime:
  1177. lastSetConf.update({'firstCoinTime': int(firstCoinTime)})
  1178. if secondCoinTime:
  1179. lastSetConf.update({'secondCoinTime': int(secondCoinTime)})
  1180. if thirdCoinTime:
  1181. lastSetConf.update({'thirdCoinTime': int(thirdCoinTime)})
  1182. # 这几个参数是申林电子8档久恒板特有的
  1183. if powerMax5:
  1184. lastSetConf.update({'powerMax5': int(powerMax5)})
  1185. if powerMax6:
  1186. lastSetConf.update({'powerMax6': int(powerMax6)})
  1187. if powerMax7:
  1188. lastSetConf.update({'powerMax7': int(powerMax7)})
  1189. if powerMax8:
  1190. lastSetConf.update({'powerMax8': int(powerMax8)})
  1191. if power5Ti:
  1192. lastSetConf.update({'power5Ti': int(power5Ti)})
  1193. if power6Ti:
  1194. lastSetConf.update({'power6Ti': int(power6Ti)})
  1195. if power7Ti:
  1196. lastSetConf.update({'power7Ti': int(power7Ti)})
  1197. if power8Ti:
  1198. lastSetConf.update({'power8Ti': int(power8Ti)})
  1199. if all([powerMax5, powerMax6, powerMax7, powerMax8, power5Ti, power6Ti, power7Ti, power8Ti]) is True:
  1200. self.set_shenlin_8_gear_settings(lastSetConf)
  1201. else:
  1202. self.set_dev_setting(lastSetConf)
  1203. # 新增总继电器开关
  1204. if relayMasterSwitch:
  1205. try:
  1206. self.relay_master_switch(relayMasterSwitch)
  1207. except Exception as e:
  1208. pass
  1209. def handle_clear_start_error(self, port):
  1210. dealer = Dealer.objects.get(id = self._device["ownerId"])
  1211. if not dealer or not dealer.managerialOpenId:
  1212. return
  1213. group = Group.get_group(self._device["groupId"])
  1214. self.lock_unlock_port(port, lock = False)
  1215. notifyData = {
  1216. "title": "设备故障报警解除",
  1217. "device": u"{gNum}组-{lc}-{port}端口".format(gNum = self._device["groupNumber"],
  1218. lc = self._device["logicalCode"],
  1219. port = port),
  1220. "location": u"{address}-{groupName}".format(address = group["address"], groupName = group["groupName"]),
  1221. "fault": u"当前端口已被正常启动,端口故障解除",
  1222. "notifyTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  1223. }
  1224. task_caller(
  1225. func_name = 'report_to_dealer_via_wechat',
  1226. openId = dealer.managerialOpenId,
  1227. dealerId = str(dealer.id),
  1228. templateName = "device_fault",
  1229. **notifyData
  1230. )
  1231. self.lock_unlock_port(port, lock = False)
  1232. def handle_out_start_error(self, port):
  1233. dealer = Dealer.objects.get(id = self._device["ownerId"])
  1234. if not dealer or not dealer.managerialOpenId:
  1235. return
  1236. group = Group.get_group(self._device["groupId"])
  1237. times = Device.get_error_start_times(self._device["devNo"], port)
  1238. self.lock_unlock_port(port, lock = True)
  1239. notifyData = {
  1240. "title": u"注意,您的设备可能发生故障!",
  1241. "device": u"{gNum}组-{lc}-{port}端口".format(gNum = self._device["groupNumber"],
  1242. lc = self._device["logicalCode"],
  1243. port = port),
  1244. "location": u"{address}-{groupName}".format(address = group["address"], groupName = group["groupName"]),
  1245. "fault": u"当前设备端口连续启动 {times} 失败,目前该端口已经被自动禁用,请注意该端口是否发生故障".format(times = times),
  1246. "notifyTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  1247. }
  1248. task_caller(
  1249. func_name = 'report_to_dealer_via_wechat',
  1250. openId = dealer.managerialOpenId,
  1251. dealerId = str(dealer.id),
  1252. templateName = "device_fault",
  1253. **notifyData
  1254. )
  1255. # 麦总的告警3次触发,同时锁定设备,为避免解锁设备之后 再次触发 这个地方需要将其缓存清空
  1256. Device.delete_error_start_times(self._device["devNo"], port)
  1257. @SmartBox.check_device_features(device_features=["fun_code_30_31"])
  1258. def relay_master_switch(self,relayMasterSwitch=1):
  1259. if relayMasterSwitch == 1:
  1260. data = "01"
  1261. else:
  1262. data = "00"
  1263. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  1264. {'IMEI': self._device['devNo'], 'funCode': '31', 'data': data},timeout=7)
  1265. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  1266. if devInfo['rst'] == -1:
  1267. raise ServiceException(
  1268. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  1269. elif devInfo['rst'] == 1:
  1270. raise ServiceException(
  1271. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  1272. self.device.update_other_conf(relayMasterSwitch=relayMasterSwitch)
  1273. @SmartBox.check_device_features(device_features=["fun_code_30_31"], no_features_to_return={"haveStatus": False})
  1274. def get_real_time_device_info(self):
  1275. try:
  1276. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  1277. {'IMEI': self._device['devNo'], 'funCode': '30', 'data': '00'},timeout=7)
  1278. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  1279. if devInfo['rst'] == -1:
  1280. raise ServiceException(
  1281. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  1282. elif devInfo['rst'] == 1:
  1283. raise ServiceException(
  1284. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  1285. except Exception:
  1286. return {"haveStatus": False}
  1287. data = devInfo.get("data")
  1288. maxPower = int(data[8:12], 16)
  1289. elec = int(data[12:16], 16)
  1290. maxTemperature = int(data[16:18], 16)
  1291. base = -1 if int(data[18:20], 16) == 1 else 1
  1292. temperature = int(data[20:22], 16) * base
  1293. fire = int(data[22:24], 16)
  1294. smoke = int(data[24:26], 16)
  1295. overheat = int(data[26:28], 16)
  1296. overload = int(data[28:30], 16)
  1297. relayMasterSwitch = int(data[30:32], 16)
  1298. return {"haveStatus": True, "maxPower": maxPower, "elec": elec, "maxTemperature": maxTemperature,
  1299. "temperature": temperature,
  1300. "fire": fire, "smoke": smoke, "overheat": overheat, "overload": overload,
  1301. "relayMasterSwitch": relayMasterSwitch, }
  1302. def _response_to_2D(self, sessionId):
  1303. """
  1304. 回复 充电桩主板的2D指令 否则会重复上报
  1305. :param sessionId:
  1306. :return:
  1307. """
  1308. MessageSender.send(
  1309. device=self.device,
  1310. cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE,
  1311. payload={
  1312. "funCode": "2D",
  1313. "data": sessionId,
  1314. "IMEI": self.device.devNo
  1315. }
  1316. )
  1317. def _check_package(self, package):
  1318. """
  1319. 获取设备启动的发送数据 根据设备的当前模式以及套餐获取
  1320. :param package:
  1321. :return:
  1322. """
  1323. unit = package.get("unit", u"分钟")
  1324. _time = float(package.get("time", 0))
  1325. # 按时间计费
  1326. if unit == u"小时":
  1327. billingType = "time"
  1328. _time = _time * 60
  1329. elif unit == u"天":
  1330. billingType = "time"
  1331. _time = _time * 24 * 60
  1332. elif unit == u"秒":
  1333. billingType = "time"
  1334. _time = _time / 60
  1335. elif unit == u"分钟":
  1336. billingType = "time"
  1337. _time = _time
  1338. else:
  1339. billingType = "elec"
  1340. _time = _time
  1341. return _time, unit, billingType
  1342. def response_card(self, res, balance, fee, oper, cardNoHex, isVir=False, virtual_card_id=None):
  1343. if isVir:
  1344. res = int(res)
  1345. count = int(balance) * 10
  1346. fee = int(fee) * 10
  1347. oper = int(oper)
  1348. MessageSender.send(
  1349. device=self.device,
  1350. cmd=220,
  1351. payload={
  1352. "res": res,
  1353. "balance": count,
  1354. "card_cst": fee,
  1355. "card_ope": oper,
  1356. "card_id": cardNoHex,
  1357. "funCode": "10",
  1358. "isVir": isVir,
  1359. "virtual_card_id": virtual_card_id
  1360. }
  1361. )
  1362. else:
  1363. res = int(res)
  1364. balance = int(balance) * 10
  1365. fee = int(fee) * 10
  1366. oper = int(oper)
  1367. MessageSender.send(
  1368. device=self.device,
  1369. cmd=220,
  1370. payload={
  1371. "res": res,
  1372. "balance": balance,
  1373. "card_cst": fee,
  1374. "card_ope": oper,
  1375. "card_id": cardNoHex,
  1376. "funCode": "10",
  1377. "isVir": isVir
  1378. }
  1379. )
  1380. def apiGetPortStatusFromJh(self, record):
  1381. return self.get_port_status_from_dev()
  1382. def apiGetPortStatusFromJn(self, record):
  1383. port = record['port']
  1384. statusData = self.get_port_status_from_dev()
  1385. portData = self.get_port_info(port)
  1386. device = Device.objects(devNo=self._device.devNo).first()
  1387. group = Group.objects(id=device.groupId).first()
  1388. cs = ConsumeRecord.objects(devNo=self._device.devNo).order_by('-dateTimeAdded')[0:10]
  1389. elecValue = 0
  1390. for _ in cs:
  1391. if _.attachParas.get('chargeIndex', '0') == port:
  1392. usedHours = round(float(round(((datetime.datetime.now() - _.dateTimeAdded).total_seconds() / 60.0))) / 60, 2)
  1393. elecValue = round((usedHours * portData.get('power', 0)) / 1000, 2)
  1394. break
  1395. else:
  1396. elecValue = 0
  1397. status = statusData.get(portData.get('port', -1), '04')
  1398. if status != '01':
  1399. elecValue = 0
  1400. voltage = random.randint(220, 240)
  1401. elecCurrent = 0
  1402. result = {
  1403. 'port': portData.get('port', -1), # int
  1404. 'status': statusData.get(portData.get('port', -1), '04'), # str '00' -> idle, '01' -> working, '03' -> 'forbidden', '04' -> 'fault'
  1405. 'voltage': voltage, # int
  1406. 'deviceCode': self._device.logicalCode, # str
  1407. 'elecCurrent': elecCurrent, # float
  1408. 'elecValue': elecValue, # float
  1409. 'leftTime': portData.get('leftTime', 0) * 60, # int 秒
  1410. 'groupName': group.groupName # str
  1411. }
  1412. return result
  1413. def apiStartDeviceForJh(self, record):
  1414. port = record['port']
  1415. minutes = record['time']
  1416. elec = record['elec']
  1417. hexPort = fill_2_hexByte(hex(int(port)), 2)
  1418. hexCoins = '000A'
  1419. hexTime = fill_2_hexByte(hex(int(minutes)))
  1420. hexElec = fill_2_hexByte(hex(int(float(elec) * 100)), 4)
  1421. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {
  1422. 'IMEI': self._device['devNo'],
  1423. "funCode": '14',
  1424. 'data': hexPort + hexCoins + hexTime + hexElec
  1425. }, timeout=10)
  1426. if devInfo.has_key('rst') and devInfo['rst'] != 0:
  1427. if devInfo['rst'] == -1:
  1428. return {'result': u'启动失败', 'resultStatus': 2, 'description': u'当前设备信号弱无响应。'}
  1429. elif devInfo['rst'] == 1:
  1430. return {'result': u'启动失败', 'resultStatus': 2, 'description': u'设备串口不通。'}
  1431. data = devInfo['data'][6::]
  1432. if data[0:2] == '01': # 表示成功
  1433. pass
  1434. else:
  1435. return {'result': u'启动失败', 'resultStatus': 2, 'description': u'设备返回失败。'}
  1436. result = data[4:6]
  1437. if result == '01': # 成功
  1438. pass
  1439. elif result == '0B':
  1440. return {'result': u'启动失败', 'resultStatus': 2, 'description': u'设备故障。'}
  1441. elif result == '0C':
  1442. return {'result': u'启动失败', 'resultStatus': 2, 'description': u'端口正在使用中。'}
  1443. start_timestamp = int(time.time())
  1444. portDict = {
  1445. 'startTime': timestamp_to_dt(start_timestamp).strftime('%Y-%m-%d %H:%M:%S'),
  1446. 'status': Const.DEV_WORK_STATUS_WORKING,
  1447. 'coins': 1,
  1448. 'price': 1,
  1449. 'needElec': elec,
  1450. 'needTime': minutes,
  1451. 'isApi': True
  1452. }
  1453. Device.update_dev_control_cache(self._device['devNo'], {str(port): portDict})
  1454. return {'result': u'启动成功', 'resultStatus': 1, 'description': u'设备开始充电。'}
  1455. def apiStopChargingPortForJh(self, record):
  1456. port = record['port']
  1457. self.stop_charging_port(port)
  1458. return {'result': u'停止充电成功', 'resultStatus': 1, 'description': u'{}号端口停止充电。'.format(str(port))}
  1459. def apiGetPortInfoFromJh(self, record):
  1460. result = self.get_port_info(record['port'])
  1461. result.pop('surp')
  1462. return result
  1463. def get_ports_info(self, **kw):
  1464. result = self.get_port_status_from_dev()
  1465. port_list = []
  1466. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  1467. statsMap = {Const.DEV_WORK_STATUS_IDLE: 'idle',
  1468. Const.DEV_WORK_STATUS_WORKING: 'busy',
  1469. Const.DEV_WORK_STATUS_FAULT: 'fault',
  1470. Const.DEV_WORK_STATUS_FORBIDDEN: 'ban',
  1471. Const.DEV_WORK_STATUS_CONNECTED: 'connected',
  1472. Const.DEV_WORK_STATUS_FINISHED: 'finished'}
  1473. for port, _info in result.items():
  1474. _info['index'] = port
  1475. if _info.get('status') == Const.DEV_WORK_STATUS_WORKING:
  1476. _info.update(self.get_port_using_detail(port, ctrInfo, isLazy=True))
  1477. for k, v in _info.items():
  1478. if k.lower().endswith('money'):
  1479. _info.pop(k)
  1480. _info['status'] = statsMap.get(_info['status'], 'busy')
  1481. port_list.append(_info)
  1482. return port_list
  1483. def _set_service_charge_pare_for_dev(self, elecCharge, serviceCharge, on):
  1484. _elecCharge = float(str(elecCharge))
  1485. _serviceCharge = float(str(serviceCharge))
  1486. billAsServiceSwitch = '01' if on else '00'
  1487. hexElecCharge_4 = fill_2_hexByte(hex(int(_elecCharge * 100)), 4)
  1488. hexServiceCharge_4 = fill_2_hexByte(hex(int(_serviceCharge * 100)), 4)
  1489. data = billAsServiceSwitch + hexElecCharge_4 + hexServiceCharge_4 + "0000000000"
  1490. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {
  1491. 'IMEI': self._device['devNo'],
  1492. "funCode": '22',
  1493. 'data': data
  1494. }, timeout=7)
  1495. if devInfo['rst'] != 0:
  1496. if devInfo['rst'] == -1:
  1497. raise ServiceException(
  1498. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  1499. elif devInfo['rst'] == 1:
  1500. raise ServiceException(
  1501. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  1502. else:
  1503. raise ServiceException(
  1504. {'result': 2, 'description': u'设备通讯失败'})
  1505. self.device.update_device_obj(**{
  1506. 'devType.features.billAsService.elecCharge': _elecCharge,
  1507. 'devType.features.billAsService.serviceCharge': _serviceCharge,
  1508. 'devType.features.billAsService.on': True if billAsServiceSwitch == '01' else False
  1509. })
  1510. def __get_service_charge_pare_for_dev(self):
  1511. serviceChargeInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  1512. {"funCode": '23', 'data': '00'}, timeout=7)
  1513. if serviceChargeInfo['rst'] != 0:
  1514. if serviceChargeInfo['rst'] == -1:
  1515. raise ServiceException(
  1516. {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
  1517. elif serviceChargeInfo['rst'] == 1:
  1518. raise ServiceException(
  1519. {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
  1520. else:
  1521. raise ServiceException(
  1522. {'result': 2, 'description': u'设备通讯失败'})
  1523. data = serviceChargeInfo['data'][8:]
  1524. on = True if data[:2] == '01' else False
  1525. elecCharge = float(int(data[2:6], 16)) / 100
  1526. serviceCharge = float(int(data[6:10], 16)) / 100
  1527. Device.get_collection().update_one({'devNo': self.device.devNo}, {
  1528. '$set': {
  1529. 'devType.features.billAsService.elecCharge': elecCharge,
  1530. 'devType.features.billAsService.serviceCharge': serviceCharge,
  1531. 'devType.features.billAsService.on': on
  1532. }})
  1533. Device.invalid_device_cache(self.device.devNo)
  1534. return {
  1535. "on": on,
  1536. "elecCharge": elecCharge,
  1537. "serviceCharge": serviceCharge
  1538. }
  1539. def response_balance_inquiry_50(self,balance,leftDayQuota,cardNo,status,power='0000'):
  1540. hexBalance = fill_2_hexByte(hex(int(float(balance) * 10)))
  1541. hexLeftDayQuota = fill_2_hexByte(hex(int(leftDayQuota)))
  1542. hexCardNo = fill_2_hexByte(hex(int(cardNo)))
  1543. hexStatus = fill_2_hexByte(hex(int(status)),2)
  1544. hexPower = fill_2_hexByte(hex(int(float(power))))
  1545. data = hexBalance + hexLeftDayQuota + hexCardNo + hexStatus + hexPower
  1546. MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_NO_RESPONSE,
  1547. {'IMEI': self._device['devNo'], 'funCode': '50', 'data': data}, timeout=7)
  1548. def response_use_card_52(self,cardNo):
  1549. data = fill_2_hexByte(hex(int(cardNo)),8)
  1550. MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_NO_RESPONSE,
  1551. {'IMEI': self._device['devNo'], 'funCode': '52', 'data': data}, timeout=7)
  1552. def respone_use_card_finished_56(self,cardNo):
  1553. data = fill_2_hexByte(hex(int(cardNo)),8)
  1554. MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_NO_RESPONSE,
  1555. {'IMEI': self._device['devNo'], 'funCode': '56', 'data': data}, timeout=7)
  1556. def response_ak_5X(self,ack_id):
  1557. data = ack_id
  1558. MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_NO_RESPONSE,
  1559. {'IMEI': self._device['devNo'], 'funCode': 'AK', 'data': data}, timeout=7)
  1560. def get_server_setting(self):
  1561. refundProtectionTime = int(self.device.get_other_conf_item('refundProtectionTime', 5))
  1562. return {
  1563. "refundProtectionTime": refundProtectionTime,
  1564. "cardRefundProtectionTime": int(
  1565. self.device.get_other_conf_item('cardRefundProtectionTime', refundProtectionTime)),
  1566. "minUsedTime": int(self.device.get_other_conf_item('minUsedTime', 0)),
  1567. "billingType": self.device.get_other_conf_item('billingType', 'time'),
  1568. "elecFee": float(self.device.get_other_conf_item('elecFee', 0.9))
  1569. }
  1570. def set_server_setting(self, payload):
  1571. minUsedTime = int(payload['minUsedTime'])
  1572. refundProtectionTime = int(payload['refundProtectionTime'])
  1573. cardRefundProtectionTime = int(payload['cardRefundProtectionTime'])
  1574. if minUsedTime > 0:
  1575. if refundProtectionTime >= minUsedTime:
  1576. raise InvalidParameter(u'最小使用时长不能小于扫码保护退费时间')
  1577. if cardRefundProtectionTime >= minUsedTime:
  1578. raise InvalidParameter(u'最小使用时长不能小于刷卡保护退费时间')
  1579. self.device.update_other_conf(refundProtectionTime=refundProtectionTime,
  1580. cardRefundProtectionTime=cardRefundProtectionTime,
  1581. minUsedTime=minUsedTime,
  1582. billingType=payload['billingType'],
  1583. elecFee=float(payload['elecFee']))
  1584. def set_service_fee_info(self, payload):
  1585. self._set_service_charge_pare_for_dev(
  1586. payload['billAsService'].get('elecCharge'), payload['billAsService'].get('serviceCharge'),
  1587. payload['billAsService'].get('on'))
  1588. super(ChargingJNDZBox, self).set_service_fee_info(payload)
  1589. def get_service_fee_info(self):
  1590. rv = super(ChargingJNDZBox, self).get_service_fee_info()
  1591. rv['billAsService'].update(self.__get_service_charge_pare_for_dev())
  1592. return rv
  1593. class IdCardStartAckEvent(AckEvent):
  1594. pass