12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- import datetime
- import logging
- import time
- from django.core.cache import caches
- from typing import Optional, Dict, TYPE_CHECKING
- from apilib.utils_datetime import timestamp_to_dt
- from apps.web.agent.models import Agent
- from apps.web.constant import DeviceCmdCode, Const, MQTT_TIMEOUT
- from apps.web.core.adapter.base import SmartBox, fill_2_hexByte, start_error_timer
- from apps.web.core.exceptions import ServiceException
- from apps.web.core.networking import MessageSender
- from apps.web.dealer.models import Dealer
- from apps.web.device.models import Device, Group, DeviceType
- from apps.web.core.device_define.jndz import CMD_CODE
- from apps.web.user.models import MyUser
- from taskmanager.mediator import task_caller
- if TYPE_CHECKING:
- from apps.web.api.models import APIStartDeviceRecord
- logger = logging.getLogger(__name__)
- class ChargingAOQIANGBox(SmartBox):
- """
- 久恒协议和劲能一致,业务修改和功能扩充需要确定是否两边都修改
- """
- def __init__(self, device):
- super(ChargingAOQIANGBox, self).__init__(device)
- @property
- def isHaveStopEvent(self):
- return True
- def translate_funcode(self, funCode):
- funCodeDict = {
- '01': u'获取端口数量',
- '02': u'获取端口数据',
- '14': u'移动支付',
- '07': u'获取刷卡投币统计数据',
- '15': u'获取设备端口详情',
- '0F': u'获取端口状态',
- '0C': u'端口锁操作',
- '0D': u'端口开关',
- '1E': u'获取设备设置',
- '18': u'设置设备参数',
- '10': u'回复卡余额',
- }
- return funCodeDict.get(funCode, '')
- def translate_event_cmdcode(self, cmdCode):
- cmdDict = {
- '06': u'充电结束',
- '16': u'充电结束',
- '0A': u'故障',
- '10': u'刷卡上报',
- '20': u'启动设备',
- }
- return cmdDict.get(cmdCode, '')
- def test(self, coins):
- hexElec = fill_2_hexByte(hex(0), 4)
- hexPort = fill_2_hexByte(hex(1), 2)
- hexCoins = fill_2_hexByte(hex(1))
- hexTime = fill_2_hexByte(hex(60))
- devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
- {'IMEI': self._device['devNo'], "funCode": '14',
- 'data': hexPort + hexCoins + hexTime + hexElec})
- return devInfo
- def stop(self, port = None):
- infoDict = self.stop_charging_port(port)
- infoDict['remainder_time'] = infoDict['leftTime']
- return infoDict
- @start_error_timer(missMessages = [u"请您选择合适的充电线路、电池类型信息", u"请您选择合适的充电线路", u"该端口正在使用中"])
- def start_device(self, package, openId, attachParas):
- if attachParas is None:
- raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路、电池类型信息'})
- if not attachParas.has_key('chargeIndex'):
- raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路'})
- devConf = caches['devmgr'].get('settingConf_%s' % (self._device['devNo']))
- if devConf is None:
- conf = self.get_dev_setting()
- caches['devmgr'].set('settingConf_%s' % (self._device['devNo']), conf, 24 * 3600)
- coinElec = conf.get('coinElec', 0.9)
- refundProtection = conf.get('refundProtection', 0)
- refundProtectionTime = conf.get('refundProtectionTime', 5)
- lowPowerDetectionTime = conf.get('lowPowerDetectionTime', 60)
- lowPowerDetectionSwitch = conf.get('lowPowerDetectionSwitch', 0)
- else:
- coinElec = devConf.get('coinElec', 0.9)
- refundProtection = devConf.get('refundProtection', 0)
- refundProtectionTime = devConf.get('refundProtectionTime', 5)
- lowPowerDetectionTime = devConf.get('lowPowerDetectionTime', 60)
- lowPowerDetectionSwitch = devConf.get('lowPowerDetectionSwitch', 0)
- price = float(package['price'])
- port = hex(int(attachParas['chargeIndex']))
- hexPort = fill_2_hexByte(port, 2)
- coins = float(package['coins'])
- hexCoins = fill_2_hexByte(hex(int(coins * 10))) # 注意单位是角
- needElec = round((coins * coinElec) / 10.0, 2) # 单位是0.01度电(配置中每次投币的最大用电量单位却是0.1度电,这里要乘以10)
- hexElec = fill_2_hexByte(hex(int(needElec * 100)), 4)
- unit = package.get('unit', u'分钟')
- needTime = int(package['time'])
- if unit in [u'分钟', u'小时', u'天']:
- billingType = 'time'
- if unit == u'小时':
- needTime = int(package['time']) * 60
- elif unit == u'天':
- needTime = int(package['time']) * 1440
- hexTime = fill_2_hexByte(hex(needTime))
- elif unit == u'度':
- billingType = 'elec'
- needElec = round((package['time']), 2)
- hexElec = fill_2_hexByte(hex(int(needElec * 100)), 4)
- hexTime = fill_2_hexByte(hex(12 * 60))
- else:
- billingType = 'elec'
- devObj = Device.objects.get(devNo = self._device['devNo'])
- elecFee = float(devObj.otherConf.get('elecFee', 0.9))
- needElec = round(min(float(coins / elecFee), needElec), 2)
- hexElec = fill_2_hexByte(hex(int(needElec * 100)), 4)
- hexTime = fill_2_hexByte(hex(12 * 60))
- # 在启动设备前,如果可能是续充,需要获取下设备端口状态,便于后面核实。防止结束报文丢包导致数据不准确
- # 重新把设备上的状态取回来,可以保证needTime数据不出错,正在服务显示也不会有问题
- ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
- lastPortInfo = ctrInfo.get(str(attachParas['chargeIndex']), None)
- if (lastPortInfo is not None) and lastPortInfo.get('status', '') == Const.DEV_WORK_STATUS_WORKING:
- self.get_port_status_from_dev()
- orderNo = attachParas.get('orderNo')
- devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {
- 'IMEI': self._device['devNo'],
- "funCode": '14',
- 'data': hexPort + hexCoins + hexTime + hexElec
- }, timeout = MQTT_TIMEOUT.START_DEVICE)
- if devInfo.has_key('rst') and devInfo['rst'] != 0:
- if devInfo['rst'] == -1:
- raise ServiceException(
- {
- 'result': 2,
- 'description': u'充电桩正在玩命找网络,您的金币还在,重试不需要重新付款,建议您试试旁边其他设备,或者稍后再试哦'
- }
- )
- elif devInfo['rst'] == 1:
- self.check_serial_port_for_startcmd(attachParas['chargeIndex'])
- data = devInfo['data'][6::]
- if data[0:2] == '01': # 表示成功
- pass
- else:
- raise ServiceException({'result': 2, 'description': u'获取端口数据失败,请重试看能否解决'})
- usePort = int(attachParas['chargeIndex'])
- result = data[4:6]
- if result == '01': # 成功
- pass
- elif result == '0B':
- newValue = {str(usePort): {'status': Const.DEV_WORK_STATUS_FAULT, 'statusInfo': u'充电站故障'}}
- Device.update_dev_control_cache(self._device['devNo'], newValue)
- raise ServiceException({'result': 2, 'description': u'充电站故障'})
- elif result == '0C':
- newValue = {str(usePort): {'status': Const.DEV_WORK_STATUS_WORKING, 'statusInfo': u''}}
- Device.update_dev_control_cache(self._device['devNo'], newValue)
- raise ServiceException({'result': 2, 'description': u'该端口正在使用中'})
- start_timestamp = int(time.time())
- portDict = {
- 'startTime': timestamp_to_dt(start_timestamp).strftime('%Y-%m-%d %H:%M:%S'),
- 'status': Const.DEV_WORK_STATUS_WORKING,
- 'coins': float(coins),
- 'isStart': True,
- 'price': price,
- 'openId': openId, 'refunded': False,
- 'billingType': billingType,
- 'refundProtection': refundProtection,
- 'refundProtectionTime': refundProtectionTime,
- 'vCardId': self._vcard_id
- }
- ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
- lastPortInfo = ctrInfo.get(str(usePort), None)
- if lastPortInfo is not None and lastPortInfo.get('status', '') == Const.DEV_WORK_STATUS_WORKING:
- if lastPortInfo.has_key('coins'):
- portDict['coins'] = float(coins) + lastPortInfo['coins']
- if lastPortInfo.has_key('price'):
- portDict['price'] = price + lastPortInfo['price']
- if unit in [u'分钟', u'小时', u'天']:
- portDict.update({'needTime': needTime, 'needElec': needElec})
- if (lastPortInfo is not None) and lastPortInfo.get('status', '') == Const.DEV_WORK_STATUS_WORKING:
- if lastPortInfo.has_key('needTime'):
- portDict['needTime'] = needTime + lastPortInfo['needTime']
- if lastPortInfo.has_key('needElec'):
- portDict['needElec'] = needElec + lastPortInfo['needElec']
- finishedTime = int(time.time()) + portDict['needTime'] * 60
- else:
- portDict.update({'needElec': needElec})
- if (lastPortInfo is not None) and lastPortInfo.has_key('needElec') and lastPortInfo.get('status',
- '') == Const.DEV_WORK_STATUS_WORKING:
- lastPortInfo['needElec'] = needElec + lastPortInfo['needElec']
- finishedTime = 12 * 60 * 60
- if attachParas.has_key('orderNo'):
- portDict.update({'orderNo': attachParas['orderNo']})
- if 'extOrderNo' in attachParas:
- portDict['extOrderNo'] = attachParas.get('extOrderNo')
- devInfo['finishedTime'] = finishedTime
- portDict.update({'finishedTime': finishedTime})
- Device.update_dev_control_cache(self._device['devNo'], {str(usePort): portDict})
- # begin: 低功率检测逻辑
- try:
- if lowPowerDetectionSwitch == 1:
- if openId is not None:
- user = MyUser.objects(openId = openId, groupId = self._device['groupId']).first()
- dealer = Dealer.get_dealer_from_groupId(self._device['groupId'])
- port = str(attachParas['chargeIndex'])
- self.notify_low_power_to_user(user, dealer, port, int(lowPowerDetectionTime))
- except Exception as e:
- logger.error(e)
- # end: 低功率检测逻辑
- return devInfo
- def start_from_api(self, record):
- # type: (APIStartDeviceRecord)->Optional[Dict]
- if 'chargeIndex' not in record.attachParas:
- from apps.web.api.exceptions import ApiParameterError
- raise ApiParameterError(u'请传入充电桩端口号chargeIndex参数')
- return super(ChargingAOQIANGBox, self).start_from_api(record)
- def analyze_event_data(self, data):
- cmdCode = data[4:6]
- #: 提交充电结束状态 (06)
- #: 老版本
- if cmdCode == CMD_CODE.DEVICE_SUBMIT_CHARGE_FINISHED_06:
- port = int(data[8:10], 16)
- leftTime = int(data[10:14], 16)
- reasonCode = data[14:16]
- desc_map = {
- '00': u'购买的充电时间或电量用完了。',
- '01': u'可能是插头被拔掉,或者电瓶已经充满。系统判断为异常断电,由于电瓶车充电器种类繁多,可能存在误差。如有问题,请您及时联系商家协助解决问题并恢复充电。',
- '02': u'恭喜您!电池已经充满电!',
- '0B': u'设备或端口出现问题,为了安全起见,被迫停止工作。建议您根据已经充电的时间评估是否需要到现场换到其他端口充电。'
- }
- return {
- 'status': Const.DEV_WORK_STATUS_IDLE,
- 'cmdCode': cmdCode,
- 'port': port,
- 'leftTime': leftTime,
- 'reason': desc_map[reasonCode],
- 'reasonCode': reasonCode
- }
- #: (高版本的) 提交充电结束状态 (16)
- elif cmdCode == CMD_CODE.DEVICE_SUBMIT_CHARGE_FINISHED_v2_16:
- port = int(data[8:10], 16)
- leftTime = int(data[10:14], 16)
- elec = int(data[14:18], 16) / 100.0
- reasonCode = data[18:20]
- desc_map = {
- '00': u'购买的充电时间或电量用完了。',
- '01': u'可能是插头被拔掉,或者电瓶已经充满。系统判断为异常断电,由于电瓶车充电器种类繁多,可能存在误差。如有问题,请您及时联系商家协助解决问题并恢复充电。',
- '02': u'恭喜您!电池已经充满电!',
- '03': u'警告!您的电池超功率,已经停止充电,为了公共安全,不建议您在该充电桩充电!提醒您,为了安全大功率的电池不要放入楼道、室内等位置充电哦',
- '04': u'远程断电。',
- '0B': u'设备或端口出现问题,为了安全起见,被迫停止工作。建议您根据已经充电的时间评估是否需要到现场换到其他端口充电。'
- }
- desc = desc_map.get(reasonCode, u'电池没有充满!原因未知。')
- return {
- 'status': Const.DEV_WORK_STATUS_IDLE,
- 'cmdCode': cmdCode,
- 'port': port,
- 'leftTime': leftTime,
- 'elec': elec,
- 'reason': desc,
- 'reasonCode': reasonCode
- }
- #: 上传设备故障
- elif cmdCode == CMD_CODE.DEVICE_SUBMIT_DEVICE_FAULT_0A:
- port = int(data[8:10], 16)
- errCode = str(data[10:12]).upper()
- desc = u'设备故障'
- if errCode == 'A0':
- desc = u'停止充电异常,可能是继电器粘连、短路'
- elif errCode == 'A1':
- desc = u'高温'
- elif errCode == 'A2':
- desc = u'低温'
- elif errCode == 'A3':
- desc = u'空载'
- elif errCode == 'A4':
- desc = u'消防(烟感)'
- elif errCode == 'A5':
- desc = u'过载'
- elif errCode == 'A6':
- desc = u'倾斜'
- elif errCode == 'A7':
- desc = u'水压高'
- elif errCode == 'A8':
- desc = u'水压低'
- elif errCode == 'A9':
- desc = u'过压'
- elif errCode == 'AA':
- desc = u'欠压'
- return {
- 'status': Const.DEV_WORK_STATUS_FAULT,
- 'statusInfo': desc,
- 'cmdCode': cmdCode,
- 'port': port,
- 'FaultCode': errCode
- }
- # 用户刷卡上报的信息
- #: 在线卡上传卡号,预扣费
- #: 示例:
- #: CARD_ID CARD_CST CARD_OPE
- #: 0x00000000 0x00 0x00
- #: 参数
- #: CARD_ID : IC 卡卡号
- #: CARD_SURP: 卡片需要改变的金额信息(以角为单位)
- #: CARD_OPE: 0x00 是扣费(减少),0x01 是充值(增加)。
- #: 回复
- #: RES: 0x00,表示扣费成功,0x01 表示余额不足,0x02 表示非法卡。
- #: CARD_SURP: 表示卡余额(以角为单位)。
- elif cmdCode == CMD_CODE.SWIPE_CARD_10:
- group = Group.get_group(self._device['groupId'])
- dealer = Dealer.get_dealer(group['ownerId'])
- agent = Agent.objects(id=dealer['agentId']).first()
- device = Device.objects(devNo=self._device['devNo']).first()
- devType = DeviceType.objects(id=device['devType']['id']).first()
- cardNo = str(int(data[8:16], 16))
- # 兼容以前的老代码, 先走以前的老代码, 如果有新代码再走新的.
- if agent is not None and 'cardNoReverse' in agent.features:
- cardData = data[14:16] + data[12:14] + data[10:12] + data[8:10]
- cardNo = str(int(cardData, 16))
- if 'cardNoReverse' in devType.features:
- if devType.features['cardNoReverse'] is True:
- cardData = data[14:16] + data[12:14] + data[10:12] + data[8:10]
- cardNo = str(int(cardData, 16))
- else:
- cardNo = str(int(data[8:16], 16))
- else:
- pass
- #: 卡里的余额,以角为单位
- preFee = int(data[16:18], 16) / 10.0
- #: 操作符 00(增加) | 01(减少)
- oper = data[18:20]
- return {'cardNo': cardNo, 'preFee': preFee, 'cmdCode': cmdCode, 'oper': oper}
- #: 上报投币打开的信息
- elif cmdCode == CMD_CODE.DEVICE_SUBMIT_OFFLINE_COINS_20: # 启动设备
- port = int(data[8:10], 16)
- needTime = int(data[10:14], 16)
- elec = int(data[14:18], 16) / 100.0
- consumeTypeTemp = data[18:20]
- if consumeTypeTemp == '00':
- consumeType = 'coin'
- elif consumeTypeTemp == '01':
- consumeType = 'card'
- elif consumeTypeTemp == '03':
- consumeType = 'server'
- else:
- consumeType = 'other'
- money = int(data[20:22], 16) / 10.0
- return {
- 'cmdCode': cmdCode,
- 'port': port,
- 'needTime': needTime,
- 'elec': elec,
- 'consumeType': consumeType,
- 'coins': money
- }
- # 4个故障的告警
- elif cmdCode == CMD_CODE.DEVICE_FAULT_FIRE:
- return {"cmdCode": cmdCode, "fault": u"火灾报警"}
- elif cmdCode == CMD_CODE.DEVICE_FAULT_SMOKE:
- return {"cmdCode": cmdCode, "fault": "烟雾报警"}
- elif cmdCode == CMD_CODE.DEVICE_FAULT_TEMPERATURE:
- return {"cmdCode": cmdCode, "fault": "温度超限告警"}
- elif cmdCode == CMD_CODE.DEVICE_FAULT_POWER:
- return {"cmdCode": cmdCode, "fault": "功率超线告警"}
- # 2个状态量上报
- elif cmdCode == CMD_CODE.DEVICE_ELEC:
- elec = int(data[8:12], 16)
- return {"cmdCode": cmdCode, "elec": elec}
- elif cmdCode == CMD_CODE.DEVICE_TEMPERATURE:
- temperature = int(data[8:12], 16)
- return {"cmdCode": cmdCode, "temperature": temperature}
- def get_dev_consume_count(self):
- devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
- {'IMEI': self._device['devNo'], "funCode": '07', 'data': '00'})
- if devInfo.has_key('rst') and devInfo['rst'] != 0:
- if devInfo['rst'] == -1:
- raise ServiceException(
- {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
- elif devInfo['rst'] == 1:
- raise ServiceException(
- {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
- data = devInfo['data'][6::]
- if data[0:2] == '01': # 表示成功
- pass
- else:
- raise ServiceException({'result': 2, 'description': u'获取统计数据失败,请重试看能否解决'})
- cardFee = int(data[2:6], 16) / 10.0 # 以角为单位
- coinFee = int(data[6:10], 16) # 以元为单位
- return {'cardFee': cardFee, 'coinFee': coinFee}
- def get_port_info(self, line):
- data = fill_2_hexByte(hex(int(line)), 2)
- devInfo = MessageSender.send(self.device, self.make_random_cmdcode(),
- {'IMEI': self._device['devNo'], "funCode": '15', 'data': data})
- if devInfo.has_key('rst') and devInfo['rst'] != 0:
- if devInfo['rst'] == -1:
- raise ServiceException(
- {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
- elif devInfo['rst'] == 1:
- raise ServiceException(
- {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
- data = devInfo['data'][6::]
- if data[0:2] == '01': # 表示成功
- pass
- else:
- raise ServiceException({'result': 2, 'description': u'获取统计数据失败,请重试看能否解决'})
- leftTime = int(data[4:8], 16)
- if data[8:12] == 'FFFF':
- power = 0
- else:
- power = int(data[8:12], 16)
- if data[12:16] == 'FFFF':
- elec = 0
- else:
- elec = int(data[12:16], 16)
- if data[16:20] == 'FFFF':
- surp = 0
- else:
- surp = int(data[16:20], 16)
- # return {'port':line,'leftTime':leftTime,'power':power,'leftElec':round(elec*0.01,2),'surp':surp}#剩余电量先不上,客户反馈有点不准
- return {'port': line, 'leftTime': leftTime, 'power': power, 'surp': surp}
- def get_port_status(self, force = False):
- if force:
- return self.get_port_status_from_dev()
- ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
- statusDict = {}
- if not ctrInfo.has_key('allPorts'):
- self.get_port_status_from_dev()
- ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
- allPorts = ctrInfo.get('allPorts', 10)
- # allPorts 有的时候会为0, 这个时候强制设置为10
- if allPorts == 0:
- allPorts = 10
- for ii in range(allPorts):
- tempDict = ctrInfo.get(str(ii + 1), {})
- if tempDict.has_key('status'):
- statusDict[str(ii + 1)] = {'status': tempDict.get('status')}
- elif tempDict.has_key('isStart'):
- if tempDict['isStart']:
- statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_WORKING}
- else:
- statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
- else:
- statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
- allPorts, usedPorts, usePorts = self.get_port_static_info(statusDict)
- Device.update_dev_control_cache(self._device['devNo'],
- {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
- return statusDict
- def get_port_status_from_dev(self):
- devInfo = MessageSender.send(self.device, self.make_random_cmdcode(),
- {'IMEI': self._device['devNo'], "funCode": '0F', 'data': '00'})
- if devInfo.has_key('rst') and devInfo['rst'] != 0:
- if devInfo['rst'] == -1:
- raise ServiceException(
- {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
- elif devInfo['rst'] == 1:
- raise ServiceException(
- {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
- data = devInfo['data'][6::]
- if data[0:2] == '01': # 表示成功
- pass
- else:
- raise ServiceException({'result': 2, 'description': u'获取统计数据失败,请重试看能否解决'})
- result = {}
- portNum = int(data[2:4], 16)
- portData = data[4::]
- ii = 0
- while ii < portNum:
- port = int(portData[ii * 4:ii * 4 + 2], 16)
- statusTemp = portData[ii * 4 + 2:ii * 4 + 4]
- if statusTemp == '01':
- status = {'status': Const.DEV_WORK_STATUS_IDLE}
- elif statusTemp == '02':
- status = {'status': Const.DEV_WORK_STATUS_WORKING}
- elif statusTemp == '03':
- status = {'status': Const.DEV_WORK_STATUS_FORBIDDEN}
- elif statusTemp == '04':
- status = {'status': Const.DEV_WORK_STATUS_FAULT}
- ii += 1
- result[str(port)] = status
- allPorts, usedPorts, usePorts = self.get_port_static_info(result)
- Device.update_dev_control_cache(self._device['devNo'],
- {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
- # 这里存在多线程更新缓存的场景,可能性比较低,但是必须先取出来,然后逐个更新状态,然后再记录缓存
- ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
- for strPort, info in result.items():
- if ctrInfo.has_key(strPort):
- ctrInfo[strPort].update({'status': info['status']})
- else:
- ctrInfo[strPort] = info
- Device.update_dev_control_cache(self._device['devNo'], ctrInfo)
- return result
- def lock_unlock_port(self, port, lock = True):
- lockStr = '00' if lock else '01'
- portStr = fill_2_hexByte(hex(int(port)), 2)
- devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
- {'IMEI': self._device['devNo'], "funCode": '0C', 'data': portStr + lockStr})
- if devInfo.has_key('rst') and devInfo['rst'] != 0:
- if devInfo['rst'] == -1:
- raise ServiceException(
- {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
- elif devInfo['rst'] == 1:
- raise ServiceException(
- {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
- data = devInfo['data'][6::]
- if data[0:2] == '01': # 表示成功
- pass
- else:
- raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'})
- if lock:
- Device.update_dev_control_cache(self._device['devNo'],
- {str(port): {'status': Const.DEV_WORK_STATUS_FORBIDDEN}})
- else:
- Device.update_dev_control_cache(self._device['devNo'], {str(port): {'status': Const.DEV_WORK_STATUS_IDLE}})
- def active_deactive_port(self, port, active):
- if not active:
- self.stop_charging_port(port)
- devInfo = Device.get_dev_control_cache(self._device['devNo'])
- portCtrInfo = devInfo.get(str(port), {})
- portCtrInfo.update({'isStart': False, 'status': Const.DEV_WORK_STATUS_IDLE,
- 'endTime': datetime.datetime.now().strftime(Const.DATETIME_FMT)})
- newValue = {str(port): portCtrInfo}
- Device.update_dev_control_cache(self._device['devNo'], newValue)
- else:
- raise ServiceException({'result': 2, 'description': u'此设备不支持直接打开端口'})
- def stop_charging_port(self, port):
- portStr = fill_2_hexByte(hex(int(port)), 2)
- devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
- {'IMEI': self._device['devNo'], "funCode": '0D', 'data': portStr + '00'})
- if devInfo.has_key('rst') and devInfo['rst'] != 0:
- if devInfo['rst'] == -1:
- raise ServiceException(
- {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
- elif devInfo['rst'] == 1:
- raise ServiceException(
- {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
- data = devInfo['data'][6::]
- if data[0:2] == '01': # 表示成功
- pass
- else:
- raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'})
- port = int(data[2:4], 16)
- leftTime = int(data[4:8], 16)
- return {'port': port, 'leftTime': leftTime}
- # 获取设备配置参数
- def get_dev_setting(self):
- devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
- {'IMEI': self._device['devNo'], "funCode": '1E', 'data': '00'})
- if devInfo.has_key('rst') and devInfo['rst'] != 0:
- if devInfo['rst'] == -1:
- raise ServiceException(
- {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
- elif devInfo['rst'] == 1:
- raise ServiceException(
- {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
- data = devInfo['data'][6::]
- if data[0:2] == '01': # 表示成功
- pass
- else:
- raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'})
- confData = data[2:-2]
- coinMin = int(confData[0:4], 16)
- cardMin = int(confData[4:8], 16)
- coinElec = int(confData[8:10], 16)
- cardElec = int(confData[10:12], 16)
- cst = int(confData[12:14], 16)
- powerMax1 = int(confData[14:18], 16)
- powerMax2 = int(confData[18:22], 16)
- powerMax3 = int(confData[22:26], 16)
- powerMax4 = int(confData[26:30], 16)
- power2Ti = int(confData[30:32], 16)
- power3Ti = int(confData[32:34], 16)
- power4Ti = int(confData[34:36], 16)
- spRecMon = int(confData[36:38], 16)
- spFullEmpty = int(confData[38:40], 16)
- fullPowerMin = int(confData[40:42], 16)
- fullChargeTime = int(confData[42:44], 16) * 10
- elecTimeFirst = int(confData[44:46], 16)
- dev = Device.objects.get(devNo = self._device['devNo'])
- billingType = dev.otherConf.get('billingType', 'time')
- elecFee = dev.otherConf.get('elecFee', 0.9)
- refundProtection = dev.otherConf.get('refundProtection', 0)
- refundProtectionTime = dev.otherConf.get('refundProtectionTime', 5)
- lowPowerDetectionTime = dev.otherConf.get('lowPowerDetectionTime', 60)
- lowPowerDetectionSwitch = dev.otherConf.get('lowPowerDetectionSwitch', 0)
- lowPowerDetectionPower = dev.otherConf.get('lowPowerDetectionPower', 50)
- # 为了预防万一,这里顺便把数据库的数据也更新一下,主要是以设备为准的数据,更新下
- dev.otherConf.update({'cardMin': cardMin})
- try:
- dev.save()
- except Exception, e:
- pass
- resultDict = {
- 'coinMin': coinMin,
- 'cardMin': cardMin,
- 'coinElec': coinElec,
- 'cardElec': cardElec,
- 'cst': cst,
- 'powerMax1': powerMax1,
- 'powerMax2': powerMax2,
- 'powerMax3': powerMax3,
- 'powerMax4': powerMax4,
- 'power2Ti': power2Ti,
- 'power3Ti': power3Ti,
- 'power4Ti': power4Ti,
- 'spRecMon': spRecMon,
- 'spFullEmpty': spFullEmpty,
- 'fullPowerMin': fullPowerMin,
- 'fullChargeTime': fullChargeTime,
- 'elecTimeFirst': elecTimeFirst,
- 'billingType': billingType,
- 'elecFee': elecFee,
- 'refundProtection': refundProtection,
- 'refundProtectionTime': refundProtectionTime,
- 'lowPowerDetectionTime': lowPowerDetectionTime,
- 'lowPowerDetectionSwitch': lowPowerDetectionSwitch,
- 'lowPowerDetectionPower': lowPowerDetectionPower
- }
- consumeInfo = self.get_dev_consume_count()
- resultDict.update(consumeInfo)
- setting2Info = self.get_dev_settings2()
- resultDict.update(setting2Info)
- return resultDict
- # 设置设备配置参数
- def set_dev_setting(self, setConf):
- data = ''
- data += fill_2_hexByte(hex(int(setConf['coinMin'])), 4)
- data += fill_2_hexByte(hex(int(setConf['cardMin'])), 4)
- data += fill_2_hexByte(hex(int(setConf['coinElec'])), 2)
- data += fill_2_hexByte(hex(int(setConf['cardElec'])), 2)
- data += fill_2_hexByte(hex(int(setConf['cst'])), 2)
- data += fill_2_hexByte(hex(int(setConf['powerMax1'])), 4)
- data += fill_2_hexByte(hex(int(setConf['powerMax2'])), 4)
- data += fill_2_hexByte(hex(int(setConf['powerMax3'])), 4)
- data += fill_2_hexByte(hex(int(setConf['powerMax4'])), 4)
- data += fill_2_hexByte(hex(int(setConf['power2Ti'])), 2)
- data += fill_2_hexByte(hex(int(setConf['power3Ti'])), 2)
- data += fill_2_hexByte(hex(int(setConf['power4Ti'])), 2)
- data += fill_2_hexByte(hex(int(setConf['spRecMon'])), 2)
- data += fill_2_hexByte(hex(int(setConf['spFullEmpty'])), 2)
- data += fill_2_hexByte(hex(int(setConf['fullPowerMin'])), 2)
- data += fill_2_hexByte(hex(int(setConf['fullChargeTime'])), 2)
- data += fill_2_hexByte(hex(int(setConf['elecTimeFirst'])), 2)
- devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
- {'IMEI': self._device['devNo'], "funCode": '18', 'data': data})
- if devInfo.has_key('rst') and devInfo['rst'] != 0:
- if devInfo['rst'] == -1:
- raise ServiceException(
- {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
- elif devInfo['rst'] == 1:
- raise ServiceException(
- {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
- data = devInfo['data'][6::]
- if data[0:2] == '01': # 表示成功
- dev = Device.objects.get(devNo = self._device['devNo'])
- dev.otherConf.update({'billingType': setConf['billingType']})
- dev.otherConf.update({'elecFee': setConf['elecFee']})
- dev.otherConf.update({'refundProtection': setConf['refundProtection']})
- dev.otherConf.update({'refundProtectionTime': setConf['refundProtectionTime']})
- dev.otherConf.update({'cardMin': setConf['cardMin']})
- dev.otherConf.update({'lowPowerDetectionTime': setConf['lowPowerDetectionTime']})
- dev.otherConf.update({'lowPowerDetectionSwitch': setConf['lowPowerDetectionSwitch']})
- dev.otherConf.update({'lowPowerDetectionPower': setConf['lowPowerDetectionPower']})
- dev.save()
- else:
- raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'})
- # 设置额外的参数
- def set_dev_setting2(self, setConf):
- data = fill_2_hexByte(hex(int(setConf['stopDelay'])), 2)
- data += fill_2_hexByte(hex(int(setConf['maxTotalTime']) / 10), 2)
- data += fill_2_hexByte(hex(int(setConf['overVoltage'])), 4)
- data += fill_2_hexByte(hex(int(setConf['lowVoltage'])), 4)
- data += fill_2_hexByte(hex(int(setConf['overTemp'])), 2)
- data += fill_2_hexByte(hex(int(setConf['overTempDelayTime'])), 2)
- data += str(setConf['timeDspOn']).replace(':', '')
- data += str(setConf['timeDspOff']).replace(':', '')
- devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
- {'IMEI': self._device['devNo'], "funCode": '22', 'data': data})
- if devInfo.has_key('rst') and devInfo['rst'] != 0:
- if devInfo['rst'] == -1:
- raise ServiceException(
- {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
- elif devInfo['rst'] == 1:
- raise ServiceException(
- {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
- def get_dev_settings2(self):
- devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
- {'IMEI': self._device['devNo'], "funCode": '23', 'data': '00'})
- if devInfo.has_key('rst') and devInfo['rst'] != 0:
- if devInfo['rst'] == -1:
- raise ServiceException(
- {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
- elif devInfo['rst'] == 1:
- raise ServiceException(
- {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'})
- data = devInfo['data']
- result = {}
- result['stopDelay'] = int(data[8:10], 16)
- result['maxTotalTime'] = int(data[10:12], 16) * 10
- result['overVoltage'] = int(data[12:16], 16)
- result['lowVoltage'] = int(data[16:20], 16)
- result['overTemp'] = int(data[20:22], 16)
- result['overTempDelayTime'] = int(data[22:24], 16)
- # 处理返回数据为 'HH:MM' 格式的字符串
- timeDspOn = int(data[24:28]) if data[24:28].isdigit() is True else None
- timeDspOff = int(data[28:32]) if data[28:32].isdigit() is True else None
- timeDspOnList = list(str(timeDspOn))
- timeDspOffList = list(str(timeDspOff))
- timeDspOnList.insert(-2, ':')
- timeDspOffList.insert(-2, ':')
- result['timeDspOn'] = ''.join(timeDspOnList) if len(timeDspOnList) == 5 else '0' + ''.join(timeDspOnList)
- result['timeDspOff'] = ''.join(timeDspOffList) if len(timeDspOffList) == 5 else '0' + ''.join(timeDspOffList)
- return result
- def response_use_card(self, res, balance):
- data = '55061001'
- data = data + res + fill_2_hexByte(hex(int(balance * 10)), 4)
- devInfo = MessageSender.send(device = self.device,
- cmd = DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE,
- payload = {
- 'IMEI': self._device['devNo'], "funCode": '10', 'data': data
- })
- if devInfo.has_key('rst') and devInfo['rst'] != 0:
- if devInfo['rst'] == -1:
- raise ServiceException(
- {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'})
- elif devInfo['rst'] == 1: # 等于1的时候,说明服务器和远程模块通讯OK,响应已经收到,不需要异常处理
- pass
- def set_device_function_param(self, request, lastSetConf):
- coinMin = request.POST.get('coinMin', None)
- cardMin = request.POST.get('cardMin', None)
- coinElec = request.POST.get('coinElec', None)
- cardElec = request.POST.get('cardElec', None)
- cst = request.POST.get('cst', None)
- powerMax1 = request.POST.get('powerMax1', None)
- powerMax2 = request.POST.get('powerMax2', None)
- powerMax3 = request.POST.get('powerMax3', None)
- powerMax4 = request.POST.get('powerMax4', None)
- power2Ti = request.POST.get('power2Ti', None)
- power3Ti = request.POST.get('power3Ti', None)
- power4Ti = request.POST.get('power4Ti', None)
- spRecMon = request.POST.get('spRecMon', None)
- spFullEmpty = request.POST.get('spFullEmpty', None)
- fullPowerMin = request.POST.get('fullPowerMin', None)
- fullChargeTime = request.POST.get('fullChargeTime', None)
- elecTimeFirst = request.POST.get('elecTimeFirst', None)
- billingType = request.POST.get('billingType', None)
- elecFee = request.POST.get('elecFee', None)
- refundProtection = request.POST.get('refundProtection', None)
- refundProtectionTime = request.POST.get('refundProtectionTime', None)
- lowPowerDetectionTime = request.POST.get('lowPowerDetectionTime', None)
- lowPowerDetectionSwitch = request.POST.get('lowPowerDetectionSwitch', None)
- lowPowerDetectionPower = request.POST.get('lowPowerDetectionPower', None)
- # 这几个是参数是奥强特有的
- stopDelay = request.POST.get('stopDelay', None)
- maxTotalTime = request.POST.get('maxTotalTime', None)
- overVoltage = request.POST.get('overVoltage', None)
- lowVoltage = request.POST.get('lowVoltage', None)
- overTemp = request.POST.get('overTemp', None)
- overTempDelayTime = request.POST.get('overTempDelayTime', None)
- timeDspOn = request.POST.get('timeDspOn', None)
- timeDspOff = request.POST.get('timeDspOff', None)
- # 这几个参数是墨小智V3特有的
- stWTime = request.POST.get('stWTime', None)
- temThre = request.POST.get('temThre', None)
- freeUse = request.POST.get('freeUse', None)
- if coinMin:
- lastSetConf.update({'coinMin': int(coinMin)})
- if cardMin:
- lastSetConf.update({'cardMin': int(cardMin)})
- if coinElec:
- lastSetConf.update({'coinElec': int(coinElec)})
- if cardElec:
- lastSetConf.update({'cardElec': int(cardElec)})
- if cst:
- lastSetConf.update({'cst': int(cst)})
- if powerMax1:
- lastSetConf.update({'powerMax1': int(powerMax1)})
- if powerMax2:
- lastSetConf.update({'powerMax2': int(powerMax2)})
- if powerMax3:
- lastSetConf.update({'powerMax3': int(powerMax3)})
- if powerMax4:
- lastSetConf.update({'powerMax4': int(powerMax4)})
- if power2Ti:
- lastSetConf.update({'power2Ti': int(power2Ti)})
- if power3Ti:
- lastSetConf.update({'power3Ti': int(power3Ti)})
- if power4Ti:
- lastSetConf.update({'power4Ti': int(power4Ti)})
- if spRecMon:
- lastSetConf.update({'spRecMon': int(spRecMon)})
- if spFullEmpty:
- lastSetConf.update({'spFullEmpty': int(spFullEmpty)})
- if fullPowerMin:
- lastSetConf.update({'fullPowerMin': int(fullPowerMin)})
- if fullChargeTime:
- lastSetConf.update({'fullChargeTime': int(fullChargeTime)})
- if elecTimeFirst:
- lastSetConf.update({'elecTimeFirst': int(elecTimeFirst)})
- if billingType:
- lastSetConf.update({'billingType': billingType})
- if elecFee:
- lastSetConf.update({'elecFee': elecFee})
- if refundProtection:
- lastSetConf.update({'refundProtection': int(refundProtection)})
- if refundProtectionTime:
- lastSetConf.update({'refundProtectionTime': int(refundProtectionTime)})
- if lowPowerDetectionTime:
- lastSetConf.update({'lowPowerDetectionTime': int(lowPowerDetectionTime)})
- if lowPowerDetectionSwitch:
- lastSetConf.update({'lowPowerDetectionSwitch': int(lowPowerDetectionSwitch)})
- if lowPowerDetectionPower:
- lastSetConf.update({'lowPowerDetectionPower': int(lowPowerDetectionPower)})
- # 这几个参数是奥强特有的
- if stopDelay:
- lastSetConf.update({'stopDelay': stopDelay})
- if maxTotalTime:
- lastSetConf.update({'maxTotalTime': maxTotalTime})
- if overVoltage:
- lastSetConf.update({'overVoltage': overVoltage})
- if lowVoltage:
- lastSetConf.update({'lowVoltage': lowVoltage})
- if overTemp:
- lastSetConf.update({'overTemp': overTemp})
- if overTempDelayTime:
- lastSetConf.update({'overTempDelayTime': overTempDelayTime})
- if timeDspOn:
- lastSetConf.update({'timeDspOn': timeDspOn})
- if timeDspOff:
- lastSetConf.update({'timeDspOff': timeDspOff})
- # 这几个参数是墨小智V3特有的
- if stWTime:
- lastSetConf.update({'stWTime': int(stWTime)})
- if temThre:
- lastSetConf.update({'temThre': int(temThre)})
- if freeUse:
- lastSetConf.update({'freeUse': int(freeUse)})
- self.set_dev_setting(lastSetConf)
- self.set_dev_setting2(lastSetConf)
- def handle_clear_start_error(self, port):
- dealer = Dealer.objects.get(id = self._device["ownerId"])
- if not dealer or not dealer.managerialOpenId:
- return
- group = Group.get_group(self._device["groupId"])
- self.lock_unlock_port(port, lock = False)
- notifyData = {
- "title": "设备故障报警解除",
- "device": u"{gNum}组-{lc}-{port}端口".format(gNum = self._device["groupNumber"],
- lc = self._device["logicalCode"],
- port = port),
- "location": u"{address}-{groupName}".format(address = group["address"], groupName = group["groupName"]),
- "fault": u"当前端口已被正常启动,端口故障解除",
- "notifyTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
- }
- task_caller(
- func_name = 'report_to_dealer_via_wechat',
- openId = dealer.managerialOpenId,
- dealerId = str(dealer.id),
- templateName = "device_fault",
- **notifyData
- )
- self.lock_unlock_port(port, lock = False)
- def handle_out_start_error(self, port):
- dealer = Dealer.objects.get(id = self._device["ownerId"])
- if not dealer or not dealer.managerialOpenId:
- return
- group = Group.get_group(self._device["groupId"])
- times = Device.get_error_start_times(self._device["devNo"], port)
- self.lock_unlock_port(port, lock = True)
- notifyData = {
- "title": u"注意,您的设备可能发生故障!",
- "device": u"{gNum}组-{lc}-{port}端口".format(gNum = self._device["groupNumber"],
- lc = self._device["logicalCode"],
- port = port),
- "location": u"{address}-{groupName}".format(address = group["address"], groupName = group["groupName"]),
- "fault": u"当前设备端口连续启动 {times} 失败,目前该端口已经被自动禁用,请注意该端口是否发生故障".format(times = times),
- "notifyTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
- }
- task_caller(
- func_name = 'report_to_dealer_via_wechat',
- openId = dealer.managerialOpenId,
- dealerId = str(dealer.id),
- templateName = "device_fault",
- **notifyData
- )
- # 麦总的告警3次触发,同时锁定设备,为避免解锁设备之后 再次触发 这个地方需要将其缓存清空
- Device.delete_error_start_times(self._device["devNo"], port)
|