# -*- coding: utf-8 -*- # !/usr/bin/env python import datetime import json import logging import time from decimal import Decimal import re from mongoengine import Q from typing import TYPE_CHECKING from apilib.monetary import RMB from apilib.systypes import StrEnum from apilib.utils import is_number from apps.web.common.proxy import ClientConsumeModelProxy from apps.web.constant import Const, DeviceCmdCode, CONSUMETYPE, MQTT_TIMEOUT from apps.web.core.adapter.base import SmartBox from apps.web.core.exceptions import ServiceException, InvalidParameter from apps.web.core.helpers import ActionDeviceBuilder 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.user.models import ConsumeRecord from taskmanager.mediator import task_caller logger = logging.getLogger(__name__) if TYPE_CHECKING: pass class CMD_CODE(StrEnum): """ 格式: 命令说明_命令码 """ # 刷卡消费的,分为扣费和退费两种 SWIPE_CARD_10 = '10' DEVICE_SUBMIT_CHARGE_FINISHED_06 = '06' DEVICE_SUBMIT_CHARGE_FINISHED_v2_16 = '16' # 上报投币打开的信息 # 设备在用户投币或者刷卡并选择端口成功打开后,主动上报服务器报文,包含端口号,并上 # 报端口号,消费类型,金额,时间,电量。 DEVICE_SUBMIT_OFFLINE_COINS_20 = '20' # 新增实时上报功能 DEVICE_SUBMIT_REAL_TIME_STATUS_21 = '21' # 设备上传机器故障码给服务器。 DEVICE_SUBMIT_DEVICE_FAULT_0A = '0A' DEVICE_FAULT_FIRE = 'B3' DEVICE_FAULT_SMOKE = 'B4' DEVICE_TEMPERATURE = 'B5' DEVICE_ELEC = 'B6' DEVICE_FAULT_POWER = 'B7' DEVICE_FAULT_TEMPERATURE = 'B8' DEVICE_FAULT_ALTER = 'B2' class ChargingXiaoKeDouBox(SmartBox): def __init__(self, device): super(ChargingXiaoKeDouBox, self).__init__(device) def _check_package(self, package): """ 获取设备启动的发送数据 根据设备的当前模式以及套餐获取 :param package: :return: """ sever_configs = self.get_server_configs() unit = package.get("unit", u"分钟") _time = float(package.get("time", 0)) coins = float(package.get('coins', 0)) price = float(package.get('price', 0)) billingMethod = package.get('billingMethod', CONSUMETYPE.POSTPAID) if billingMethod != CONSUMETYPE.POSTPAID: if coins <= 0: raise ServiceException({"result": 2, "description": u"套餐单位错误,套餐金币不能为0"}) if price <= 0: raise ServiceException({"result": 2, "description": u"套餐单位错误,套餐金额不能为0"}) timeElec = float(sever_configs.get('timeElec', 1)) # 每60分钟最大用电量 # 按时间计费 if unit == u"小时": need_time = _time * 60 need_elec = _time * timeElec elif unit == u"天": need_time = _time * 24 * 60 need_elec = _time * 24 / timeElec elif unit == u"秒": need_time = _time / 60 need_elec = _time / 60 / 60 / timeElec elif unit == u"分钟": need_time = _time need_elec = _time / 60 / timeElec elif unit == u'度': need_elec = _time need_time = 60 * 12 else: raise ServiceException({"result": 2, "description": u"套餐单位错误,请联系经销商"}) return need_time, need_elec, coins def disable_app_device(self, switch=True): # type:(bool) -> None otherConf = self.device.get('otherConf', {}) otherConf['disableDevice'] = switch Device.objects.filter(devNo=self.device['devNo']).update(otherConf=otherConf) Device.invalid_device_cache(self.device['devNo']) @staticmethod def reverse_hex(data): # type:(str) -> str if not isinstance(data, str): raise TypeError return ''.join(list(reversed(re.findall(r'.{2}', data)))) @staticmethod def encode_str(data, length=2, ratio=1.0, base=16): # type:(any,int,float,int) -> str if not isinstance(data, Decimal): data = Decimal(data) if not isinstance(length, str): length = str(length) if not isinstance(ratio, Decimal): ratio = Decimal(ratio) end = 'X' if base == 16 else 'd' encodeStr = '%.' + length + end encodeStr = encodeStr % (data * ratio) return encodeStr @staticmethod def decode_str(data, ratio=1.0, base=16, reverse=False): # type:(str,float,int) -> str """ ratio:比率单位转换 """ if not isinstance(data, str): data = str(data) if reverse: data = ''.join(list(reversed(re.findall(r'.{2}', data)))) return '%.10g' % (int(data, base) * ratio) def decode_long_hex_to_list(self, data, split=2, ratio=1.0, base=16): # type:(str,int,float,int) -> list """ return: list """ if len(data) % split != 0: raise Exception('Invalid data') pattern = r'.{%s}' % split hex_list = re.findall(pattern, data) hex_list = map(lambda x: self.decode_str(x, ratio=ratio, base=base), hex_list) return hex_list @staticmethod def check_params_range(params, minData=None, maxData=None, desc=''): # type:(str,float,float,str) -> str """ 检查参数,返回字符串参数 """ if params is None: raise InvalidParameter(u'参数错误.') if not isinstance(params, Decimal): params = Decimal(params) if not minData and maxData: if not isinstance(maxData, Decimal): maxData = Decimal(maxData) if params <= maxData: return '%g' % params else: raise InvalidParameter(u'%s超出可选范围,可选最大值为%g' % (desc, maxData)) if not maxData and minData: if not isinstance(minData, Decimal): minData = Decimal(minData) if minData <= params: return '%g' % params else: raise InvalidParameter(u'%s超出可选范围,可选最小值为%g' % (desc, minData)) if not minData and not maxData: return '%g' % params else: if not isinstance(minData, Decimal): minData = Decimal(minData) if not isinstance(maxData, Decimal): maxData = Decimal(maxData) if minData <= params <= maxData: return '%g' % params else: raise InvalidParameter(u'%s参数超出可选范围,可取范围为%g-%g' % (desc, minData, maxData)) def send_mqtt(self, funCode, data, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, order_id=None, timeout=MQTT_TIMEOUT.NORMAL): """ 发送mqtt 指令210 返回data """ if not isinstance(funCode, str): funCode = str(funCode) if not isinstance(data, str): data = str(data) payload = {'IMEI': self.device['devNo'], 'funCode': funCode, 'data': data} if order_id: payload.update({'order_id': order_id}) result = MessageSender.send(self.device, cmd, payload, timeout=timeout) if 'rst' in result and result['rst'] != 0: if result['rst'] == -1: raise ServiceException( {'result': 2, 'description': u'该设备正在玩命找网络,请您稍候再试', 'rst': -1}) elif result['rst'] == 1: raise ServiceException( {'result': 2, 'description': u'该设备忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能', 'rst': 1}) else: if cmd in [DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE]: return if 'order_id' in result or 'order_info' in result: return result else: return result.get('data', '') @staticmethod def port_is_busy(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 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 ack_05_event(self, ack_event_id): return MessageSender.send(device=self.device, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, payload={ 'IMEI': self.device.devNo, 'data': '00', 'cmd': DeviceCmdCode.OPERATE_DEV_SYNC, 'ack_event_id': ack_event_id, 'funCode': 'CA'}) def update_device_configs(self, updateDict): dev = Device.objects.get(devNo=self.device.devNo) deviceConfigs = dev.otherConf.get('deviceConfigs', {}) deviceConfigs.update(updateDict) dev.otherConf['deviceConfigs'] = deviceConfigs dev.save() Device.invalid_device_cache(self.device.devNo) def get_device_configs(self): dev = Device.get_dev(self.device.devNo) deviceConfigs = dev.get('otherConf', {}).get('deviceConfigs', {}) return deviceConfigs def get_server_configs(self): dev = Device.get_dev(self.device.devNo) serverConfigs = dev.get('otherConf', {}).get('serverConfigs', {}) return serverConfigs def update_server_configs(self, updateDict): dev = Device.objects.get(devNo=self.device.devNo) serverConfigs = dev.otherConf.get('serverConfigs', {}) serverConfigs.update(updateDict) dev.otherConf['serverConfigs'] = serverConfigs dev.save() Device.invalid_device_cache(self.device.devNo) def get_default_port_nums(self): data = self.send_mqtt(funCode='01', data='00') portTotalNum = self.decode_str(data[8:10]) theFirst = self.decode_str(data[10:12]) return portTotalNum, theFirst def get_port_info(self, line): port = self.encode_str(line) result = self.send_mqtt(funCode='15', data=port) data = result.get('data') order_info = result.get('order_info') port = self.decode_str(data[8:10]) if port == '0': status = 'noload' else: status = 'busy' # 主板串口传回数据 # leftTime = self.decode_str(data[10:14]) power = self.decode_str(data[14:18] if data[14:18] != 'FFFF' else '0000') # elec = self.decode_str(data[18:22] if data[18:22] != 'FFFF' else '0000', ratio=0.01) # surp = self.decode_str(data[22:26] if data[22:26] != 'FFFF' else '0000', ratio=0.1) port_info = {'port': port, 'power': power, 'status': status} try: # 模块计算的数据 if order_info: port_info['usedTime'] = round((time.time() - order_info['sts']) / 60.0, 1) port_info['needTime'] = round(order_info.get('need_time', 0) / 60.0, 1) port_info['leftTime'] = round(port_info['needTime'] - port_info['usedTime'], 1) # port_info['actualNeedTime'] = port_info['needTime'] port_info['needElec'] = round(order_info.get('need_elec', 0) / 3600000.0, 3) port_info['usedElec'] = round(order_info.get('elec', 0) / 3600000.0, 3) port_info['leftElec'] = round(port_info['needElec'] - port_info['usedElec'], 3) if not port_info.get('power'): port_info['power'] = order_info.get('power') consumeRcd = ConsumeRecord.objects.filter( Q(orderNo=order_info.get('order_id')) | Q(startKey=order_info.get('order_id'))).first() if consumeRcd: user = consumeRcd.user port_info['nickName'] = user.nickname port_info['openId'] = user.openId port_info['startTime'] = consumeRcd.startTime.strftime('%m-%d %H:%M:%S') if consumeRcd.package.get('name') == '充满自停': port_info.pop('actualNeedTime', None) port_info.pop('needTime', None) port_info.pop('leftTime', None) port_info.pop('needElec', None) port_info.pop('leftElec', None) port_info['needTime'] = '充满自停' else: if consumeRcd.package.get('unit') in ['分钟', '小时', '天', '秒']: port_info.pop('needElec', None) port_info.pop('leftElec', None) billingMethod = order_info.get('billingMethod', CONSUMETYPE.POSTPAID) if billingMethod == CONSUMETYPE.POSTPAID: port_info.pop('leftTime', None) precision = order_info.get('attach_paras', {}).get('precision', 1000) port_info['consumeMoney'] = '{}元'.format( round(order_info.get('need_pay', 0) / (100.0 * precision), 2)) else: port_info['usedTime'] = min(port_info['usedTime'], port_info['needTime']) port_info['consumeMoney'] = round( port_info['coins'] * 0.01 * port_info['usedTime'] / port_info['needTime'], 2) elif consumeRcd.package.get('unit') == '度': port_info.pop('needTime', None) port_info.pop('leftTime', None) port_info.pop('actualNeedTime', None) billingMethod = order_info.get('billingMethod', CONSUMETYPE.POSTPAID) if billingMethod == CONSUMETYPE.POSTPAID: precision = order_info.get('attach_paras', {}).get('precision', 1000) port_info['consumeMoney'] = '{}元'.format( round(order_info.get('need_pay', 0) / (100.0 * precision), 2)) elif billingMethod == CONSUMETYPE.BILL_AS_SERVICE_POSTPAID: ratio_conversion = order_info.get("ratio_conversion",100) precision = order_info.get('attach_paras', {}).get('precision', 1000) port_info["serviceFee"] = '{}'.format(round(order_info.get("service_fee",0) / ratio_conversion / (1000.0 * precision),2)) port_info["elecFee"] = '{}'.format(round(order_info.get("elec_fee",0) / ratio_conversion / (1000.0 * precision),2)) port_info['consumeMoney'] = '{}元'.format( round(order_info.get('need_pay', 0) / ratio_conversion /(1000.0 * precision), 2)) elif billingMethod == CONSUMETYPE.BILL_AS_SERVICE: ratio_conversion = order_info.get("ratio_conversion", 100) port_info['usedElec'] = min(port_info['usedElec'], port_info['needElec']) port_info["serviceFee"] = '{}'.format( round(order_info.get("serviceFee", 0) * port_info['usedElec'] / ratio_conversion,2)) port_info["elecFee"] = '{}'.format( round(order_info.get("elecFee", 0) * port_info['usedElec'] / ratio_conversion, 2)) port_info['consumeMoney'] = '{}元'.format(round( order_info['coins'] * 0.01 * port_info['usedElec'] / port_info['needElec'], 2)) else: port_info['usedElec'] = min(port_info['usedElec'], port_info['needElec']) port_info['consumeMoney'] = '{}元'.format(round( order_info['coins'] * 0.01 * port_info['usedElec'] / port_info['needElec'], 2)) elif order_info.get('card_id'): if self.device.bill_as_service_feature.on: port_info.pop('needTime', None) port_info.pop('leftTime', None) port_info.pop('actualNeedTime', None) ratio_conversion = order_info.get("ratio_conversion", 100) precision = order_info.get('attach_paras', {}).get('precision', 1000) port_info["serviceFee"] = '{}'.format( round(order_info.get("service_fee", 0) / ratio_conversion / (1000.0 * precision), 2)) port_info["elecFee"] = '{}'.format( round(order_info.get("elec_fee", 0) / ratio_conversion / (1000.0 * precision), 2)) port_info['consumeMoney'] = '{}元'.format( round(order_info.get('need_pay', 0) / ratio_conversion / (1000.0 * precision), 2)) else: pass except: pass return port_info def get_ports_info(self, **kw): result = self.get_port_status_from_dev() port_list = [] ctrInfo = Device.get_dev_control_cache(self.device.devNo) statsMap = {Const.DEV_WORK_STATUS_IDLE: 'idle', Const.DEV_WORK_STATUS_WORKING: 'busy', Const.DEV_WORK_STATUS_FAULT: 'fault', Const.DEV_WORK_STATUS_FORBIDDEN: 'ban', Const.DEV_WORK_STATUS_CONNECTED: 'connected', Const.DEV_WORK_STATUS_FINISHED: 'finished'} for port, _info in result.items(): _info['index'] = port if _info.get('status') == Const.DEV_WORK_STATUS_WORKING: _info.update(self.get_port_using_detail(port, ctrInfo, isLazy=True)) for k, v in _info.items(): if k.lower().endswith('money'): _info.pop(k) _info['status'] = statsMap.get(_info['status'], 'busy') port_list.append(_info) return port_list def get_idle_port(self): data = self.send_mqtt(funCode='02', data='00') idlePortNum = self.decode_str(data[8:10]) idlePortList = self.decode_long_hex_to_list(data[10:-2]) return {'idlePortNum': idlePortNum, 'idlePortList': idlePortList} def lock_unlock_port(self, port, lock=True): lockStr = '00' if lock else '01' portStr = self.encode_str(port) data = portStr + lockStr data = self.send_mqtt(funCode='0C', data=data) if data[6:8] == '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 stop_charging_port(self, port, orderNo=None): data = self.encode_str(port) + '01' return self.send_mqtt(funCode='0D', data=data, order_id=orderNo, timeout=10) 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 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): data = self.send_mqtt(funCode='0F', data='00') data = 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} else: 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 @property def isHaveStopEvent(self): return True def test(self, coins, port=1, time='60', elec='0'): data = self.encode_str(port) data += self.encode_str(coins) data += self.encode_str(time, length=4) data += self.encode_str(elec, length=4, ratio=100) devInfo = self.send_mqtt(funCode='14', data=data) return devInfo def get_dev_setting(self): # 音量从缓存中取, 不影响大的流程 resultDict = { 'volume': self.device['otherConf'].get('volume', 3), } try: setting1Info = self.get_dev_settings1() resultDict.update(setting1Info) except: pass try: setting2Info = self.get_dev_settings2() resultDict.update(setting2Info) except: pass try: consumeInfo = self.get_dev_consume_count() resultDict.update(consumeInfo) except: pass return resultDict def get_dev_consume_count(self): data = self.send_mqtt(funCode='07', data='00') data = 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_dev_settings1(self): data = self.send_mqtt(funCode='1E', data='00') data = 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) elecTimeFirst = int(confData[44:46], 16) return { '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, } def get_dev_settings2(self): data = self.send_mqtt(funCode='23', data='00') data = data[:-2] result = {} result['devVer'] = 'v1' result['stopDelay'] = int(data[8:10], 16) result['maxTotalTime'] = int(data[10:12], 16) 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) result['pricePower1'] = self.decode_str(data[32:36], ratio=0.001) result['pricePower2'] = self.decode_str(data[36:40], ratio=0.001) result['pricePower3'] = self.decode_str(data[40:44], ratio=0.001) result['pricePower4'] = self.decode_str(data[44:48], ratio=0.001) result['pricePower5'] = self.decode_str(data[48:52], ratio=0.001) if data[52:56]: result['devVer'] = 'v2' result['lowPower'] = int(data[8:10], 16) result['lowPowerCheckTime'] = self.decode_str(data[52:56]) result['stopDelayTime'] = self.decode_str(data[56:60]) if data[60:64]: result['devVer'] = 'v3' result['maxTotalPower'] = format(int(data[60:64], 16) * 0.1, '.1f') return result def response_use_card(self, res, balance): data = '55061001' data = data + res + self.encode_str(balance, length=4, ratio=10) self.send_mqtt(funCode='10', data=data, cmd=DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE) def set_dev_setting(self, setConf): data = self.encode_str(setConf['coinMin'], length=4) data += self.encode_str(setConf['cardMin'], length=4) data += self.encode_str(setConf['coinElec'], length=2) data += self.encode_str(setConf['cardElec'], length=2) data += self.encode_str(setConf['cst'], length=2) data += self.encode_str(setConf['powerMax1'], length=4) data += self.encode_str(setConf['powerMax2'], length=4) data += self.encode_str(setConf['powerMax3'], length=4) data += self.encode_str(setConf['powerMax4'], length=4) data += self.encode_str(setConf['power2Ti'], length=2) data += self.encode_str(setConf['power3Ti'], length=2) data += self.encode_str(setConf['power4Ti'], length=2) data += self.encode_str(setConf['spRecMon'], length=2) data += self.encode_str(setConf['spFullEmpty'], length=2) data += self.encode_str(setConf['fullPowerMin'], length=2) data += self.encode_str(setConf['fullChargeTime'], length=2) data += self.encode_str(setConf['elecTimeFirst'], length=2) self.send_mqtt(funCode='18', data=data) def set_dev_setting2(self, setConf): if 'lowPower' in setConf: # 新版本 data = self.encode_str(setConf['lowPower']) else: # 老版本 data = self.encode_str(setConf['stopDelay']) data += self.encode_str(setConf['maxTotalTime']) data += self.encode_str(setConf['overVoltage'], length=4) data += self.encode_str(setConf['lowVoltage'], length=4) data += self.encode_str(setConf['overTemp']) data += self.encode_str(setConf['overTempDelayTime']) data += str(setConf['timeDspOn']).replace(':', '') data += str(setConf['timeDspOff']).replace(':', '') data += self.encode_str(setConf['pricePower1'], length=4, ratio=1000.0) data += self.encode_str(setConf['pricePower2'], length=4, ratio=1000.0) data += self.encode_str(setConf['pricePower3'], length=4, ratio=1000.0) data += self.encode_str(setConf['pricePower4'], length=4, ratio=1000.0) data += self.encode_str(setConf['pricePower5'], length=4, ratio=1000.0) if 'lowPower' in setConf: # 新版本 data += self.encode_str(setConf.get('lowPowerCheckTime', 30), length=4) data += self.encode_str(setConf.get('stopDelayTime', 30), length=4) else: pass if 'maxTotalPower' in setConf: data += self.encode_str(setConf['maxTotalPower']) self.send_mqtt(funCode='22', data=data) 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) 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) pricePower1 = request.POST.get('pricePower1', None) pricePower2 = request.POST.get('pricePower2', None) pricePower3 = request.POST.get('pricePower3', None) pricePower4 = request.POST.get('pricePower4', None) pricePower5 = request.POST.get('pricePower5', 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 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}) if pricePower1: lastSetConf.update({'pricePower1': pricePower1}) if pricePower2: lastSetConf.update({'pricePower2': pricePower2}) if pricePower3: lastSetConf.update({'pricePower3': pricePower3}) if pricePower4: lastSetConf.update({'pricePower4': pricePower4}) if pricePower5: lastSetConf.update({'pricePower5': pricePower5}) # 新版本参数 lowPower = request.POST.get('lowPower') if lowPower: lastSetConf.update({'lowPower': lowPower}) lowPowerCheckTime = request.POST.get('lowPowerCheckTime') if lowPowerCheckTime: lastSetConf.update({'lowPowerCheckTime': lowPowerCheckTime}) stopDelayTime = request.POST.get('stopDelayTime') if stopDelayTime: lastSetConf.update({'stopDelayTime': stopDelayTime}) # 2022-05-23 新增最大总功率控制 maxTotalPower = request.POST.get("maxTotalPower", None) if maxTotalPower: lastSetConf.update({'maxTotalPower': int(float(maxTotalPower) * 10)}) # 2022-3-23 新增音量控制 volume = request.POST.get('volume') if volume: self.set_volume(volume) 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) def set_coin_ic_switch(self, coin=True, ic=True): if coin: data = '01' else: data = '00' if ic: data += '01' else: data += '00' result = self.send_mqtt(funCode='09', data=data) return result def check_dev_status(self, attachParas=None): if attachParas is None: raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路、电池类型信息'}) if attachParas.get('isTempPackage') == True: washConfig = self.device.get('tempWashConfig', {}) else: washConfig = self.device.get('washConfig', {}) packageId = attachParas.get('packageId') if not packageId: # 此时为快捷启动充电之前已经校验过一次 return package = washConfig.get(packageId) billingMethod = package.get('billingMethod') if int(self.device.driverVersion.split('.')[-1]) < 14 and billingMethod == 'prepaid': raise ServiceException( {'result': 0, 'description': '当前设备版本不支持预付费模式, 请联系经销商修改为 <后付费套餐> 或 升级设备版本', 'payload': {}}) if not attachParas.has_key('chargeIndex'): raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路'}) port = attachParas['chargeIndex'] result = self.send_mqtt(funCode='88', data=format(int(port), '02X')) if 'order_id' in result: raise ServiceException({'result': 2, 'description': u'当前端口繁忙'}) def start_device_realiable(self, order): # 处理模块侧浮点数问题 RATIO_CONVERSION = 100 attachParas = order.attachParas package = order.package billingMethod = package.get('billingMethod', CONSUMETYPE.POSTPAID) if self.device.bill_as_service_feature.on: billingMethod = CONSUMETYPE.BILL_AS_SERVICE_POSTPAID if billingMethod == CONSUMETYPE.POSTPAID else CONSUMETYPE.BILL_AS_SERVICE port = attachParas.get('chargeIndex') if not port: raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路'}) needTime, needElec, coins = self._check_package(package) payload = { 'IMEI': self._device.devNo, 'funCode': '14', 'order_id': order.orderNo, 'order_type': 'com_start', 'port': int(port), 'coins': int(coins * 100), 'need_time': int(needTime), 'need_elec': int(needElec * 100), 'billingMethod': billingMethod, 'attach_paras': {'openId': order.openId, } } # 为先后付费开关 做预留(有则为后付费,没有则为预付) if package.get('billingMethod') == CONSUMETYPE.POSTPAID or package.get('billingMethod') == CONSUMETYPE.BILL_AS_SERVICE_POSTPAID: payload.update({'coins': 99 * 100}) else: pass if float(self.device['driverVersion'].split('.')[-1]) <= 9: payload.update({'account_rule': self.get_account_rule()}) else: payload.update({'account_rule': self.get_account_rule_new()}) payload['attach_paras'].update({'precision': 3600}) if self.device.bill_as_service_feature.on: payload.update({'serviceFee': int(self.device.bill_as_service_feature.service_charge * RATIO_CONVERSION), "elecFee": int(self.device.bill_as_service_feature.elec_charge * RATIO_CONVERSION), "ratio_conversion":RATIO_CONVERSION}) uart_source = [] uart_source.append( {'write_start': json.dumps(payload, indent=4), 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}) result = MessageSender.send(device=self.device, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, payload=payload, timeout=120) uart_source.append( {'rece_start': json.dumps(result, indent=4), 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}) # 兼容远程上分 远程上分没有订单 try: order.update(uart_source=uart_source) except: pass return result def check_order_state(self, openId): """ 通过 openId 以及设备来鉴别 订单 :param openId: :return: """ dealerId = self.device.ownerId # return ConsumeRecord.objects.filter(ownerId=dealerId, # openId=openId, # devTypeCode=devTypeCode, # status__ne='finished', # isNormal=True, # package__billingMethod=CONSUMETYPE.POSTPAID).first() return ClientConsumeModelProxy.get_not_finished_record(ownerId=dealerId, openId=openId, devTypeCode=self._device["devType"]["code"], package__billingMethod=CONSUMETYPE.POSTPAID) def stop_by_order(self, port, orderNo): order = ConsumeRecord.objects.filter(orderNo=orderNo, isNormal=True, status__ne='finished').first() if order: try: self.stop_charging_port(port, orderNo) except: logger.info('Stop failed orderNo<{}>, devNo<{}>'.format(orderNo, self.device.devNo)) raise ServiceException( {'result': 2, 'description': u'设备当前正忙. 请稍后重试...', 'rst': -1}) def deal_order_finish(self, order, data=None): pass # self.do_postpaid_finished_event(order, data) def do_finished_event(self, order): pass def response_card(self, res, balance, fee, oper, cardNoHex, isVir=False, virtual_card_id=None): if isVir: res = int(res) count = int(balance) * 10 fee = int(fee) * 10 oper = int(oper) MessageSender.send( device=self.device, cmd=220, payload={ 'res': res, 'balance': count, 'card_cst': fee, 'card_ope': oper, 'card_id': cardNoHex, 'funCode': '10', 'isVir': isVir, 'virtual_card_id': virtual_card_id } ) else: res = int(res) balance = int(balance) * 10 fee = int(fee) * 10 oper = int(oper) MessageSender.send( device=self.device, cmd=220, payload={ 'res': res, 'balance': balance, 'card_cst': fee, 'card_ope': oper, 'card_id': cardNoHex, 'funCode': '10', 'isVir': isVir } ) def check_power_price_rules(self, packages): save_configs = [] lastMaxPower = 0 for package in packages: try: minPower = float(package.get('min')) maxPower = float(package.get('max')) price = float(package.get('price')) except Exception: raise InvalidParameter(u'价格规则填写错误,请正确的输入每个功率段') self.check_params_range(str(price), minData=0.0, maxData=10.0, desc='功率段价格') if maxPower > 9999: raise InvalidParameter(u'最大功率不能超过9999W') if minPower != lastMaxPower: raise InvalidParameter(u'功率段之间有未覆盖到的区间,请正确设置功率段') else: lastMaxPower = maxPower save_configs.append({'min': minPower, 'max': maxPower, 'price': price}) return save_configs def get_power_price_rules(self): serverConfigs = self.get_server_configs() packages = serverConfigs.get('power_price_rules', []) if not packages: packages = Const.POWER_PRICE_RULES return packages def get_account_rule(self): powerLevel = self.get_power_price_rules() account_rule = [] for pp in powerLevel: if 'max' in pp and 'price' in pp: account_rule.append( '{:0>4d}-{:0>8d}'.format(int(pp['max']), int(pp['price'] * 100 * 1000))) # price 单位:分 放大1000倍!! # 添加一个兜底的费用 maxOne = max(powerLevel, key=lambda _: _['max']) if maxOne['max'] < 9999: account_rule.append( '{:0>4d}-{:0>8d}'.format(9999, int(maxOne['price'] * 100 * 1000))) # price 单位:分 放大1000倍!! return account_rule def get_account_rule_new(self): powerLevel = self.get_power_price_rules() account_rule = [] for pp in powerLevel: if 'max' in pp and 'price' in pp: account_rule.append( '{:0>4d}-{:0>8d}'.format(int(pp['max']), int(pp['price'] * 100))) # price 单位:分 放大1000倍!! # 添加一个兜底的费用 maxOne = max(powerLevel, key=lambda _: _['max']) if maxOne['max'] < 9999: account_rule.append( '{:0>4d}-{:0>8d}'.format(9999, int(maxOne['price'] * 100))) # price 单位:分 放大1000倍!! return account_rule def get_current_use(self, base_data, spDict): port = spDict['port'] base_data['port'] = port device_info = self.send_mqtt(funCode='15', data=self.encode_str(port)) uart_data = device_info.get('data') if uart_data: base_data['power'] = self.decode_str(uart_data[14:18] if uart_data[14:18] != 'FFFF' else '0000') order_info = device_info.get('order_info', {}) if order_info: usedTime = round((time.time() - order_info['sts']) / 60.0, 1) needTime = round(order_info.get('need_time', 0) / 60.0, 1) base_data['leftTime'] = round(needTime - usedTime, 1) base_data['usedTime'] = usedTime base_data['needTime'] = '{}分钟'.format(needTime) base_data['needElec'] = round(order_info.get('need_elec', 0) / 3600000.0, 3) base_data['elec'] = round(order_info.get('elec', 0) / 3600000.0, 3) base_data['leftElec'] = round(base_data['needElec'] - base_data['elec'], 3) if not base_data.get('power'): base_data['power'] = order_info.get('power') # base_data['needPayMoney'] = '{}元'.format(round(order_info.get('need_pay', 0) / (100 * 1000.0), 2)) # precision = order_info.get('attach_paras', {}).get('precision', 1000) # base_data['consumeMoney'] = '{}元'.format(round(order_info.get('need_pay', 0) / (100.0 * precision), 2)) base_data['order'] = spDict['consumeOrder'] base_data['orderNo'] = spDict['consumeOrder']['orderNo'] try: order = ConsumeRecord.objects.filter(orderNo=base_data['orderNo']).first() if order.package.get('name') == '充满自停': base_data.pop('actualNeedTime', None) base_data.pop('needTime', None) base_data.pop('leftTime', None) base_data.pop('needElec', None) base_data.pop('leftElec', None) base_data['needTime'] = '充满自停' else: if order.package.get('unit') in ['分钟', '小时', '天', '秒']: base_data.pop('needElec', None) base_data.pop('leftElec', None) billingMethod = order_info.get('billingMethod', CONSUMETYPE.POSTPAID) if billingMethod == CONSUMETYPE.POSTPAID: precision = order_info.get('attach_paras', {}).get('precision', 1000) base_data['consumeMoney'] = '{}元'.format( round(order_info.get('need_pay', 0) / (100.0 * precision), 2)) else: base_data['usedTime'] = min(usedTime, needTime) base_data['consumeMoney'] = round( order_info['coins'] * 0.01 * usedTime / needTime, 2) elif order.package.get('unit') == '度': base_data.pop('needTime', None) base_data.pop('leftTime', None) billingMethod = order_info.get('billingMethod', CONSUMETYPE.POSTPAID) if billingMethod == CONSUMETYPE.POSTPAID: precision = order_info.get('attach_paras', {}).get('precision', 1000) base_data['consumeMoney'] = '{}元'.format( round(order_info.get('need_pay', 0) / (100.0 * precision), 2)) else: base_data['elec'] = min(base_data['elec'], base_data['needElec']) base_data['consumeMoney'] = round( order_info['coins'] * 0.01 * base_data['elec'] / base_data['needElec'], 2) else: pass except: pass # 更新按钮 if time.time() - spDict['start_time'] > 60: base_data.update(DeviceType.get_services_button(self.device['devType']['id'])) return [base_data, ] def force_stop_order(self, order, **kwargs): order.update(isNormal=False, errorDesc='经销商强制结束订单') def set_volume(self, volume): volume = self.check_params_range(volume, minData=0.0, maxData=8.0, desc='音量') try: self.send_mqtt(funCode='24', data=self.encode_str(volume), timeout=2) except: pass Device.get_collection().update_one(filter={'devNo': self.device.devNo}, update={'$set': {'otherConf.volume': volume, }}) Device.invalid_device_cache(self.device.devNo) def custom_push_url(self, order, user, **kw): from apps.web.utils import concat_user_center_entry_url from apps.web.utils import concat_front_end_url return concat_user_center_entry_url(agentId=user.productAgentId, redirect=concat_front_end_url( uri='/user/index.html#/user/deviceStatus')) def get_server_setting(self): return { 'minAfterStartCoins': self.device['otherConf'].get('minAfterStartCoins', 0), 'refundProtectionTime': self.get_server_configs().get('refundProtectionTime', 5), 'packages': self.get_power_price_rules(), 'timeElec': self.get_server_configs().get('timeElec', 1), 'coinRatio': self.get_server_configs().get('coinRatio', 1), 'onlineCardMinStartCoins': self.get_server_configs().get('onlineCardMinStartCoins', 0), 'onlineCardTime': self.get_server_configs().get('onlineCardTime', 720), 'onlineCardELec': self.get_server_configs().get('onlineCardELec', 3) } def set_server_setting(self, payload): updateConf = { } packages = payload.get('packages') if packages: updateConf.update({ 'otherConf.serverConfigs.power_price_rules': self.check_power_price_rules(packages) }) timeElec = payload.get('timeElec') if timeElec: updateConf.update({'otherConf.serverConfigs.timeElec': round(float(timeElec), 4)}) refundProtectionTime = payload.get('refundProtectionTime', None) if refundProtectionTime: updateConf.update({ 'otherConf.serverConfigs.refundProtectionTime': int(refundProtectionTime) }) coinRatio = payload.get('coinRatio') if coinRatio: updateConf.update({ 'otherConf.serverConfigs.coinRatio': round(float(coinRatio), 2) }) if 'minAfterStartCoins' in payload and payload['minAfterStartCoins']: minAfterStartCoins = self.check_params_range( payload['minAfterStartCoins'], minData=0.0, maxData=100.0, desc='设备启动最小余额') updateConf.update({ 'otherConf.minAfterStartCoins': float(minAfterStartCoins) }) onlineCardMinStartCoins = payload.get('onlineCardMinStartCoins') onlineCardTime = payload.get('onlineCardTime') onlineCardELec = payload.get('onlineCardELec') if onlineCardMinStartCoins and onlineCardTime and onlineCardELec: updateConf.update( { 'otherConf.serverConfigs.onlineCardMinStartCoins': round(float(onlineCardMinStartCoins), 2), 'otherConf.serverConfigs.onlineCardTime': round(float(onlineCardTime), 2), 'otherConf.serverConfigs.onlineCardELec': round(float(onlineCardELec), 4) }) self.device.update_device_obj(**updateConf) def format_port_using_detail(self, detailDict): detailDict = super(ChargingXiaoKeDouBox, self).format_port_using_detail(detailDict) if 'actualNeedTime' in detailDict: detailDict.pop('actualNeedTime') if 'coins' in detailDict: detailDict['coins'] = '{}元'.format(detailDict['coins']) return detailDict @property def support_device_package(self): return True def format_device_package(self, isTemp=False, **kw): def __generate_id(ids): i = 1 while True: if str(i) in ids: i += 1 else: return str(i) def __formart_ruleList(): packageList = kw['serviceData'] for item in packageList: item["sn"] = packageList.index(item) ids = [str(rule['id']) for rule in packageList if 'id' in rule] washConfig = {} for i, rule in enumerate(packageList): ruleId = str(rule.get('id', '')) if not ruleId: ruleId = __generate_id(ids) ids.append(ruleId) washConfig[ruleId] = {} if 'switch' in rule: washConfig[ruleId].update({'switch': rule['switch']}) if 'billingMethod' in rule: washConfig[ruleId].update({'billingMethod': rule['billingMethod']}) if rule['billingMethod'] == CONSUMETYPE.POSTPAID: washConfig[ruleId].update({'price': 0, 'coins': 0}) else: if int(self.device.driverVersion.split('.')[-1]) < 14: raise ServiceException( {'result': 0, 'description': '当前设备版本不支持预付费模式, 请联系经销商修改为 <后付费套餐> 或 升级设备版本','payload': {}}) if 'price' in rule: if float(rule['price']) <= 0: raise ServiceException( {'result': 0, 'description': '套餐( {} )支付金额必须大于0'.format(rule['name']), 'payload': {}}) washConfig[ruleId].update({'price': round(float(rule['price']), 2) or 0}) else: raise ServiceException( {'result': 0, 'description': '套餐( {} )支付金额未配置'.format(rule['name']), 'payload': {}}) if 'coins' in rule: if float(rule['coins']) <= 0: raise ServiceException( {'result': 0, 'description': '套餐( {} )支付币数必须大于0'.format(rule['name']), 'payload': {}}) washConfig[ruleId].update({'coins': round(float(rule['coins']), 2) or 0}) else: raise ServiceException( {'result': 0, 'description': '套餐( {} )支付币数未配置'.format(rule['name']), 'payload': {}}) if 'time' in rule: washConfig[ruleId].update({'time': rule['time'] or 0}) if 'name' in rule: washConfig[ruleId].update({'name': rule['name'] or '套餐{}'.format(i + 1)}) if 'unit' in rule: washConfig[ruleId].update({'unit': rule['unit']}) if 'sn' in rule: washConfig[ruleId].update({'sn': rule['sn']}) if 'autoStop' in rule: washConfig[ruleId]['autoStop'] = rule['autoStop'] if 'autoRefund' in rule: washConfig[ruleId]['autoRefund'] = rule['autoRefund'] if 'minAfterStartCoins' in rule: washConfig[ruleId]['minAfterStartCoins'] = rule['minAfterStartCoins'] if 'minFee' in rule: washConfig[ruleId]['minFee'] = rule['minFee'] if isTemp: pass # 检验部分 if RMB(rule.get('price') or 0) > self.device.owner.maxPackagePrice: raise ServiceException( {'result': 0, 'description': '套餐( {} )金额超限'.format(rule['name']), 'payload': {}}) return washConfig def __formart_displaySwitchs(): displaySwitchs = kw.get('displaySwitchs', { 'displayCoinsSwitch': True, 'displayTimeSwitch': True, 'displayPriceSwitch': True, 'setPulseAble': False, 'setBasePriceAble': False } ) if self.washConfig: if self.washConfig.values()[0]['billingMethod'] == CONSUMETYPE.POSTPAID: displaySwitchs.update({ 'displayCoinsSwitch': False, 'displayPriceSwitch': False, 'setPulseAble': False, 'setBasePriceAble': False }) return displaySwitchs self.washConfig = __formart_ruleList() self.displaySwitchs = __formart_displaySwitchs() return self.washConfig, self.displaySwitchs 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) # 显示展示给用户信息部分 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] billingMethod = payload.get("billAsService").get('billingMethod') washConfig[str(ruleId)] = { 'billingMethod': billingMethod, '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') } self.device.update_device_obj(**{ 'washConfig': washConfig, 'otherConf.displaySwitchs': displaySwitchs, 'otherConf.billingMethod': billingMethod, '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), 'billingMethod': rule.get('billingMethod'), } 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 } if "billingMethod" in self.device.my_obj.otherConf: billingMethod = self.device.my_obj.otherConf.get('billingMethod') billAsService["billingMethod"]= billingMethod else: billAsService["billingMethod"] = CONSUMETYPE.BILL_AS_SERVICE_POSTPAID ruleList = sorted(ruleList, key=lambda x: (x.get('sn'), x.get('id'))) return { 'ruleList': ruleList, 'billAsService': billAsService, 'devData': devData, 'displaySwitchs': displaySwitchs, } # def dealer_show_package(self, isTemp=False): # # def get_rule_list(): # if isTemp: # config = self.device.get('tempWashConfig', {}) # else: # config = self.device['washConfig'] # # ruleList = [] # for packageId, rule in config.items(): # item = { # 'id': packageId # } # # if 'switch' in rule: # item['switch'] = rule['switch'] # # if 'name' in rule: # item['name'] = rule['name'] # # if 'billingMethod' in rule: # item['billingMethod'] = rule['billingMethod'] # # if 'coins' in rule: # item['coins'] = rule['coins'] # # if 'price' in rule: # item['price'] = rule['price'] # # if 'time' in rule: # item['time'] = rule['time'] # # if 'description' in rule: # item['description'] = rule['description'] # # if 'unit' in rule: # item['unit'] = rule['unit'] # # if 'imgList' in rule: # item['imgList'] = rule['imgList'] # # if 'sn' in rule: # item['sn'] = rule['sn'] # # if 'autoStop' in rule: # item['autoStop'] = rule['autoStop'] # # if 'minAfterStartCoins' in rule: # item['minAfterStartCoins'] = rule['minAfterStartCoins'] # # if 'minFee' in rule: # item['minFee'] = rule['minFee'] # # ruleList.append(item) # # return sorted(ruleList, key=lambda x: (x.get('sn'), x.get('id'))) # # def get_display_switchs(): # if "displaySwitchs" in self.device["otherConf"]: # displaySwitchs = self.device["otherConf"].get('displaySwitchs') # else: # displaySwitchs = {'displayCoinsSwitch': True, # 'displayTimeSwitch': True, # 'displayPriceSwitch': True, # "setPulseAble": False, # "setBasePriceAble": False} # return displaySwitchs # # ruleList = get_rule_list() # displaySwitchs = get_display_switchs() # # return {"ruleList": ruleList, 'displaySwitchs': displaySwitchs}