# -*- coding: utf-8 -*- # !/usr/bin/env python import datetime import logging import time from decimal import Decimal from typing import TYPE_CHECKING from apilib.utils import is_number from apilib.monetary import RMB from apilib.utils_string import split_str from apps.web.constant import Const, DeviceCmdCode, ErrorCode, DeviceErrorCodeDesc, MQTT_TIMEOUT, CONSUMETYPE from apps.web.core.adapter.base import SmartBox, fill_2_hexByte, make_six_bytes_session_id from apps.web.core.exceptions import ServiceException, InvalidParameter from apps.web.core.networking import MessageSender from apps.web.device.models import Device, DeviceType from apps.web.south_intf.yuhuan_fire import YuhuanNorther logger = logging.getLogger(__name__) if TYPE_CHECKING: from apps.web.device.models import GroupDict class ChargingZHIXIA2Box(SmartBox): def __init__(self, device): super(ChargingZHIXIA2Box, self).__init__(device) def _start_device_for_card(self, needTime, portStr): """ 刷卡启动 :param needTime: :param portStr: :return: """ hexTime = "{:04X}".format(int(needTime)) hexPort = "{:02X}".format(int(portStr)) MessageSender.send( device = self.device, cmd = DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_SYNC, payload = { 'IMEI': self._device['devNo'], "funCode": '02', 'data': 'EE0D02{}'.format(make_six_bytes_session_id()) + hexPort + '0000' + hexTime }, timeout = MQTT_TIMEOUT.START_DEVICE ) def _check_package(self, package): """ 获取设备启动的发送数据 根据设备的当前模式以及套餐获取 :param package: :return: """ consumeModule = self._device.get("otherConf", dict()).get("consumeModule", 0) unit = package.get("unit", u"分钟") _time = float(package.get("time", 0)) # 按时间计费 if consumeModule == 0: billingType = "time" if unit == u"小时": _time = _time * 60 elif unit == u"天": _time = _time * 24 * 60 elif unit == u"秒": _time = _time / 60 elif unit == u"分钟": _time = _time else: raise ServiceException({"result": 2, "description": u"套餐单位错误,请联系经销商"}) # 按电量计费 else: billingType = "elec" if unit != u"度": raise ServiceException({"result": 2, "description": u"套餐单位错误,请联系经销商"}) else: _time = _time * 100 return _time, unit, billingType def translate_funcode(self, funCode): funCodeDict = { '01': u'查询设备每个端口当前的状态', '02': u'移动支付', '0C': u'获取设备设置', '06': u'获取设备端口详情', '07': u'获取刷卡投币统计数据', '08': u'设置设备参数', '09': u'设置刷卡投币使能', '0A': u'端口使能', '0B': u'端口关闭', '16': u'设置设备参数', '20': u'设备重启', '13': u'设置卡充满退费', '15': u'获取功率费率配置', '14': u'设置功率费率配置', '12': u'充值', '22': u'回复卡充值', } return funCodeDict.get(funCode, '') def translate_event_cmdcode(self, cmdCode): cmdDict = { '03': u'投币上报', '04': u'刷卡上报', '05': u'充电结束', '16': u'充电结束', '10': u'刷卡上报', '0D': u'故障', '20': u'启动设备', '11': u'刷卡使用', '12': u'卡充值', '22': u'ID卡扣费', '23': u'ID卡扣费状态', '17': u'IC卡回收余额成功', } return cmdDict.get(cmdCode, '') def is_port_can_use(self, port, canAdd=False): # 电川的在启动的时候去判断是否需要续充 return True, '' def check_dev_status(self, attachParas=None): """ 如果超过两个心跳周期没有报心跳,并且最后一次更新时间在2个小时内,需要从设备获取状态 否则以缓存状态为准。 :param attachParas: :return: """ if attachParas is None: raise ServiceException({'result': 0, 'description': u'请您选择合适的充电端口、电池类型信息'}) if not attachParas.has_key('chargeIndex'): raise ServiceException({'result': 0, 'description': u'请您选择合适的充电端口'}) if not self.device.need_fetch_online: raise ServiceException( {'result': 2, 'description': DeviceErrorCodeDesc.get(ErrorCode.DEVICE_CONN_CHECK_FAIL)}) self.get_port_status_from_dev() group = self.device.group # type: GroupDict if group.is_free: logger.debug('{} is free. no need to check continue pay.'.format(repr(self.device))) return # 处理是否能够续充 portDict = self.get_port_status() port = str(attachParas['chargeIndex']) if port in portDict: isCanAdd = self.device['devType'].get('payableWhileBusy', False) if portDict[port]['status'] == Const.DEV_WORK_STATUS_IDLE: return elif portDict[port]['status'] == Const.DEV_WORK_STATUS_FAULT: raise ServiceException({'result': 0, 'description': u'该端口故障,暂时不能使用'}) elif portDict[port]['status'] == Const.DEV_WORK_STATUS_WORKING: if isCanAdd: return else: raise ServiceException({'result': 0, 'description': u'该端口正在工作不能使用,请您使用其他端口'}) elif portDict[port]['status'] == Const.DEV_WORK_STATUS_FORBIDDEN: raise ServiceException({'result': 0, 'description': u'该端口已被禁止使用,请您使用其他端口'}) elif portDict[port]['status'] == Const.DEV_WORK_STATUS_CONNECTED: return else: raise ServiceException({'result': 0, 'description': u'端口状态未知,暂时不能使用'}) else: raise ServiceException({'result': 0, 'description': u'未知端口,暂时不能使用'}) def test(self, coins): hexPort = fill_2_hexByte(hex(int(1)), 2) hexTime = fill_2_hexByte(hex(5)) devInfo = MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, payload = { 'IMEI': self._device['devNo'], "funCode": '02', 'data': hexPort + '0000' + hexTime }, timeout = MQTT_TIMEOUT.SHORT) return devInfo def port_is_busy(self, port_dict): if not port_dict: return False if 'billingType' not in port_dict: return False if 'status' not in port_dict: return False if 'coins' not in port_dict: return False if 'price' not in port_dict: return False if port_dict['billingType'] not in ['time', 'elec']: return False if port_dict['billingType'] == 'time': if 'needTime' not in port_dict: return False else: if 'needElec' not in port_dict: return False if port_dict['status'] == Const.DEV_WORK_STATUS_WORKING: return True else: return False 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'请您选择合适的充电线路'}) price = float(package['price']) port = hex(int(attachParas['chargeIndex'])) hexPort = fill_2_hexByte(port, 2) _time, unit, billingType = self._check_package(package) coins = float(package['coins']) hexTime = fill_2_hexByte(hex(int(_time))) devInfo = MessageSender.send( device = self.device, cmd = DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_SYNC, payload = { 'IMEI': self._device['devNo'], "funCode": '02', 'data': 'EE0D02{}'.format(make_six_bytes_session_id()) + hexPort + '0000' + hexTime }, timeout = MQTT_TIMEOUT.START_DEVICE) usePort = int(attachParas['chargeIndex']) if 'rst' not in devInfo: raise ServiceException( { 'result': 2, 'description': u'启动设备失败,您的支付金额已经退还,请重新扫码设备试试(1001)' }) if 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']) devInfo['rst'] = ErrorCode.SUCCESS else: raise ServiceException( { 'result': 2, 'description': u'启动设备失败,您的支付金额已经退回,请重新扫码设备({})'.format(devInfo['rst']) }) else: if 'data' not in devInfo: logger.warning('no data in success response. result = {}'.format(str(devInfo))) raise ServiceException( { 'result': 2, 'description': u'启动设备失败,您的支付金额已经退回,请重试' }) else: data = devInfo['data'][18::] result = data[2:4] if result == '01': # 成功 pass elif result == '02': 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 == '03': 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'该端口正在使用中'}) else: raise ServiceException({ 'result': 2, 'description': u'启动设备失败({}),您的支付金额已经退回,请重新扫码设备'.format(result) }) portDict = { 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'status': Const.DEV_WORK_STATUS_WORKING, 'coins': coins, 'price': price, 'billingType': billingType, 'isStart': True, 'openId': openId, 'refunded': False, 'vCardId': self._vcard_id, 'payInfo': list() } ctrInfo = Device.get_dev_control_cache(self._device.devNo) lastPortInfo = ctrInfo.get(str(usePort), {}) if self.port_is_busy(lastPortInfo) and \ lastPortInfo.get('billingType') == billingType and \ lastPortInfo.get('openId') == openId: is_continus = True portDict['coins'] = float(coins) + lastPortInfo['coins'] portDict['price'] = float(price) + lastPortInfo['price'] else: is_continus = False portDict['coins'] = float(coins) portDict['price'] = float(price) if attachParas.get('redpackId'): if is_continus: redpackInfo = lastPortInfo.get("redpackInfo", list()) if not redpackInfo: logger.warning("miss redpackInfo! {}-{}".format(self._device["devNo"], usePort)) else: redpackInfo = list() redpackInfo.append({'redpackId': str(attachParas['redpackId'])}) portDict['redpackInfo'] = redpackInfo else: if is_continus: portDict['redpackInfo'] = lastPortInfo.get("redpackInfo", list()) else: portDict['redpackInfo'] = list() if 'linkedRechargeRecordId' in attachParas and attachParas.get('isQuickPay', False): if is_continus: payInfo = lastPortInfo.get("payInfo", list()) if not payInfo: logger.warning("miss payInfo! {}-{}".format(self._device["devNo"], usePort)) else: payInfo = list() payInfo.append({'rechargeRcdId': str(attachParas['linkedRechargeRecordId'])}) portDict['payInfo'] = payInfo else: if is_continus: portDict['payInfo'] = lastPortInfo.get("payInfo", list()) else: portDict['payInfo'] = list() if billingType == 'time': if is_continus: portDict['needTime'] = _time + lastPortInfo['needTime'] else: portDict['needTime'] = _time finishedTime = int(time.time()) + int(portDict['needTime'] * 60) else: if is_continus: portDict['needElec'] = _time / 100.0 + lastPortInfo['needElec'] else: portDict['needElec'] = _time / 100.0 finishedTime = int(time.time()) + 60 * 60 * 12 portDict.update({'finishedTime': finishedTime}) if 'orderNo' in attachParas: portDict.update({'orderNo': attachParas['orderNo']}) Device.update_dev_control_cache( self._device['devNo'], { str(usePort): portDict }) devInfo['finishedTime'] = finishedTime # 玉环的对接,放这里吧 YuhuanNorther.send_dev_status(self._device, attachParas['chargeIndex'], '1') return devInfo def analyze_event_data(self, data): cmdCode = data[4:6] if cmdCode == '05': port = int(data[18:20], 16) leftTime = int(data[20:24], 16) reason = data[24:26] devType = DeviceType.objects(id=self.device['devType']['id']).first() finishedReasonDict = devType.finishedReasonDict if finishedReasonDict != {}: desc_map = finishedReasonDict else: desc_map = { '00': u'购买的充电时间或电量用完了。', '01': u'插头被拔或者松动,或者电瓶已经充满(系统判断为异常断电,电瓶车充电器种类繁多,可能存在误差)', '02': u'电池已经充满', '03': u'设备或者端口故障。建议您根据已经充电的时间评估是否需要到现场换到其他端口充电', '04': u'警告!您的电池功率超过本机最大限制。为了公共安全,不建议您在该充电桩充电', '05': u'刷卡退费结束', '06': u'可能是插头被拔掉或者未连接充电器。如果不是自己操作,建议您到现场检查是否有人误操作', '07': u'远程方式停止充电。如果不是自己操作,建议到现场尽快恢复充电' } desc = desc_map.get(reason, u'电池没有充满!原因未知。') # 刷卡充电的结束 if len(data) > 34: cardNo = str(int(data[26:34], 16)) backMoney = int(data[34:36], 16) / 10.0 cardType = 'ID' if data[36:40] == 'AA33' else 'IC' if cardNo != str(0): return {'status': Const.DEV_WORK_STATUS_IDLE, 'cmdCode': cmdCode, 'port': port, 'leftTime': leftTime, 'reason': desc, 'isStart': False, 'cardNo': cardNo, 'backMoney': backMoney, 'cardType': cardType, 'endType': reason, 'reasonCode': reason, 'uartData': data} else: return {'status': Const.DEV_WORK_STATUS_IDLE, 'cmdCode': cmdCode, 'port': port, 'leftTime': leftTime, 'reason': desc, 'isStart': False, 'endType': reason, 'reasonCode': reason, 'uartData': data} # 非刷卡充电的结束 else: return {'status': Const.DEV_WORK_STATUS_IDLE, 'cmdCode': cmdCode, 'port': port, 'leftTime': leftTime, 'reason': desc, 'isStart': False, 'endType': reason, 'reasonCode': reason, 'uartData': data} elif cmdCode == '0D': port = int(data[16:18], 16) errCode = data[18:20] if errCode == '01': return {'statusInfo': u'端口输出故障', 'cmdCode': cmdCode, 'port': port, 'FaultCode': errCode, "uart": data} elif errCode == '02': return {'statusInfo': u'机器整体充电功率过大', 'cmdCode': cmdCode, 'port': port, 'FaultCode': errCode, "uart": data} elif errCode == '03': return {'statusInfo': u'电源故障', 'cmdCode': cmdCode, 'port': port, 'FaultCode': errCode, "uart": data} elif cmdCode == '03': # 投币上报 coin = int(data[18:20], 16) try: port = int(data[20:22], 16) except: port = None return {'cmdCode': cmdCode, 'coins': coin, 'port': port} elif cmdCode == '11': # 刷卡后,按下端口,上报的报文 cardNo = int(data[18:26], 16) cardNo = str(cardNo) fee = int(data[26:28], 16) / 10.0 balance = int(data[28:32], 16) / 10.0 cardType = 'ID' if data[32:36] == 'AA33' else 'IC' port = int(data[36:38], 16) status = data[38:40] return {'cardNo': cardNo, 'fee': fee, 'balance': balance, 'cardType': cardType, 'port': port, 'status': status, 'cmdCode': cmdCode} elif cmdCode == '12': cardNo = data[18:26] cardNo = str(int(cardNo, 16)) balance = int(data[26:30], 16) / 10.0 cardType = 'ID' if data[30:34] == 'AA33' else 'IC' return {'cmdCode': cmdCode, 'balance': balance, 'cardType': cardType, 'cardNo': cardNo} elif cmdCode == '04': # 旧版本的刷卡报文 money = int(data[18:20], 16) return {'cmdCode': cmdCode, 'money': money} elif cmdCode == '30': # 宇泽家的,主动上报电流 portData = data[18:48] port1 = int(portData[2:4]) port2 = int(portData[4:6]) port3 = int(portData[6:8]) port4 = int(portData[8:10]) port5 = int(portData[10:12]) port6 = int(portData[12:14]) port7 = int(portData[14:16]) port8 = int(portData[16:18]) port9 = int(portData[18:20]) port10 = int(portData[20:22]) device = int(portData[22:24]) return { 'cmdCode': cmdCode, 'port1': port1, 'port2': port2, 'port3': port3, 'port4': port4, 'port5': port5, 'port6': port6, 'port7': port7, 'port8': port8, 'port9': port9, 'port10': port10, 'device': device, } elif cmdCode == '31': # 宇泽家的,主动上报温度 portData = data[18:48] port1 = int(portData[2:4]) port2 = int(portData[4:6]) port3 = int(portData[6:8]) port4 = int(portData[8:10]) port5 = int(portData[10:12]) port6 = int(portData[12:14]) port7 = int(portData[14:16]) port8 = int(portData[16:18]) port9 = int(portData[18:20]) port10 = int(portData[20:22]) device = int(portData[22:24]) return { 'cmdCode': cmdCode, 'port1': port1, 'port2': port2, 'port3': port3, 'port4': port4, 'port5': port5, 'port6': port6, 'port7': port7, 'port8': port8, 'port9': port9, 'port10': port10, 'device': device, } elif cmdCode == '32': # 宇泽家的,主动上报温度 if data[18:20] == '01': return {'isYangan': True, 'cmdCode': cmdCode} else: return {'isYangan': False, 'cmdCode': cmdCode} elif cmdCode == '17': """ IC卡回收余额成功,上报回收的数目 样例 660F170048614800052DDA5A0D000001DD """ cardNo = int(data[18:26], 16) cardNo = str(cardNo) backMoney = int(data[26:30], 16) / 10.0 return {'cmdCode': cmdCode, 'cardNo': cardNo, 'backMoney': backMoney} elif cmdCode == '22': # ID卡,上报上来扣费 cardNo = int(data[18:26], 16) cardNo = str(cardNo) fee = int(data[26:28], 16) / 10.0 cardType = 'ID' if data[28:32] == 'AA33' else 'IC' return {'cmdCode': cmdCode, 'cardNo': cardNo, 'fee': fee, 'cardType': cardType} elif cmdCode == '23': # ID卡,上报上来扣费 cardNo = int(data[18:26], 16) cardNo = str(cardNo) status = data[26:28] return {'cmdCode': cmdCode, 'cardNo': cardNo, 'status': status} elif cmdCode == '35': temperature = int(data[18:20], 16) smoke = int(data[20:22], 16) voltage = int(data[22:26], 16) return {'cmdCode': cmdCode, 'temperature': temperature, 'smokeWarning': (smoke == 0x01), 'voltage': voltage} elif cmdCode == '41': return {'cmdCode': cmdCode, 'smokeWarning': True} elif cmdCode == "2C": # 中润易和独有 参数意义分别是卡号 端口号 刷卡次数 return {"cmdCode": cmdCode, "cardNo": str(int(data[18:26], 16)), "portStr": str(int(data[26:28], 16)), "num": int(data[28:30], 16)} 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'][16::] 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): support_06 = self._device.get('otherConf', {}).get('support_06', True) if not support_06: return {'port': line} data = fill_2_hexByte(hex(int(line)), 2) devInfo = MessageSender.send(self.device, self.make_random_cmdcode(), {'IMEI': self._device['devNo'], "funCode": '06', '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'][16::] leftTime = int(data[4:8], 16) if data[8:12] == 'FFFF': power = 0 else: power = int(data[8:12], 16) / 10.0 data = {"port": line, "power": power} # 电量计费模式 if not self._device.get("otherConf", dict()).get("consumeModule", 0): data.update({"leftTime": leftTime}) else: data.update({"leftElec": leftTime / 100.0}) if self.device.bill_as_service_feature.on: ctrInfo = Device.get_dev_control_cache(self._device['devNo']) needElec = ctrInfo[line]['needElec'] usedElec = needElec - leftTime / 100.0 elecCharge = self.device.bill_as_service_feature.elec_charge serviceCharge = self.device.bill_as_service_feature.service_charge elecFee = float(elecCharge * usedElec) serviceFee = float(serviceCharge * usedElec) consumeMoney = elecFee + serviceFee data.update({"elecFee": elecFee, "serviceFee" : serviceFee,'consumeMoney':consumeMoney}) return data # 访问设备电流 def get_port_elec_from_dev(self): devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], "funCode": '21', '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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) portData = devInfo['data'][18::] port1 = int(portData[2:4], 16) port2 = int(portData[4:6], 16) port3 = int(portData[6:8], 16) port4 = int(portData[8:10], 16) port5 = int(portData[10:12], 16) port6 = int(portData[12:14], 16) port7 = int(portData[14:16], 16) port8 = int(portData[16:18], 16) port9 = int(portData[18:20], 16) port10 = int(portData[20:22], 16) device = int(portData[22:24], 16) return { 'port1': port1, 'port2': port2, 'port3': port3, 'port4': port4, 'port5': port5, 'port6': port6, 'port7': port7, 'port8': port8, 'port9': port9, 'port10': port10, 'device': device, } # 访问设备 def get_port_temperature_from_dev(self): devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], "funCode": '22', '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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) portData = devInfo['data'][18::] port1 = int(portData[2:4], 16) port2 = int(portData[4:6], 16) port3 = int(portData[6:8], 16) port4 = int(portData[8:10], 16) port5 = int(portData[10:12], 16) port6 = int(portData[12:14], 16) port7 = int(portData[14:16], 16) port8 = int(portData[16:18], 16) port9 = int(portData[18:20], 16) port10 = int(portData[20:22], 16) device = int(portData[22:24], 16) return { 'port1': port1, 'port2': port2, 'port3': port3, 'port4': port4, 'port5': port5, 'port6': port6, 'port7': port7, 'port8': port8, 'port9': port9, 'port10': port10, 'device': device, } # 访问设备,获取设备端口信息 def get_port_status_from_dev(self): devInfo = MessageSender.send(device = self.device, cmd = DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_SYNC, payload = { 'IMEI': self._device['devNo'], 'funCode': '01', 'data': 'EE0901{}'.format(make_six_bytes_session_id()) + '00' }) if devInfo.has_key('rst') and devInfo['rst'] != 0: if devInfo['rst'] == -1: raise ServiceException( {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试', 'rst': -1}) elif devInfo['rst'] == 1: raise ServiceException( {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能', 'rst': 1}) data = devInfo['data'][16::] result = {} portNum = int(data[2:4], 16) portData = data[4::] ii = 0 while ii < portNum: statusTemp = portData[ii * 2:ii * 2 + 2] 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} # 不再上述状态之列的 统一为故障状态 else: status = {'status': Const.DEV_WORK_STATUS_FAULT} ii += 1 result[str(ii)] = status allPorts, usedPorts, usePorts = self.get_port_static_info(result) # 这里存在多线程更新缓存的场景,可能性比较低,但是必须先取出来,然后逐个更新状态,然后再记录缓存 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 ctrInfo.update({ 'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts }) Device.update_dev_control_cache(self.device.devNo, ctrInfo) return result def get_default_port_nums(self): default_num = 10 if self.device.devTypeCode == Const.DEVICE_TYPE_CODE_CHARGING_DIANCHUAN_HIGH: default_num = 2 return default_num 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) if 'allPorts' in ctrInfo and ctrInfo['allPorts'] > 0: allPorts = ctrInfo['allPorts'] else: allPorts = self.get_default_port_nums() statusDict = {} 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} return statusDict 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(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, payload = {'IMEI': self._device['devNo'], "funCode": '0A', 'data': portStr + lockStr}, timeout = MQTT_TIMEOUT.SHORT) 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'][18::] 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 active: raise ServiceException({'result': 2, 'description': u'该设备不支持直接打开端口'}) 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, 'needTime': 0, 'leftTime': 0, 'endTime': datetime.datetime.now().strftime(Const.DATETIME_FMT)}) newValue = {str(port): portCtrInfo} Device.update_dev_control_cache(self._device['devNo'], newValue) def stop_charging_port(self, port): portStr = fill_2_hexByte(hex(int(port)), 2) devInfo = MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, payload = {'IMEI': self._device['devNo'], "funCode": '0B', 'data': portStr}, timeout = MQTT_TIMEOUT.SHORT) 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'][16::] port = int(data[2:4], 16) leftTime = int(data[4:8], 16) data = {"port": port} # 电量计费模式 if not self._device.get("otherConf", dict()).get("consumeModule", 0): data.update({"remainder_time": leftTime}) else: data.update({"remainder_time": leftTime / 100.0}) return data def stop(self, port = None): return self.stop_charging_port(port) @property def isHaveStopEvent(self): return True # 获取IC卡、投币、最大功率设置 def get_IC_coin_power_config(self): devInfo = MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, payload = {'IMEI': self._device['devNo'], "funCode": '0C', 'data': '00'}, timeout = MQTT_TIMEOUT.SHORT) 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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) confData = devInfo['data'][18::] maxPower = int(confData[0:4], 16) icMoney = int(confData[4:6], 16) if len(confData) > 6: time1 = int(confData[6:10], 16) time2 = int(confData[10:14], 16) time3 = int(confData[14:18], 16) else: time1, time2, time3 = 0, 0, 0 return {'maxPower': maxPower, 'icMoney': icMoney, 'time1': time1, 'time2': time2, 'time3': time3} def set_IC_coin_power_config(self, infoDict): data = '' data += fill_2_hexByte(hex(int(infoDict['maxPower'])), 4) data += fill_2_hexByte(hex(int(infoDict['icMoney'])), 2) data += fill_2_hexByte(hex(int(infoDict['time1'])), 4) data += fill_2_hexByte(hex(int(infoDict['time2'])), 4) data += fill_2_hexByte(hex(int(infoDict['time3'])), 4) devInfo = MessageSender.send( device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, payload = {'IMEI': self._device['devNo'], "funCode": '08', 'data': data}, timeout = MQTT_TIMEOUT.SHORT ) 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'充电桩忙,无响应,请您稍候再试'}) otherConf = self.device.get("otherConf") or dict() otherConf.update(infoDict) Device.objects.filter(devNo=self.device.devNo).update(otherConf=otherConf) Device.invalid_device_cache(self.device.devNo) def get_coin_card_enable(self): devs = Device.get_collection().find({'devNo': self._device['devNo']}) if devs.count == 0: raise ServiceException( {'result': 2, 'description': u'没有找到设备哦'}) return {'putCoins': devs[0].get('otherConf', {}).get('putCoins', False), 'icCard': devs[0].get('otherConf', {}).get('icCard', False)} def set_coin_card_enable(self, infoDict): data = '' if infoDict['putCoins']: data += '01' else: data += '00' if infoDict['icCard']: data += '01' else: data += '00' devInfo = MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, payload = {'IMEI': self._device['devNo'], "funCode": '09', 'data': data}, timeout = MQTT_TIMEOUT.SHORT) 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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) try: conf = Device.objects.get(devNo = self._device['devNo']).otherConf conf.update({'putCoins': infoDict['putCoins'], 'icCard': infoDict['icCard']}) Device.get_collection().update_one({'devNo': self._device['devNo']}, {'$set': {'otherConf': conf}}) except Exception, e: logger.error('update dev=%s coin enable ic enable e=%s' % (self._device['devNo'], e)) def get_dev_all_settings(self): """ 电川PCB- DC_Crg_10L_C4.0及以上版本 支持全部参数一起查询 多参数消费流程 消费方式 :return: """ result = MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, payload = { "IMEI": self._device["devNo"], "data": "00", "funCode": "36" }, timeout = MQTT_TIMEOUT.SHORT) rst = result.get("rst") if rst == -1: raise ServiceException({'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'}) elif rst == 1: raise ServiceException({'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) elif rst == 0: data = split_str(result.get('data'), lens = "4444444242424224442222", startIndex = 18, toInt = True) resultDict = { "time1": data[0], "time2": data[1], "time3": data[2], "card1Time": data[3], "card2Time": data[4], "card3Time": data[5], "icMoney": data[6], # 前台单位为角 "cardRefund": bool(data[7]), "power1": data[8], "power1ratio": data[9] * 10, "power2": data[10], "power2ratio": data[11] * 10, "power3": data[12], "power3ratio": data[13] * 10, "autoStop": bool(data[14]), "fuchongPower": data[15] * 0.1, "fuchongTime": data[16], "noPowerTime": data[17], "chargeFree": bool(data[18]), "volume": data[19], "consumeProcess": data[20], "consumeModule": data[21] } else: raise ServiceException({'result': 2, 'description': u"数据读取失败,请您稍后再试"}) # TODO zjl 这个地方主板的读取所有参数对于icMoney这个字段读取的是错误的值,需要再使用旧的读取指令读取一遍,然后更新,等主板更正过来之后删除这一次的读取 oldGetSettingsData = self.get_IC_coin_power_config() resultDict['icMoney'] = oldGetSettingsData.get("icMoney") resultDict['maxPower'] = oldGetSettingsData.get("maxPower") coinCardEnable = self.get_coin_card_enable() resultDict['putCoins'] = coinCardEnable['putCoins'] resultDict['icCard'] = coinCardEnable['icCard'] return resultDict # 获取设备配置参数 def get_dev_setting(self): driverCode = self._device.get("devType", dict()).get("code", "") dev = Device.objects.get(devNo = self._device['devNo']) # type: Device if driverCode == Const.DEVICE_TYPE_CODE_CHARGING_DIANCHUAN_HIGH: resultDict = self.get_dev_all_settings() else: resultDict = self.get_IC_coin_power_config() tempDict = self.get_coin_card_enable() resultDict.update(tempDict) tempDict = self.get_gear_conf() resultDict.update(tempDict) tempDict = self.get_fullstop_cardrefund() resultDict.update(tempDict) # 兼容刷卡时间 2a指令 try: tempDict = self.get_freemode_volume_andsoon_config() except Exception: tempDict = { 'volume': '', 'card1Time': '', 'card2Time': '', 'card3Time': '', 'fuchongPower': dev.otherConf.get('fuchongPower', ''), 'fuchongTime': dev.otherConf.get('fuchongTime', '') } finally: resultDict.update(tempDict) cardNoRangeStart = dev.otherConf.get('cardNoRangeStart', '') resultDict.update({'cardNoRangeStart': cardNoRangeStart}) cardNoRangeEnd = dev.otherConf.get('cardNoRangeEnd', '') resultDict.update({'cardNoRangeEnd': cardNoRangeEnd}) tempDict = self.get_temperature_voltage_threshold() resultDict.update(tempDict) return resultDict # 获取设备配置参数 def set_dev_setting(self, setConf): keys = setConf.keys() if 'putCoins' in keys or 'icCard' in keys: self.set_coin_card_enable(setConf) if 'maxPower' in keys or 'icMoney' in keys or 'time1' in keys or 'time2' in keys or 'time3' in keys: self.set_IC_coin_power_config(setConf) def get_IC_card_password(self): devInfo = MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, payload = {'IMEI': self._device['devNo'], "funCode": '1A', 'data': '00'}, timeout = MQTT_TIMEOUT.SHORT) 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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) confData = devInfo['data'][18::] result = confData[0:2] if result == '00': cardPassword = '' else: cardPassword = str(int(confData[2:12])) return {'cardPassword': cardPassword} def get_fullstop_cardrefund(self): devInfo = MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, payload = {'IMEI': self._device['devNo'], "funCode": '0C', 'data': '00'}, timeout = MQTT_TIMEOUT.SHORT) 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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) confData = devInfo['data'][18::] maxPower = int(confData[0:4], 16) icMoney = int(confData[4:6], 16) if len(confData) > 6: time1 = int(confData[6:10], 16) time2 = int(confData[10:14], 16) time3 = int(confData[14:18], 16) else: time1, time2, time3 = 0, 0, 0 return {'autoStop': True if confData[20:22] == u'01' else False, 'cardRefund': True if confData[18:20] == u'01' else False} def set_fullstop_cardrefund(self, infoDict): data = '' if infoDict['autoStop']: data += '01' else: data += '00' if infoDict['cardRefund']: data += '01' else: data += '00' devInfo = MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, payload = {'IMEI': self._device['devNo'], "funCode": '13', 'data': data}, timeout = MQTT_TIMEOUT.SHORT) 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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) try: conf = Device.objects.get(devNo = self._device['devNo']).otherConf conf.update({'autoStop': infoDict['autoStop'], 'cardRefund': infoDict['cardRefund']}) Device.get_collection().update({'devNo': self._device['devNo']}, {'$set': {'otherConf': conf}}) except Exception, e: logger.error('update dev=%s coin enable ic enable e=%s' % (self._device['devNo'], e)) def get_gear_conf(self): resultDict = {'power1': 0, 'power1ratio': 0, 'power2': 0, 'power2ratio': 0, 'power3': 0, 'power3ratio': 0} devInfo = MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, payload = {'IMEI': self._device['devNo'], "funCode": '15', 'data': '00'}, timeout = MQTT_TIMEOUT.SHORT) if devInfo.has_key('rst') and devInfo['rst'] != 0: if devInfo['rst'] == -1: raise ServiceException( {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'}) elif devInfo['rst'] == 1: return resultDict confData = devInfo['data'][18::] power1 = int(confData[0:4], 16) power1ratio = int(confData[4:6], 16) * 10 power2 = int(confData[6:10], 16) power2ratio = int(confData[10:12], 16) * 10 power3 = int(confData[12:16], 16) power3ratio = int(confData[16:18], 16) * 10 result = { 'power1': power1, 'power1ratio': power1ratio, 'power2': power2, 'power2ratio': power2ratio, 'power3': power3, 'power3ratio': power3ratio } if self.device.devTypeCode == Const.DEVICE_TYPE_CODE_ZHANGRUNYIHE: power4 = int(confData[18:22], 16) power4ratio = int(confData[22:24], 16) * 10 power5ratio = int(confData[24:26], 16) * 10 result.update({'power4': power4, 'power4ratio': power4ratio, 'power5ratio': power5ratio}) else: if len(confData) > 31: power4 = int(confData[18:22], 16) power4ratio = int(confData[22:24], 16) * 10 power5 = int(confData[24:28], 16) power5ratio = int(confData[28:30], 16) * 10 result.update({'power4': power4, 'power4ratio': power4ratio, 'power5': power5, 'power5ratio': power5ratio, 'support5PowerTi': True}) return result def set_gear_conf(self, infoDict): data = '' data += fill_2_hexByte(hex(int(infoDict['power1'])), 4) data += fill_2_hexByte(hex(int(infoDict['power1ratio']) / 10), 2) data += fill_2_hexByte(hex(int(infoDict['power2'])), 4) data += fill_2_hexByte(hex(int(infoDict['power2ratio']) / 10), 2) data += fill_2_hexByte(hex(int(infoDict['power3'])), 4) data += fill_2_hexByte(hex(int(infoDict['power3ratio']) / 10), 2) if self.device.devTypeCode == Const.DEVICE_TYPE_CODE_ZHANGRUNYIHE: data += fill_2_hexByte(hex(int(infoDict['power4'])), 4) data += fill_2_hexByte(hex(int(infoDict['power4ratio']) / 10), 2) data += fill_2_hexByte(hex(int(infoDict['power5ratio']) / 10), 2) else: if 'power4' in infoDict: data += fill_2_hexByte(hex(int(infoDict['power4'])), 4) data += fill_2_hexByte(hex(int(infoDict['power4ratio']) / 10), 2) data += fill_2_hexByte(hex(int(infoDict['power5'])), 4) data += fill_2_hexByte(hex(int(infoDict['power5ratio']) / 10), 2) devInfo = MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, payload = {'IMEI': self._device['devNo'], "funCode": '14', 'data': data}, timeout = MQTT_TIMEOUT.SHORT) 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 recharge_card(self, cardNo, money, orderNo = None): # type:(str,RMB)->(dict, RMB) try: data = 'EE1012{}'.format(make_six_bytes_session_id()) cardNo = fill_2_hexByte(hex(int(cardNo)), 8) data += cardNo + fill_2_hexByte(hex(int(money * 10)), 4) + '0001' devInfo = MessageSender.send(device = self.device, cmd = DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_SYNC, payload = {'IMEI': self._device['devNo'], 'data': data, 'funCode': '12'}, timeout = MQTT_TIMEOUT.LONGEST) if devInfo['rst'] != 0: if devInfo['rst'] == ErrorCode.DEVICE_CONN_FAIL: # 离线无法判断是否成功, 认为充值成功, 走售后解决 return { 'result': ErrorCode.DEVICE_CONN_FAIL, 'description': u'当前充电桩正在玩命找网络,请您稍候再试' }, None elif devInfo['rst'] == ErrorCode.BOARD_UART_TIMEOUT: return { 'result': ErrorCode.BOARD_UART_TIMEOUT, 'description': u'当前充电桩忙,无响应,请您稍候再试' }, None else: return { 'result': devInfo['rst'], 'description': u'系统异常' }, None resultData = devInfo['data'] if resultData[4:6] != '16': return { 'result': ErrorCode.PARAMETER_ERROR_TO_BOX, 'description': u'充值返回报文命令码不为16' }, None balance = RMB(int(resultData[26:30], 16)) * Decimal('0.1') result = True if resultData[34:36] == '01' else False if result: return { 'result': ErrorCode.SUCCESS, 'description': '' }, balance else: return { 'result': ErrorCode.IC_RECHARGE_FAIL, 'description': u'充值失败' }, balance except Exception as e: logger.exception(e) return { 'result': ErrorCode.EXCEPTION, 'description': e.message }, None # 获取IC卡、投币、最大功率设置 def get_freemode_volume_andsoon_config(self): devInfo = MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, payload = {'IMEI': self._device['devNo'], "funCode": '2A', 'data': '00'}, timeout = MQTT_TIMEOUT.SHORT) 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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) confData = devInfo['data'][18::] card1Time = int(confData[0:4], 16) card2Time = int(confData[4:8], 16) card3Time = int(confData[8:12], 16) # chargeFree = True if confData[12:14] == '01' else False chargeFree = int(confData[12:14]) volume = int(confData[14:16], 16) fuchongPower = int(confData[16:20], 16) * 0.1 fuchongTime = int(confData[20:24], 16) return {'card1Time': card1Time, 'card2Time': card2Time, 'card3Time': card3Time, 'chargeFree': chargeFree, 'volume': volume, 'fuchongPower': fuchongPower, 'fuchongTime': fuchongTime} def get_mcu_version(self): devInfo = MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, payload = {'IMEI': self._device['devNo'], "funCode": '25', 'data': '00'}, timeout = MQTT_TIMEOUT.SHORT) 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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) confData = devInfo['data'][18::] mcuVersion = int(confData[0:4], 16) return {'mcuVersion': mcuVersion} def set_freemode_volume_config(self, infoDict): data = '01' if infoDict['chargeFree'] == '1' else '00' data += fill_2_hexByte(hex(int(infoDict.get('volume', 5))), 2) devInfo = MessageSender.send(device = self.device, cmd = self.make_random_cmdcode(), payload = {'IMEI': self._device['devNo'], "funCode": '27', 'data': data}, timeout = MQTT_TIMEOUT.SHORT) 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 set_fuchong_config(self, infoDict): data = fill_2_hexByte(hex(int(infoDict['fuchongPower']) * 10), 4) data += fill_2_hexByte(hex(int(infoDict['fuchongTime'])), 4) devInfo = MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, payload = {'IMEI': self._device['devNo'], "funCode": '28', 'data': data}, timeout = MQTT_TIMEOUT.SHORT) 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'充电桩忙,无响应,请您稍候再试'}) dev = Device.objects.get(devNo = self._device['devNo']) dev.otherConf.update({'fuchongPower': int(infoDict['fuchongPower'])}) dev.otherConf.update({'fuchongTime': int(infoDict['fuchongTime'])}) dev.save() def set_card_time_config(self, infoDict): data = fill_2_hexByte(hex(int(infoDict['card1Time'])), 4) data += fill_2_hexByte(hex(int(infoDict['card2Time'])), 4) data += fill_2_hexByte(hex(int(infoDict['card3Time'])), 4) devInfo = MessageSender.send(device = self.device, cmd = self.make_random_cmdcode(), payload = {'IMEI': self._device['devNo'], "funCode": '29', 'data': data}, timeout = MQTT_TIMEOUT.SHORT) 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 set_IC_card_password(self, infoDict): passwordStr = str(infoDict['cardPassword']) if len(passwordStr) < 6 or len(passwordStr) > 12: raise ServiceException({'result': 2, 'description': u'密码长度不合法, 请重新设置'}) data = passwordStr.rjust(12, '0') devInfo = MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, payload = {'IMEI': self._device['devNo'], 'funCode': '18', 'data': data}, timeout = MQTT_TIMEOUT.SHORT) def set_IC_card_range(self, infoDict): cardNoRangeStart = str(infoDict['cardNoRangeStart']) cardNoRangeEnd = str(infoDict['cardNoRangeEnd']) if int(cardNoRangeStart) > 4294967295 or int(cardNoRangeEnd) > 4294967295 or int(cardNoRangeStart) > int( cardNoRangeEnd): raise ServiceException({'result': 2, 'description': u'卡号设置不合法, 请重新设置'}) cardNoRangeStart = cardNoRangeStart.rjust(10, '0') cardNoRangeEnd = cardNoRangeEnd.rjust(10, '0') data = cardNoRangeStart + cardNoRangeEnd devInfo = MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, payload = {'IMEI': self._device['devNo'], 'funCode': '19', 'data': data}, timeout = MQTT_TIMEOUT.SHORT) dev = Device.objects.get(devNo = self._device['devNo']) dev.otherConf.update({'cardNoRangeStart': str(infoDict['cardNoRangeStart'])}) dev.otherConf.update({'cardNoRangeEnd': str(infoDict['cardNoRangeEnd'])}) dev.save() def set_no_power_time(self, infoDict): """ 指令是0x37 可能是 双路大功率独有的 需要找主板厂家确认一下 设置充电器移除时间 相当于负载检测 5-999秒 设置浮充功率 进入浮充时间的最低功率 单位0.1w 0-99.9w 设置浮充时间 进入浮充之后 的充电时间 120-999秒 :param infoDict: :return: """ fuchongPower = infoDict["fuchongPower"] fuchongTime = infoDict["fuchongTime"] noPowerTime = infoDict["noPowerTime"] if fuchongPower > 99.9: raise ServiceException({"result": 2, "description": u"浮充功率设置范围为0-99.9w"}) if fuchongTime > 999 or fuchongTime < 100: raise ServiceException({"result": 2, "description": u"浮充时间设置范围为120-999秒"}) if noPowerTime > 999 or noPowerTime < 5: raise ServiceException({"result": 2, "decription": u"充电器移除时间设置范围为5-999秒"}) result = MessageSender.send( device=self.device, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, payload={ "IMEI": self.device.devNo, "funCode": "37", "data": "{:0>4X}{:0>4X}{:0>4X}".format(int(fuchongPower*10), int(fuchongTime), int(noPowerTime)) } ) if result.get('rst') == -1: raise ServiceException({'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'}) elif result.get('rst') == 1: raise ServiceException({'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试'}) else: if result["data"][18:20] != "01": raise ServiceException({"result": 2, "description": u"设置失败,请您稍后再试"}) otherConf = self.device.get("otherConf", dict()) otherConf.update({"fuchongPower": fuchongPower, "fuchongTime": fuchongTime, "noPowerTime": noPowerTime}) Device.objects.filter(devNo=self.device.devNo).update(otherConf=otherConf) def set_device_function(self, request, lastSetConf): if request.POST.has_key('putCoins'): # putCoins = True if request.POST.get('putCoins') == 'true' else False putCoins = request.POST.get("putCoins", False) lastSetConf.update({'putCoins': putCoins}) self.set_coin_card_enable(lastSetConf) if request.POST.has_key('icCard'): # icCard = True if request.POST.get('icCard') == 'true' else False icCard = request.POST.get("icCard", False) lastSetConf.update({'icCard': icCard}) self.set_coin_card_enable(lastSetConf) if request.POST.has_key('autoStop'): # autoStop = True if request.POST.get('autoStop') == 'true' else False autoStop = request.POST.get("autoStop", False) lastSetConf.update({'autoStop': autoStop}) self.set_fullstop_cardrefund(lastSetConf) if request.POST.has_key('cardRefund'): # cardRefund = True if request.POST.get('cardRefund') == 'true' else False cardRefund = request.POST.get("cardRefund", False) lastSetConf.update({'cardRefund': cardRefund}) self.set_fullstop_cardrefund(lastSetConf) if request.POST.has_key('chargeFree'): # chargeFree = True if request.POST.get('chargeFree') == 'true' else False chargeFree = request.POST.get("chargeFree", False) lastSetConf.update({'chargeFree': chargeFree}) self.set_freemode_volume_config(lastSetConf) def set_elecCharge_function(self,elecCharge): data = fill_2_hexByte(hex(int(elecCharge * 100)), 4) devInfo = MessageSender.send( device=self.device, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, payload={'IMEI': self._device['devNo'], "funCode": '50', 'data': data}, timeout=MQTT_TIMEOUT.SHORT ) if "rst" in devInfo and devInfo.get("rst") != 0: if devInfo.get("rst") == -1: raise ServiceException({"result": 2, "description": u"网络故障,请重新试试"}) if devInfo.get("rst") == 1: raise ServiceException({"result": 2, "description": u"充电桩无响应,请稍后再试试"}) return devInfo def get_elecCharge_function(self): time.sleep(15) data = 'EE0951{}'.format(make_six_bytes_session_id()) + '01' devInfo = MessageSender.send( device=self.device, cmd=DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_SYNC, payload={'IMEI': self._device['devNo'], "funCode": '51', 'data': data}, timeout=MQTT_TIMEOUT.SHORT ) if "rst" in devInfo and devInfo.get("rst") != 0: if devInfo.get("rst") == -1: raise ServiceException({"result": 2, "description": u"网络故障,请重新试试"}) if devInfo.get("rst") == 1: raise ServiceException({"result": 2, "description": u"充电桩无响应,请稍后再试试"}) elecCharge = round(float(int(devInfo['data'][18:22],16)) / 100,2) return elecCharge def set_device_function_param(self, request, lastSetConf): # 和 主板厂家沟通 目前已经不会存在 有maxPower这个控制选项 maxPower 就是分档功率中的最高档功率 if request.POST.has_key('icMoney'): lastSetConf.update({'icMoney': int(request.POST.get('icMoney', 0))}) self.set_IC_coin_power_config(lastSetConf) if "noPowerTime" in request.POST: lastSetConf.update({ "noPowerTime": int(request.POST["noPowerTime"]), "fuchongPower": int(request.POST["fuchongPower"]), "fuchongTime": int(request.POST["fuchongTime"]), }) self.set_no_power_time(lastSetConf) if request.POST.has_key('time1') and request.POST.has_key('time2') and request.POST.has_key('time3'): lastSetConf.update({'time1': int(request.POST.get('time1', 0))}) lastSetConf.update({'time2': int(request.POST.get('time2', 0))}) lastSetConf.update({'time3': int(request.POST.get('time3', 0))}) self.set_IC_coin_power_config(lastSetConf) if request.POST.has_key('power1') and request.POST.has_key('power1ratio'): lastSetConf.update({'power1': int(request.POST.get('power1', 0))}) lastSetConf.update({'power1ratio': int(request.POST.get('power1ratio', 0))}) lastSetConf.update({'power2': int(request.POST.get('power2', 0))}) lastSetConf.update({'power2ratio': int(request.POST.get('power2ratio', 0))}) lastSetConf.update({'power3': int(request.POST.get('power3', 0))}) lastSetConf.update({'power3ratio': int(request.POST.get('power3ratio', 0))}) lastSetConf.update({'power4': int(request.POST.get('power4', 0))}) lastSetConf.update({'power4ratio': int(request.POST.get('power4ratio', 0))}) lastSetConf.update({'power5': int(request.POST.get('power5', 0))}) lastSetConf.update({'power5ratio': int(request.POST.get('power5ratio', 0))}) self.set_gear_conf(lastSetConf) if request.POST.has_key('card1Time') and request.POST.has_key('card2Time') and request.POST.has_key( 'card3Time'): lastSetConf.update({'card1Time': int(request.POST.get('card1Time', 0))}) lastSetConf.update({'card2Time': int(request.POST.get('card2Time', 0))}) lastSetConf.update({'card3Time': int(request.POST.get('card3Time', 0))}) self.set_card_time_config(lastSetConf) if 'fuchongPower' in request.POST and 'fuchongTime' in request.POST: fuchongPower = 0 if request.POST.get('fuchongPower') == '' else int(request.POST.get('fuchongPower')) fuchongTime = 0 if request.POST.get('fuchongTime') == '' else int(request.POST.get('fuchongTime')) lastSetConf.update({'fuchongPower': fuchongPower}) lastSetConf.update({'fuchongTime': fuchongTime}) self.set_fuchong_config(lastSetConf) if "consumeProcess" in request.POST and "consumeModule" in request.POST: lastSetConf.update({"consumeProcess": request.POST.get("consumeProcess")}) lastSetConf.update({"consumeModule": request.POST.get("consumeModule")}) self.set_consume_module(lastSetConf) if 'cardPassword' in request.POST: lastSetConf.update({'cardPassword': int(request.POST.get('cardPassword', 0))}) self.set_IC_card_password(lastSetConf) if 'cardNoRangeStart' in request.POST and 'cardNoRangeEnd' in request.POST: lastSetConf.update({'cardNoRangeStart': int(request.POST.get('cardNoRangeStart', 0))}) lastSetConf.update({'cardNoRangeEnd': int(request.POST.get('cardNoRangeEnd', 0))}) self.set_IC_card_range(lastSetConf) if request.POST.has_key('voltageThre') and request.POST.has_key('temThre'): lastSetConf.update({'voltageThre': int(request.POST.get('voltageThre', 230))}) lastSetConf.update({'temThre': int(request.POST.get('temThre', 60))}) self.set_temperature_voltage_threshold(lastSetConf) def response_card_status(self, cardNo, balance, status): data = 'EE1123{}'.format(make_six_bytes_session_id()) + fill_2_hexByte(hex(int(cardNo)), 4) + fill_2_hexByte( hex(int(balance * 10)), 4) + 'AA33' + status devInfo = MessageSender.send(self.device, DeviceCmdCode.PASSTHROUGH_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: # 等于1的时候,说明服务器和远程模块通讯OK,响应已经收到,不需要异常处理 raise ServiceException( {'result': 2, 'description': u'充电桩没有响应,可能是版本不对或者串口接线不良'}) resultData = devInfo['data'] if resultData[4:6] != '23': return {'status': '00'} backCardNo = int(resultData[18:26], 16) if backCardNo != int(cardNo): return {'status': '00'} return {'status': resultData[26:28]} def set_consume_module(self, infoDict): """ 设置消费模式以及消费流程 包含两个参数 consumeProcess 消费流程 consumeModule 消费方式 :param infoDict: :return: """ consumeProcess = infoDict.get("consumeProcess", 0) consumeModule = infoDict.get("consumeModule", 0) consumeProcessHex = fill_2_hexByte(hex(int(consumeProcess)), 2) consumeModuleHex = fill_2_hexByte(hex(int(consumeModule)), 2) result = MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, payload = { "IMEI": self._device["devNo"], "funCode": "38", "data": consumeProcessHex + consumeModuleHex }, timeout = MQTT_TIMEOUT.SHORT) rst = result.get("rst") data = result.get("data") if rst == 0 and data[-4:-2] == "01": # OtherConf中也保留一份 方便启动的时候随时查询 otherConf = self._device.get("otherConf", dict()) otherConf.update({ "consumeProcess": int(consumeProcess), "consumeModule": int(consumeModule) }) Device.objects.filter(devNo = self._device["devNo"]).update(otherConf = otherConf) Device.invalid_device_cache(self._device["devNo"]) return elif data[-4: -2] != "01": raise ServiceException({'result': 2, 'description': u'设置失败,请重新试试'}) elif rst == -1: raise ServiceException({'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'}) elif rst == 1: raise ServiceException({'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试'}) else: raise ServiceException({'result': 2, 'description': u'未知错误'}) def get_temperature_voltage_threshold(self): devObj = Device.objects.get(devNo = self._device['devNo']) return {'temThre': devObj.otherConf.get('temThre', 230), 'voltageThre': devObj.otherConf.get('voltageThre', 60)} def set_temperature_voltage_threshold(self, infoDict): data = '' data += fill_2_hexByte(hex(int(infoDict['temThre'])), 2) data += fill_2_hexByte(hex(int(infoDict['voltageThre'])), 4) devInfo = MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, payload = {'IMEI': self._device['devNo'], "funCode": 'AA', 'data': data}, timeout = MQTT_TIMEOUT.SHORT) 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'充电桩忙,无响应,请您稍候再试'}) devObj = Device.objects.get(devNo = self._device['devNo']) devObj.otherConf['temThre'] = int(infoDict['temThre']) devObj.otherConf['voltageThre'] = int(infoDict['voltageThre']) devObj.save() def format_upload_power(self, power): return float(power / 10) ################ api相关接口 def apiGetPortStatusFromDc(self, record): return self.get_port_status_from_dev() def apiGetDevSettingsFromDc(self, record): result = self.get_dev_setting() data = dict() data['power1'] = result['power1'] data['power2'] = result['power2'] data['power3'] = result['power3'] data['power1ratio'] = result['power1ratio'] data['power2ratio'] = result['power2ratio'] data['power3ratio'] = result['power3ratio'] data['fuchongTime'] = result['fuchongTime'] data['fuchongPower'] = result['fuchongPower'] data['maxPower'] = result['maxPower'] data['icMoney'] = result['icMoney'] data['time1'] = result['time1'] data['time2'] = result['time2'] data['time3'] = result['time3'] return data def apisetDevSettingsFromDc(self, record): self.set_IC_coin_power_config(record) self.set_gear_conf(record) self.set_fuchong_config(record) return {} def apiGetPortInfoFromDc(self, record): return self.get_port_info(record['port']) def apiStartDeviceForDc(self, record): port = hex(int(record['port'])) hexPort = fill_2_hexByte(port, 2) coins = float(record['price']) hexTime = fill_2_hexByte(hex(int(record['time']))) devInfo = MessageSender.send( device=self.device, cmd=DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_SYNC, payload={ 'IMEI': self._device['devNo'], "funCode": '02', 'data': 'EE0D02{}'.format(make_six_bytes_session_id()) + hexPort + '0000' + hexTime }, timeout=MQTT_TIMEOUT.START_DEVICE) usePort = int(record['port']) if 'rst' not in devInfo: raise ServiceException( { 'result': 2, 'description': u'启动设备失败,您的支付金额已经退还,请重新扫码设备试试(1001)' }) if devInfo['rst'] != 0: if devInfo['rst'] == -1: raise ServiceException( { 'result': 2, 'description': u'充电桩正在玩命找网络,您的金币还在,重试不需要重新付款,建议您试试旁边其他设备,或者稍后再试哦' }) elif devInfo['rst'] == 1: raise ServiceException( {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) else: raise ServiceException( { 'result': 2, 'description': u'启动设备失败,您的支付金额已经退回,请重新扫码设备({})'.format(devInfo['rst']) }) portDict = { 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'status': Const.DEV_WORK_STATUS_WORKING, 'coins': coins, 'price': coins, 'isApi': True } Device.update_dev_control_cache( self._device['devNo'], { str(usePort): portDict }) return devInfo def get_server_setting(self): if self.device.devTypeCode == Const.DEVICE_TYPE_CODE_CHARGING_DIANCHUAN_HIGH: default_refund_protection_time = 3 else: default_refund_protection_time = 5 return { 'refundProtectionTime': int( self.device.get_other_conf_item('refundProtectionTime', default_refund_protection_time)), } def set_server_setting(self, payload): refundProtectionTime = int(payload['refundProtectionTime']) self.device.update_other_conf(refundProtectionTime=refundProtectionTime) def set_service_fee_info(self, payload): elecCharge = round(float(payload['billAsService'].get('elecCharge', 0.5)), 2) serviceCharge = round(float(payload['billAsService'].get('serviceCharge', 0.5)), 2) total = elecCharge + serviceCharge result = self.set_elecCharge_function(total) if result['data'][18:20] != '01': raise InvalidParameter(u'电费参数设置失败') # 显示展示给用户信息部分 if payload.get('displaySwitchs'): displaySwitchs = payload.get('displaySwitchs') else: displaySwitchs = {'displayCoinsSwitch': False, 'displayTimeSwitch': True, 'displayPriceSwitch': True, 'setPulseAble': False, 'setBasePriceAble': False} # 套餐部分 packages = payload.get('packages') # 调整SN套餐顺序 for i, item in enumerate(packages): item['sn'] = i # 套餐id 去重 existIds = list(map(lambda _: _.get('id'), packages)) washConfig = {} for rule in packages: if 'price' in rule: if not is_number(rule['price'] and is_number(rule.get('time', 0))): raise InvalidParameter(u'金币数目或者时间或者价格必须是数字') if RMB(rule['price']) >= self.device.owner.maxPackagePrice: raise InvalidParameter(u'套餐金额超限') if len(rule['name']) > 20: raise InvalidParameter(u'套餐名字只能取1-20位') if 'id' in rule: ruleId = rule['id'] else: ruleId = list(set(range(1, 71)) - set([int(ruleId) for ruleId in washConfig.keys()]) - set(existIds))[0] washConfig[str(ruleId)] = { 'billingMethod': CONSUMETYPE.BILL_AS_SERVICE, 'name': rule['name'], 'coins': float(rule['price']), 'price': float(rule['price']), 'time': float(rule.get('time', 0)), 'description': rule.get('description', ''), 'imgList': rule.get('imgList', []), 'unit': rule.get('unit', u'分钟'), 'switch': rule.get('switch', True), 'sn': rule.get('sn') } total = elecCharge + serviceCharge self.device.update_device_obj(**{ 'washConfig': washConfig, 'otherConf.displaySwitchs': displaySwitchs, 'otherConf.consumeModule': 1, 'otherConf.billingType': 'elec', 'otherConf.totalCharge': total, 'devType.features.billAsService.elecCharge': elecCharge, 'devType.features.billAsService.serviceCharge': serviceCharge }) dealer = self.device.owner dealer.defaultWashConfig[self.device.devTypeId] = self.device['washConfig'].values() dealer.save() def get_service_fee_info(self): ruleList = [] for packageId, rule in self.device['washConfig'].items(): item = { 'id': packageId, 'name': rule['name'], 'coins': rule['coins'], 'price': rule.get('price', rule['coins']), 'time': rule.get('time', 20), 'description': rule.get('description', ''), 'imgList': rule.get('imgList', []), 'unit': rule.get('unit', u'分钟'), 'switch': rule.get('switch', True), } if 'sn' in rule: item['sn'] = rule['sn'] ruleList.append(item) billAsService = self.device.bill_as_service_feature devData = { 'id': self.device.devNo, 'isManager': True, 'groupName': self.device.group['groupName'], 'groupNumber': self.device['groupNumber'], 'devNo': self.device.devNo, 'devTypeName': self.device.devTypeName, 'devTypeCode': self.device.devTypeCode } if "displaySwitchs" in self.device.my_obj.otherConf: displaySwitchs = self.device.my_obj.otherConf.get('displaySwitchs') else: displaySwitchs = { 'displayCoinsSwitch': True, 'displayTimeSwitch': True, 'displayPriceSwitch': True, "setPulseAble": False, "setBasePriceAble": False } ruleList = sorted(ruleList, key=lambda x: (x.get('sn'), x.get('id'))) return { 'ruleList': ruleList, 'billAsService': billAsService, 'devData': devData, 'displaySwitchs': displaySwitchs, }