123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713 |
- # -*- 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}
|