# -*- coding: utf-8 -*- # !/usr/bin/env python import datetime import logging import re import time from decimal import Decimal from typing import TYPE_CHECKING from apilib.monetary import RMB, VirtualCoin from apilib.utils import is_number from apilib.utils_datetime import to_datetime from apps import serviceCache from apps.web.constant import Const, DeviceCmdCode, CONSUMETYPE from apps.web.core.adapter.base import SmartBox from apps.web.core.exceptions import ServiceException, InvalidParameter from apps.web.core.networking import MessageSender from apps.web.device.models import Device, Group from apps.web.user.models import Card, CardRechargeOrder, ConsumeRecord, MyUser logger = logging.getLogger(__name__) if TYPE_CHECKING: pass class ChargingZhongShanBox(SmartBox): def __init__(self, device): super(ChargingZhongShanBox, self).__init__(device) # 解析获取设备信息的返回报文 def analyze_event_data(self, device_data): uart_data = device_data.get("data") if not uart_data: return data = str(uart_data) if data[:2] != "CC": return consumeModule = self.device["otherConf"].get("deviceConfigs", {}).get("consumeModule") if consumeModule is None: consumeModule = self.get_function_switch().get("consumeModule") self.get_consumption_rules() self._device = Device.get_dev(self.device.devNo) if self.device.devTypeCode == Const.DEVICE_TYPE_CODE_CHARGE_ZHONGSHAN_BILLASSERVICE: billingType = "billAsService" if consumeModule == 1 else "elec" else: billingType = "time" if consumeModule == 1 else "elec" cmd = data[4:6] if cmd == "99": return {"cmd":cmd} if cmd == "04": port = self.decode_str(data[18:20]) cardNo = "{:0>10}".format(self.decode_str(data[20:28])) eventCode = data[28:30] fee = self.decode_str(data[30:32], ratio=0.1, base=10) cardType = "IC" if data[32:34] == "02" else "ID" cardBalance = self.decode_str(data[34:38], ratio=0.1, base=10) return {"cmd": cmd, "cardNo": cardNo, "port": port, "eventCode": eventCode, "fee": fee, "cardType": cardType, "cardBalance": cardBalance, "billingType": billingType, "uart_data": uart_data} elif cmd == "05": reasonDict = { "01": "购买的充电时间、电量用完了", "02": "用户手动停止(拔插头,或是按了停止按钮)", "03": "您的爱车充满电了", "04": "设备或是端口出现问题,被迫停止", "05": "因充电器功率超过充电站的单路最大输出功率,切断输出", "06": "超高温(达到75度)", "07": "烟雾报警", "08": "漏电报警", "09": "负载电流过大(短路)", "0A": "开始充电未接充电器", "0B": "端口远程停止", } consumeTypeDict = { "01": "投币消费", "02": "离线卡消费", "03": "在线卡消费", "04": "扫码消费", "05": "远程开启", } port = self.decode_str(data[18:20]) reasonCode = data[24:26] reason = reasonDict.get(reasonCode) backMoney = self.decode_str(data[34:38], ratio=0.01, base=10) consumeTypeCode = data[38:40] consumeType = consumeTypeDict.get(consumeTypeCode) result = {"cmd": cmd, "port": port, "reasonCode": reasonCode, "reason": reason, "backMoney": backMoney, "consumeTypeCode": consumeTypeCode, "consumeType": consumeType, "billingType": billingType, "uart_data": uart_data } if "ack_event_id" in device_data: result["ack_event_id"] = device_data["ack_event_id"] if billingType == "time": leftTime = self.decode_str(data[20:24]) result["leftTime"] = leftTime elif billingType == "billAsService": serviceFee = self.decode_str(data[20:24], ratio=0.01) result["serviceFee"] = serviceFee else: leftElec = self.decode_str(data[20:24], ratio=0.01) result["leftElec"] = leftElec if consumeTypeCode == "02": # 离线卡 cardNo = "{:0>10}".format(self.decode_str(data[26:34])) result["cardNo"] = cardNo elif consumeTypeCode == "03": # 扫码 cardNo = "{:0>10}".format(self.decode_str(data[26:34])) result["cardNo"] = cardNo elif consumeTypeCode == "01": # 线下投币 try: rechargeCode = self.decode_long_hex_to_list(data[26:34]) rechargeCode = "".join(rechargeCode[:3]) result["rechargeCode"] = rechargeCode except Exception: result["rechargeCode"] = "000" else: pass return result elif cmd == "09": errorDict = { "01": "端口输出故障", "02": "机器整机功率过大", "03": "电源故障", "04": "机箱超温(高于80摄氏度)", "05": "烟雾警告", "06": "漏电", "07": "端口故障", "08": "其他", } port = data[18:20] if data[18:20] != "FF" else None FaultCode = data[20:22] fault = errorDict.get(FaultCode) return {"status": Const.DEV_WORK_STATUS_FAULT, "statusInfo": fault, "cmdCode": cmd, "port": port, "FaultCode": FaultCode, "billingType": billingType, "uart_data": uart_data} elif cmd == "1D": cardNo = "{:0>10}".format(self.decode_str(data[18:26])) cardType = "IC" if data[26:28] == "02" else "ID" cardBalance = self.decode_str(data[28:32], ratio=0.1, base=10) return {"cmd": cmd, "cardNo": cardNo, "cardType": cardType, "cardBalance": cardBalance, "billingType": billingType, "uart_data": uart_data} elif cmd == "1F": cardNo = "{:0>10}".format(self.decode_str(data[18:26])) cardType = "IC" if data[26:28] == "02" else "ID" ayscMoney = self.decode_str(data[28:32], ratio=0.1, base=10) cardBalance = self.decode_str(data[32:36], ratio=0.1, base=10) status = data[36:38] return {"cmd": cmd, "cardNo": cardNo, "cardType": cardType, "ayscMoney": ayscMoney, "cardBalance": cardBalance, "status": status, "billingType": billingType, "uart_data": uart_data} elif cmd == "03": port = self.decode_str(data[18:20]) coins = self.decode_str(data[20:22], ratio=0.1, base=10) return {"cmd": cmd, "port": port, "coins": coins, "billingType": billingType, "uart_data": uart_data} elif cmd == "F1": # 主板心跳 data = self.decode_str(data[18:20]) return {"cmd": cmd, "billingType": billingType, "data": data, "uart_data": uart_data} else: pass def _check_package(self, package): """ 获取设备启动的发送数据 根据设备的当前模式以及套餐获取 :param package: :return: """ consumeModule = self.device.get("otherConf", dict()).get("deviceConfigs", dict()).get("consumeModule",None) if consumeModule is None: consumeModule = int(self.get_function_switch().get("consumeModule")) self.get_consumption_rules() self._device = Device.get_dev(self.device.devNo) unit = package.get("unit", u"分钟") _time = float(package.get("time", 0)) if self.device.devTypeCode == Const.DEVICE_TYPE_CODE_CHARGE_ZHONGSHAN_BILLASSERVICE: if consumeModule == 1: billingType = "billAsService" else: billingType = "elec" if unit != u"度": raise ServiceException({"result": 2, "description": u"套餐单位错误,请联系经销商"}) else: _time = _time else: # 按时间计费 if consumeModule == 1: billingType = "time" if unit == u"小时": _time = _time * 60 elif unit == u"天": _time = _time * 24 * 60 elif unit == u"秒": _time = _time / 60 elif unit == u"分钟": _time = _time else: raise ServiceException({"result": 2, "description": u"套餐单位错误,请联系经销商"}) # 按电量计费 else: billingType = "elec" if unit != u"度": raise ServiceException({"result": 2, "description": u"套餐单位错误,请联系经销商"}) else: _time = _time return _time, unit, billingType 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 encode_str(data, length=2, ratio=1.0, base=16): # type:(str,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): # type:(str,float,int) -> str """ ratio:比率单位转换 """ if not isinstance(data, str): data = str(data) return "%.10g" % (int(data, base) * ratio) @staticmethod def reverse_hex(data): # type:(str) -> str if not isinstance(data, str): raise TypeError return "".join(list(reversed(re.findall(r".{2}", data)))) 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 ServiceException({"result": 2, "description": 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 ServiceException({"result": 2, "description": 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 ServiceException({"result": 2, "description": 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 ServiceException( {"result": 2, "description": u"%s参数超出可选范围,可取范围为%g-%g" % (desc, minData, maxData)}) def send_mqtt(self, funCode, data, cmd=DeviceCmdCode.OPERATE_DEV_SYNC): """ 发送mqtt 指令210 返回data """ if not isinstance(funCode, str): funCode = str(funCode) if not isinstance(data, str): data = str(data) result = MessageSender.send(self.device, cmd, {"IMEI": self.device["devNo"], "funCode": funCode, "data": data}) 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 result.get("data") == "00": raise ServiceException({"result": 2, "description": u"设备操作失败.请重试"}) else: return str(result["data"][18:-2]) @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): try: 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"}) except Exception: pass def send_F1_heartbeat(self): self.send_mqtt(funCode="F1", data="00") def do_update_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 start_device(self, package, openId, attachParas): # type: (...)-> dict if attachParas is None: raise ServiceException({"result": 2, "description": u"请您选择合适的充电线路、电池类型信息"}) if "chargeIndex" not in attachParas: raise ServiceException({"result": 2, "description": u"请您选择合适的充电线路"}) dev = Device.objects.get(devNo=self.device["devNo"]) refundProtection = dev.otherConf.get("refundProtection", 0) refundProtectionTime = dev.otherConf.get("refundProtectionTime", 5) port = attachParas["chargeIndex"] portHex = self.encode_str(port, 2) _time, unit, billingType = self._check_package(package) coins = float(package.get("coins", 0)) coinsHex = self.encode_str(coins, length=4, ratio=10, base=10) unitHex = self.encode_str(_time, length=4) unit = package["unit"] price = float(package['price']) if unit == "度": unitHex = self.encode_str(_time, length=4, ratio=100) orderNo = attachParas.get("orderNo") data = portHex + coinsHex + unitHex logger.info("lz_start_device devNo:{}, cmd:220 funcCode:02 uart_send:{}".format(self.device["devNo"], data)) devInfo = MessageSender.send( device=self.device, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, payload={ "IMEI": self.device["devNo"], "funCode": "02", "data": data }, timeout=120 ) if "rst" in devInfo and devInfo["rst"] != 0: if devInfo["rst"] == -1: raise ServiceException( {"result": 2, "description": u"充电桩正在玩命找网络,您的金币还在,重试不需要重新付款,建议您试试旁边其他设备,或者稍后再试哦"}) elif devInfo["rst"] == 1: self.check_serial_port_for_startcmd(attachParas["chargeIndex"]) # 收到回应报文后,需要ack响应报文。 logger.info("lz_start_device devNo:{}, funcCode:02 uart_rece:{}".format(self.device["devNo"], devInfo)) data = devInfo["data"][18::] usePort = int(attachParas["chargeIndex"]) result = data[2:4] if result == "01": # 成功 pass elif result == "02": newValue = {str(usePort): {"status": Const.DEV_WORK_STATUS_FAULT, "statusInfo": u"充电站故障"}} Device.update_dev_control_cache(self.device["devNo"], newValue) raise ServiceException({"result": 2, "description": u"充电站故障"}) elif result == "03": newValue = {str(usePort): {"status": Const.DEV_WORK_STATUS_WORKING, "statusInfo": u""}} Device.update_dev_control_cache(self.device["devNo"], newValue) raise ServiceException({"result": 2, "description": u"该端口正在使用中"}) portDict = { "startTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "status": Const.DEV_WORK_STATUS_WORKING, "billingType": billingType, "isStart": True, "openId": openId, "refunded": False, "refundProtection": refundProtection, "refundProtectionTime": refundProtectionTime, "doPowerLevel": False, "consumeType": "mobile", "orderNo": orderNo, "port": usePort } servicedInfo = {"chargeIndex":usePort} ConsumeRecord.objects.filter(orderNo = orderNo).update(servicedInfo=servicedInfo,remarks='扫码启动') ctrInfo = Device.get_dev_control_cache(self.device["devNo"]) lastPortInfo = ctrInfo.get(str(usePort), {}) if (lastPortInfo is not None) and \ lastPortInfo.get('status', '') == Const.DEV_WORK_STATUS_WORKING and \ lastPortInfo.get('openId') == openId and \ lastPortInfo.get('billingType') == billingType: is_continus = True else: is_continus = False if 'linkedRechargeRecordId' in attachParas: portDict['rechargeRcdId'] = str(attachParas['linkedRechargeRecordId']) if billingType == "time": if is_continus: portDict["coins"] = float(coins) + lastPortInfo["coins"] portDict["price"] = float(price) + lastPortInfo["price"] portDict["needTime"] = _time + lastPortInfo["needTime"] else: portDict["coins"] = float(coins) portDict["price"] = float(price) portDict.update({"needTime": _time}) finishedTime = int(time.time()) + int(portDict["needTime"] * 60) elif billingType == "elec": if is_continus: portDict["coins"] = float(coins) + lastPortInfo["coins"] portDict["needElec"] = _time + lastPortInfo["needElec"] else: portDict["coins"] = float(coins) portDict.update({"needElec": _time}) finishedTime = int(time.time()) + 60 * 60 * 12 else: if is_continus: portDict["coins"] = float(coins) + lastPortInfo["coins"] else: portDict["coins"] = float(coins) finishedTime = int(time.time()) + 60 * 60 * 12 portDict.update({"finishedTime": finishedTime}) if "orderNo" in attachParas: portDict.update({"orderNo": attachParas["orderNo"]}) Device.update_port_control_cache(self.device["devNo"], portDict) devInfo["finishedTime"] = finishedTime return devInfo def get_device_ID(self): # type:() -> dict """ 获取设备唯一ID funcCode : 55 """ result = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {"IMEI": self.device["devNo"], "funCode": "55", "data": "00"}) 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 result.get("data") == "00": raise ServiceException({"result": 2, "description": u"设备操作失败.请重试"}) else: data = result.get("data") data_list = self.decode_long_hex_to_list(data[6:-2], base=10) return {"deviceId": "".join(data_list)} def get_default_port_nums(self): allPorts = self.device["otherConf"].get("deviceConfigs", {}).get("allPorts") if not allPorts: data = self.send_mqtt(funCode="01", data="00") allPorts = int(data[:2], 16) self.do_update_configs({"allPorts": allPorts}) return allPorts def get_mcu_version(self): devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {"IMEI": self.device["devNo"], "funCode": "25", "data": "00"}) if "rst" in devInfo and devInfo["rst"] != 0: if devInfo["rst"] == -1: raise ServiceException( {"result": 2, "description": u"充电桩正在玩命找网络,请您稍候再试"}) elif devInfo["rst"] == 1: raise ServiceException( {"result": 2, "description": u"充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能"}) confData = devInfo["data"][18::] mcuVersion = int(confData[0:4], 16) return {"mcuVersion": mcuVersion} def get_port_status(self, force=False): if force: return self.get_all_port_status() ctrInfo = Device.get_dev_control_cache(self.device.devNo) if "allPorts" in ctrInfo and ctrInfo["allPorts"] > 0: allPorts = ctrInfo["allPorts"] else: allPorts = self.get_default_port_nums() statusDict = {} for ii in range(allPorts): tempDict = ctrInfo.get(str(ii + 1), {}) if "status" in tempDict: statusDict[str(ii + 1)] = {"status": tempDict.get("status")} elif "isStart" in tempDict: if tempDict["isStart"]: statusDict[str(ii + 1)] = {"status": Const.DEV_WORK_STATUS_WORKING} else: statusDict[str(ii + 1)] = {"status": Const.DEV_WORK_STATUS_IDLE} else: statusDict[str(ii + 1)] = {"status": Const.DEV_WORK_STATUS_IDLE} return statusDict def get_port_info(self, port): # type:(str) -> dict """ 获取单个端口状态 funcCode : 06 发送:CC09060009090909090107 返回:AA0D06000909090909 01 0054 0364 30 """ data = self.encode_str(port, 2) data = self.send_mqtt(funCode="06", data=data) if self.device.bill_as_service_feature.on: billingType = "billAsService" if self.device["otherConf"].get("deviceConfigs", dict()).get("consumeModule", 0) == 1 else "elec" else: billingType = "time" if self.device["otherConf"].get("deviceConfigs", dict()).get("consumeModule", 0) == 1 else "elec" port = self.decode_str(data[:2]) if billingType == "time": leftTime = int(self.decode_str(data[2:6])) else: if self.device.bill_as_service_feature.on: if billingType == 'billAsService': serviceFee = float(self.decode_str(data[2:6], ratio=0.01)) else: leftElec = float(self.decode_str(data[2:6], ratio=0.01)) leftTime = int(self.decode_str(data[10:14])) else: leftElec = float(self.decode_str(data[2:6], ratio=0.01)) leftTime = int(self.decode_str(data[10:14])) power = self.decode_str(data[6:10], ratio=0.1, base=10) leftBalance = self.decode_str(data[14:18], ratio=0.01, base=10) result = locals() result.pop("self") return result def get_all_port_status(self): # type:() -> dict """ 获取所有端口的状态 空闲,使用,禁用,故障 funcCode : 01 发送:CC09010009090909090001 返回:AA1301000909090909 0A 1000 00 00 00 00 00 00 00 00 01 """ data = self.send_mqtt(funCode="01", data="00") result = {} allPorts = int(data[:2], 16) usedPorts = 0 portData = re.findall(r"..", data[2:]) for i, statusTemp in enumerate(portData[:allPorts]): if statusTemp == "00": status = {"status": Const.DEV_WORK_STATUS_IDLE} elif statusTemp == "01": status = {"status": Const.DEV_WORK_STATUS_WORKING} usedPorts += 1 elif statusTemp == "02": status = {"status": Const.DEV_WORK_STATUS_FORBIDDEN} usedPorts += 1 elif statusTemp == "03": status = {"status": Const.DEV_WORK_STATUS_FAULT} usedPorts += 1 else: status = {"status": Const.DEV_WORK_STATUS_FAULT} usedPorts += 1 result[str(i + 1)] = status usePorts = allPorts - usedPorts # 这里存在多线程更新缓存的场景,可能性比较低,但是必须先取出来,然后逐个更新状态,然后再记录缓存 ctrInfo = Device.get_dev_control_cache(self.device["devNo"]) for strPort, info in result.items(): if strPort in ctrInfo: ctrInfo[strPort].update({"status": info["status"]}) else: ctrInfo[strPort] = info ctrInfo.update({ "allPorts": allPorts, "usedPorts": usedPorts, "usePorts": usePorts }) Device.update_dev_control_cache(self.device.devNo, ctrInfo) # 01指令读不到非法充电的充电状态,所以需要从缓存中查找是否有非法充电的端口 cacheInfo = Device.get_dev_control_cache(self.device.devNo) for k,v in cacheInfo.items(): if type(k) == unicode: k = k.encode('gbk') if str.isdigit(k): if v.get('statusErrorInfo') == '非法充电': if result[k]["status"] == Const.DEV_WORK_STATUS_IDLE: result[k] = {"status": Const.DEV_WORK_STATUS_WORKING,"errorInfo":True} return result def get_all_port_info(self): # type:() -> dict """ 获取全部端口的信息 funcCode : 07 发送:CC09070009090909090007 返回:AA330700000000000100081D00000000000000000000000000000000000519840011006000000000000000000000000000000000C9 """ if self.device.devTypeCode == Const.DEVICE_TYPE_CODE_CHARGE_ZHONGSHAN_BILLASSERVICE: billingType = "billAsservice" if self.device["otherConf"].get("deviceConfigs", dict()).get("consumeModule", 0) == 1 else "elec" else: billingType = "time" if self.device["otherConf"].get("deviceConfigs", dict()).get("consumeModule", 0) == 1 else "elec" data = self.send_mqtt(funCode="07", data="00") totalAmpere = self.decode_str(data[:4], ratio=0.1, base=10) temperature = self.decode_str(data[4:6]) data = data[6:] lis = [] for i in xrange(0, 80, 16): lis.append(data[i:i + 16]) allPorts = self.get_default_port_nums() if allPorts > 5: data = self.send_mqtt(funCode="30", data="00") for i in xrange(0, 80, 16): lis.append(data[i:i + 16]) result = {} if billingType == "time": for index, item in enumerate(lis): result[str(index + 1)] = { "leftTime": int(self.decode_str(item[:4])), "power": self.decode_str(item[4:8], ratio=0.1, base=10), "leftBalance": self.decode_str(item[12:], ratio=0.01, base=10) } elif billingType == "elec": for index, item in enumerate(lis): result[str(index + 1)] = { "leftElec": float(self.decode_str(item[:4], ratio=0.01)), "power": self.decode_str(item[4:8], ratio=0.1, base=10), "leftTime": int(self.decode_str(item[8:12])), "leftBalance": self.decode_str(item[12:], ratio=0.01, base=10) } elif billingType == "billAsservice": # 新增电费+服务费模式 for index, item in enumerate(lis): result[str(index + 1)] = { "serviceFee": float(self.decode_str(item[:4],ratio=0.01)), "power": self.decode_str(item[4:8], ratio=0.1, base=10), "leftTime": int(self.decode_str(item[8:12])), "leftBalance": self.decode_str(item[12:], ratio=0.01, base=10) } result.update({"totalAmpere": totalAmpere, "temperature": temperature, "billingType": billingType}) return result def get_port_status_from_dev(self): all_port_status = self.get_all_port_status() # 做一个兼容,不是所有主板都支持06指令,支持06指令的主板优先是由06指令查询到的数据 try: all_port_detail = dict() for port, status in all_port_status.items(): if status['status'] != 0: port_detail = self.get_port_info(port) if float(port_detail['power']) <= 5 and status['errorInfo']: Device.clear_port_control_cache(self.device.devNo, port) all_port_detail.update({port: port_detail}) except: all_port_detail = self.get_all_port_info() cacheInfo = Device.get_dev_control_cache(self.device.devNo) parse_status_code = { "0": u"端口空闲", "1": u"端口正在使用", "2": u"端口禁用", "3": u"端口故障", } lis = [] for port, info in all_port_status.items(): lineInfo = cacheInfo.get(port, {}) if info["status"] == 0: item = {"index": port, "status": "idle", "desc": parse_status_code.get(str(info["status"]))} elif info["status"] == 1: if not lineInfo.get("startTime"): # 端口启动 没有任何订单消息 最直接的原因就经销商直接换了模块 服务器没有任何信息 item = {"index": port, "status": "busy", "desc": parse_status_code.get(str(info["status"])), "leftElec": all_port_detail.get(port, {}).get("leftElec"), "leftMoney": all_port_detail.get(port, {}).get("leftBalance"), "leftTime": all_port_detail.get(port, {}).get("leftTime"), "power": all_port_detail.get(port, {}).get("power"), } else: nowTime = datetime.datetime.now() startTime = to_datetime(lineInfo["startTime"]) usedTime = int(round((((nowTime - startTime).total_seconds() + 59) / 60.0))) if self.device.devTypeCode == Const.DEVICE_TYPE_CODE_CHARGE_ZHONGSHAN_BILLASSERVICE: billingType = lineInfo.get("billingType") if billingType == "elec": item = { "startTime": lineInfo.get("startTime", '')[2:], "index": port, "status": "busy", "usedTime": usedTime, "power": all_port_detail.get(port, {}).get("power"), "desc": parse_status_code.get(str(info["status"])), "leftMoney": all_port_detail.get(port, {}).get("leftBalance"), "consumeMoney": round(float(lineInfo.get("coins")) - float(all_port_detail.get(port, {}).get("leftBalance")),2), "coins": str(lineInfo.get("coins")) + "币", "consumeType": lineInfo.get("consumeType"), "leftElec":all_port_detail.get(port, {}).get("leftElec"), "needElec":lineInfo['needElec'], "usedElec":round(float(lineInfo['needElec']) - float(all_port_detail.get(port, {}).get("leftElec")), 2), "leftTime":all_port_detail.get(port, {}).get("leftTime") } # 记录非法充电的功率,非法充电时功率只能通过06指令查到 elif billingType == "faultCharge": # data = self.encode_str(port, 2) # res = self.send_mqtt(funCode='06',data=data) item = { "startTime": lineInfo.get("startTime", '')[2:], "index": port, "status": "busy", "usedTime": usedTime, "power": all_port_detail.get(port, {}).get("power"), "statusErrorInfo": lineInfo.get('statusErrorInfo') } else: item = { "startTime": lineInfo.get("startTime", '')[2:], "index": port, "status": "busy", "usedTime": usedTime, "power": all_port_detail.get(port, {}).get("power"), "desc": parse_status_code.get(str(info["status"])), "leftMoney": all_port_detail.get(port, {}).get("leftBalance"), "consumeMoney": round( float(lineInfo.get("coins")) - float( all_port_detail.get(port, {}).get("leftBalance")), 2), "coins": str(lineInfo.get("coins")) + "币", "consumeType": lineInfo.get("consumeType"), "serviceFee" : all_port_detail.get(port, {}).get("serviceFee"), "elecFee" : round(float(lineInfo.get("coins")) - float(all_port_detail.get(port, {}).get("leftBalance")) - float(all_port_detail.get(port, {}).get("serviceFee")),2), } else: billingType = lineInfo.get("billingType") if billingType == "faultCharge": item = { "startTime": lineInfo.get("startTime", '')[2:], "index": port, "status": "busy", "usedTime": usedTime, "power": all_port_detail.get(port, {}).get("power"), "statusErrorInfo": lineInfo.get('statusErrorInfo') } else: item = { "startTime": lineInfo.get("startTime", '')[2:], "index": port, "status": "busy", "leftTime": all_port_detail.get(port, {}).get("leftTime"), "usedTime": usedTime, "actualNeedTime": usedTime + int(all_port_detail.get(port, {}).get("leftTime")), "leftElec": all_port_detail.get(port, {}).get("leftElec"), "power": all_port_detail.get(port, {}).get("power"), "desc": parse_status_code.get(str(info["status"])), "leftMoney": all_port_detail.get(port, {}).get("leftBalance"), "consumeMoney": round(float(lineInfo.get("coins")) - float(all_port_detail.get(port, {}).get("leftBalance")),2), "coins": str(lineInfo.get("coins")) + "币", "consumeType": lineInfo.get("consumeType"), } if 'needElec' in lineInfo: item['needElec'] = lineInfo['needElec'] item['usedElec'] = round(float(lineInfo['needElec']) - float(item['leftElec']), 2) if lineInfo.get("cardNo") and lineInfo.get("cardNo") != "0000000000": item.update({"cardNo": lineInfo.get("cardNo")}) if lineInfo.get("openId"): user = MyUser.objects.filter(openId=lineInfo.get("openId"), groupId=self.device["groupId"]).first() if user: item.update({"nickName": user.nickname}) if 'rechargeRcdId' in lineInfo: item['coins'] = str(lineInfo.get("coins")) + "元" if 'needTime' in lineInfo: item['needTime'] = lineInfo['needTime'] elif info["status"] == 2: item = {"index": port, "status": "ban", "desc": parse_status_code.get(str(info["status"]))} elif info["status"] == 3: item = {"index": port, "status": "fault", "desc": parse_status_code.get(str(info["status"]))} else: pass lis.append(item) lis = sorted(lis, key=lambda x: int(x["index"])) return lis @property def isHaveStopEvent(self): return True def test(self, port="1", coins="1", time="100", elec="0"): # type:(...) -> dict """ 启动某个端口 funcCode : 02 """ portHex = self.encode_str(port, 2) coinsHex = self.encode_str(coins, 4, ratio=10) if elec: unitHex = self.encode_str(int(elec) * 100, 4) else: unitHex = self.encode_str(int(time), 4) data = portHex + coinsHex + unitHex result = self.send_mqtt(funCode="02", data=data) if result: port = result[:2] status = "Charging success" if result[2:4] == "01" else "Charging failed" return {"port": port, "status": status} def remote_device(self, port, mode="start"): # type:(str,str) -> dict """ 远程操作某个端口 funCode: 08 发送:CC0A08000909090909020009 返回:AA0B0800090909090902021913" """ port = self.encode_str(port) if mode == "start": data = port + "01" elif mode == "stop": data = port + "00" else: return {} data = self.send_mqtt(funCode="08", data=data) # TODO parse dataHex port = self.decode_str(data[:2]) result = self.decode_str(data[4:], base=16) if self.device.devTypeCode == Const.DEVICE_TYPE_CODE_CHARGE_ZHONGSHAN_BILLASSERVICE: billingType = self.device["otherConf"].get("deviceConfigs", dict()).get("consumeModule", "elec") if billingType == "serviceFee": return {"port": port, "serviceFee": result} else: return {"port": port, "leftElec": result} else: return {"port": port, "leftTime": result} def set_consumption_rules(self, configDict): # type:(dict) -> None """ 设置单次刷卡,投币,远程 用户获取量(时间:分钟 ,电量: 度) funCode: 0A """ if self.device.devTypeCode == Const.DEVICE_TYPE_CODE_CHARGE_ZHONGSHAN_BILLASSERVICE: remoteElec = self.check_params_range(params=configDict.get("remoteElec"), minData=0, maxData=5.00, desc="扫码计费标准") serviceCharge = self.check_params_range(params=configDict.get("serviceFee"), minData=0, maxData=5.00, desc="服务费计费标准") time = self.check_params_range(params=configDict.get("time"), minData=0, maxData=600, desc="消费1元充电时间上限") coinElec = self.check_params_range(params=configDict.get("coinElec"), minData=0, maxData=5.00, desc="投币计费标准") cardElec = self.check_params_range(params=configDict.get("cardElec"), minData=0, maxData=5.00, desc="刷卡计费标准") data = self.encode_str(time, length=4, ratio=1) data += self.encode_str(coinElec, length=4, ratio=100) data += self.encode_str(cardElec, length=4, ratio=100) data += self.encode_str(remoteElec, length=4, ratio=100) data += self.encode_str(serviceCharge, length=4, ratio=100) self.send_mqtt(funCode="0A", data=data) self.device.update_device_obj(**{ 'devType.features.billAsService.serviceCharge': serviceCharge, 'devType.features.billAsService.elecCharge': remoteElec, }) else: coinTime = self.check_params_range(params=configDict.get("coinTime"), minData=0, maxData=600, desc="投币一个的充电时间") coinElec = self.check_params_range(params=configDict.get("coinElec"), minData=0, maxData=5.00, desc="投币一个的充电电量") cardTime = self.check_params_range(params=configDict.get("cardTime"), minData=0, maxData=600, desc="刷卡一次的充电时间") cardElec = self.check_params_range(params=configDict.get("cardElec"), minData=0, maxData=5.00, desc="刷卡一次的充电电量") remoteTime = self.check_params_range(params=configDict.get("remoteTime"), minData=0, maxData=600, desc="扫码一元的充电时间") remoteElec = self.check_params_range(params=configDict.get("remoteElec"), minData=0, maxData=5.00, desc="扫码一元的充电电量") data = self.encode_str(coinTime, length=4, ratio=1) data += self.encode_str(coinElec, length=4, ratio=100) data += self.encode_str(cardTime, length=4, ratio=1) data += self.encode_str(cardElec, length=4, ratio=100) data += self.encode_str(remoteTime, length=4, ratio=1) data += self.encode_str(remoteElec, length=4, ratio=100) self.send_mqtt(funCode="0A", data=data) def get_consumption_rules(self): # type:() -> dict """ 获取单次刷卡,投币,远程 用户获取量(时间:分钟 ,电量: 度) funCode: 0B """ data = self.send_mqtt(funCode="0B", data="00") if self.device.devTypeCode == Const.DEVICE_TYPE_CODE_CHARGE_ZHONGSHAN_BILLASSERVICE: time = self.decode_str(data[:4], ratio=1) coinElec = self.decode_str(data[4:8], ratio=0.01) cardElec = self.decode_str(data[8:12], ratio=0.01) remoteElec = self.decode_str(data[12:16], ratio=0.01) serviceCharge = self.decode_str(data[16:20], ratio=0.01) result = {"time": time, "coinElec": coinElec, "cardElec": cardElec, "remoteElec": remoteElec, "serviceFee": serviceCharge} self.do_update_configs(result) self.device.update_device_obj(**{ 'devType.features.billAsService.serviceCharge': serviceCharge, 'devType.features.billAsService.elecCharge': remoteElec, }) return result coinTime = self.decode_str(data[:4], ratio=1) coinElec = self.decode_str(data[4:8], ratio=0.01) cardTime = self.decode_str(data[8:12], ratio=1) cardElec = self.decode_str(data[12:16], ratio=0.01) remoteTime = self.decode_str(data[16:20], ratio=1) remoteElec = self.decode_str(data[20:24], ratio=0.01) result = {"coinTime": coinTime, "coinElec": coinElec, "cardTime": cardTime, "cardElec": cardElec, "remoteTime": remoteTime, "remoteElec": remoteElec} self.do_update_configs(result) return result def set_function_switch(self, configDict): # type:(dict) -> None """ 设置功能开关: ic卡(00关闭,01可用),刷卡退费(00关闭,01可用),充满自停(00关闭,01可用),免费充电(00关闭,01可用),消费模式(00电量,01时间) funCode: 0C """ coinSwitch = configDict.get("coinSwitch") cardSwitch = configDict.get("cardSwitch") cardRefund = configDict.get("cardRefund") autoStop = configDict.get("autoStop") chargeFree = configDict.get("chargeFree") consumeModule = configDict.get("consumeModule") data = self.encode_str(coinSwitch) data += self.encode_str(cardSwitch) data += self.encode_str(cardRefund) data += self.encode_str(autoStop) data += self.encode_str(chargeFree) data += self.encode_str(consumeModule) self.send_mqtt(funCode="0C", data=data) updateConfigs = { "coinSwitch": int(coinSwitch), "cardSwitch": int(cardSwitch), "cardRefund": int(cardRefund), "autoStop": int(autoStop), "chargeFree": int(chargeFree), "consumeModule": int(consumeModule) } self.do_update_configs(updateConfigs) if self.device.devTypeCode == Const.DEVICE_TYPE_CODE_CHARGE_ZHONGSHAN_BILLASSERVICE: billAsServiceSwitch = True if consumeModule == "1" else False self.device.update_device_obj(**{ 'devType.features.billAsService.on': billAsServiceSwitch, 'otherConf.deviceConfigs.consumeModule' : 1 if consumeModule == "1" else 0 }) def get_function_switch(self): # type:() -> dict """ 设置功能开关: ic卡(00关闭,01可用),刷卡退费(00关闭,01可用),充满自停(00关闭,01可用),免费充电(00关闭,01可用),消费模式(00电量,01时间) funCode: 0D """ data = self.send_mqtt(funCode="0D", data="00") result_list = self.decode_long_hex_to_list(data) # 给前台做兼容 结果的值转为数字 result_list = map(lambda x: int(x), result_list) name_list = ["coinSwitch", "cardSwitch", "cardRefund", "autoStop", "chargeFree", "consumeModule"] retult = dict(zip(name_list, result_list)) self.do_update_configs(retult) return retult def set_icMoney_maxPower_floatingCharge(self, configDict): # type:(dict) -> None """ 设置 刷卡金额 最大功率 浮充功率 浮充时间 funCode: 0E """ icMoney = self.check_params_range(params=configDict.get("icMoney"), minData=1, maxData=99, desc="单次刷卡需要消耗的金额") # TODO 单位角 maxPower = self.check_params_range(params=configDict.get("maxPower"), minData=1, maxData=999, desc="最大功率") floatingPower = self.check_params_range(params=configDict.get("floatingPower"), minData=1, maxData=99, desc="浮充功率") floatingTime = self.check_params_range(params=configDict.get("floatingTime"), minData=1, maxData=120, desc="浮充时间") data = self.encode_str(icMoney, length=2, base=10) # TODO 文档中是十进制待验证! data += self.encode_str(maxPower, length=4) data += self.encode_str(floatingPower, length=2) data += self.encode_str(floatingTime, length=2) result = self.send_mqtt(funCode="0E", data=data) def get_icMoney_maxPower_floatingCharge(self): # type:() -> dict """ 获取 刷卡金额 最大功率 浮充功率 浮充时间 funCode: 0F """ data = self.send_mqtt(funCode="0F", data="00") icMoney = self.decode_str(data[:2], base=10) maxPower = self.decode_str(data[2:6]) floatingPower = self.decode_str(data[6:8]) floatingTime = self.decode_str(data[8:10]) return {"icMoney": icMoney, "maxPower": maxPower, "floatingPower": floatingPower, "floatingTime": floatingTime} def set_noloadTime_volume(self, configDict): # type:(dict) -> None """ 设置 空载时间(noloadTime) 喇叭音量(volume) funCode: 10 """ noloadTime = self.check_params_range(params=configDict.get("noloadTime"), minData=1, maxData=180, desc="空载检测时间") volume = self.check_params_range(params=configDict.get("volume"), minData=0, maxData=15, desc="喇叭音量") data = self.encode_str(noloadTime) data += self.encode_str(volume) result = self.send_mqtt(funCode="10", data=data) def get_noloadTime_volume(self): # type:() -> dict """ 读取 空载时间(noloadTime) 喇叭音量(volume) funCode: 11 """ data = self.send_mqtt(funCode="11", data="00") noloadTime = self.decode_str(data[:2]) volume = self.decode_str(data[2:4]) return {"noloadTime": noloadTime, "volume": volume} def set_powerPs(self, configDict): # type:(dict) -> None """ 设置 修正系数(powerPs)[20B] 如0x0100表示1.00 funCode: 12 """ data = "" for i in xrange(1, 11): powerPs = self.check_params_range(params=configDict.get("powerPs{}".format(i)), minData=0, maxData=9.99, desc="修正系数") data += self.encode_str(powerPs, length=4, ratio=100, base=10) # TODO 文档中是十进制待验证! result = self.send_mqtt(funCode="12", data=data) def get_powerPs(self): # type:() -> dict """ 获取 修正系数(powerPs)[20B] 如0x0100表示1.00 funCode: 13 """ data = self.send_mqtt(funCode="13", data="00") data_list = self.decode_long_hex_to_list(data, split=4, ratio=0.01, base=10) result = {} for i in xrange(1, len(data_list) + 1): result["powerPs{}".format(i)] = data_list[i - 1] return result def reset_device(self): # type:() -> None """ 复位设备 funCode: 14 下发:CC09140009090909090001 返回:AA09140009090909090115 """ self.send_mqtt(funCode="14", data="00") def set_dev_fault(self, fault): # type:(dict) -> None """ 开启禁用设备 (01:开启设备 00:禁用设备) funCode: 15 """ if fault == True: data = "00" self.send_mqtt(funCode="15", data=data) self.disable_app_device(True) elif fault == False: data = "01" self.send_mqtt(funCode="15", data=data) self.disable_app_device(False) else: raise ServiceException({"result": 2, "description": u"设备操作故障!"}) def reset_factory_Mode(self): # type:() -> None """ 恢复工厂模式: funCode: 16 """ self.send_mqtt(funCode="16", data="00") def get_total_consumption(self): # type:() -> dict """ 获取消费总额 totalCoins[4B] + totalCard[4B] + totalRemote[4B] + totalAll[4B] + totalElec[4B] funCode: 17 """ data = self.send_mqtt(funCode="17", data="00") data_list = self.decode_long_hex_to_list(data, split=8, ratio=0.1, base=10) name_list = ["totalCoins", "totalCard", "totalRemote", "totalAll", "totalElec"] return dict(zip(name_list, data_list)) def get_total_refund(self): # type:() -> dict """ 获取退费总额度 totalRefundCoins[4B] + totalRefundCard[4B] + totalRefundRemote[4B] funCode: 18 """ data = self.send_mqtt(funCode="18", data="00") data_list = self.decode_long_hex_to_list(data, split=8, ratio=0.1, base=10) name_list = ["totalRefundCoins", "totalRefundCard", "totalRefundRemote"] return dict(zip(name_list, data_list)) def lock_unlock_port(self, ports, switch=True): # type:(str,str) -> None """ 远程禁用某个或多个端口 port[10B] 0x00:禁用 0x01:开启 funCode: 19 """ if isinstance(ports, list) or isinstance(ports, tuple): ports = map(lambda x: str(x), ports) elif isinstance(ports, dict): ports = map(lambda x: str(x), ports.keys()) else: ports = [str(ports)] # 防止下面1判断成10 if switch: switchStr1 = "00" switchStr2 = "01" else: switchStr1 = "01" switchStr2 = "01" data = "" for port in xrange(1, 11): if str(port) in ports: data += switchStr1 else: data += switchStr2 self.send_mqtt(funCode="19", data=data) def set_device_time(self, configDict): # type:(dict) -> None """ 设置设备系统时间 year + month + date + day + hour + minute + second funCode: 1A """ year = self.check_params_range(configDict.get("year"), minData=0, maxData=99, desc="年份") month = self.check_params_range(configDict.get("month"), minData=1, maxData=12, desc="月份") date = self.check_params_range(configDict.get("date"), minData=1, maxData=31, desc="日份") day = self.check_params_range(configDict.get("day"), minData=1, maxData=7, desc="星期") hour = self.check_params_range(configDict.get("hour"), minData=1, maxData=23, desc="时") minute = self.check_params_range(configDict.get("minute"), minData=1, maxData=59, desc="分") second = self.check_params_range(configDict.get("second"), minData=1, maxData=59, desc="秒") data = self.encode_str(year) data += self.encode_str(month) data += self.encode_str(date) data += self.encode_str(day) data += self.encode_str(hour) data += self.encode_str(minute) data += self.encode_str(second) self.send_mqtt(funCode="1A", data=data) def get_device_time(self): # type:() -> dict """ 获取设备系统时间 funCode: 1B """ data = self.send_mqtt(funCode="1B", data="00") data_list = self.decode_long_hex_to_list(data) name_list = ["year", "month", "date", "day", "hour", "minute", "second"] return dict(zip(name_list, data_list)) def get_charging_record(self, date): # type:(str) -> None """ 获取某天的充电纪录,连续返回,连续返回,连续返回!!! funCode: 1C example:201109 20年11月9日 """ year = date[:2] month = date[2:4] day = date[4:] data = self.encode_str(year) data += self.encode_str(month) data += self.encode_str(day) self.send_mqtt(funCode="1C", data=data) # TODO 收到指令后进行解析 def parse_reson(self, code): # type:(str) -> str """ code 传入解码的字符串 """ if not isinstance(code, str): code = str(code) infoDict = { "1": "购买的充电时间、电量用完了", "2": "用户手动停止(拔插头,或是按了停止按钮)", "3": "充电满了,自动停止", "4": "设备或是端口出现问题,被迫停止", "5": "因充电器功率超过充电站的单路最大输出功率,切断输出", "6": "超高温(达到75度)", "7": "烟雾报警", "8": "漏电报警", "9": "负载电流过大(短路)", "0A": "开始充电未接充电器", "0B": "端口远程停止", } return infoDict.get(code) def read_charging_record(self, data): # type:(str) -> dict """ 接上一条,连续发送后收到解析 """ year = self.decode_str(data[:2]) month = self.decode_str(data[2:4]) date = self.decode_str(data[4:6]) day = self.decode_str(data[6:8]) hour = self.decode_str(data[8:10]) minute = self.decode_str(data[10:12]) second = self.decode_str(data[12:14]) dateNum = self.decode_str(data[14:16]) port = self.decode_str(data[16:18]) consumeType = self.decode_str(data[18:20]) chargeStatus = self.decode_str(data[20:22]) reason = self.parse_reson(data[22:24]) payMoney = self.decode_str(data[24:28], ratio=0.1, base=10) leftMoney = self.decode_str(data[28:32], ratio=0.1, base=10) leftTime = self.decode_str(data[32:36], ratio=0.1, base=10) cardNo = "{:0>10}".format(self.decode_str(data[36:44])) randomCode = self.decode_str(data[44:48]) usedElec = self.decode_str(data[48:52], ratio=0.01, base=10) cardBalance = self.decode_str(data[52:56], ratio=0.1, base=10) TotalRecordSum = self.decode_str(data[56:60]) return locals() def recharge_ic_card(self, cardNo, coin): # type:(str,str)->None """ 下发充电指令 funcCode: 1E """ cardNo = self.encode_str(cardNo) cardType = "02" money = self.encode_str(coin, length=4, ratio=10, base=10) data = cardNo + cardType + money self.send_mqtt(funCode="1E", data=data, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE) def stop(self, port="1"): # type: (str) -> None self.remote_device(port, "stop") time.sleep(1) def response_card_status(self, cardNo, cardBalance, cardType, result): # type: (str,RMB,str,str) -> None """ 刷卡上报后回复设备,在上报事件里面报 funCode: 04 """ data = self.encode_str(cardNo) data += self.encode_str(str(cardBalance), length=4, ratio=10, base=10) data += self.encode_str(cardType) data += self.encode_str(result) self.send_mqtt(funCode="04", data=data) def get_dev_setting(self): # type: () -> dict result = {} data = self.get_device_ID() result.update(data) data = self.get_function_switch() result.update(data) data = self.get_consumption_rules() result.update(data) data = self.get_powerPs() result.update(data) data = self.get_icMoney_maxPower_floatingCharge() result.update(data) data = self.get_noloadTime_volume() result.update(data) time = self.get_device_time() result.update(time) totalRecords = self.get_total_consumption() result.update(totalRecords) refundProtection = self.device.get("otherConf", {}).get("refundProtection", 1) refundProtectionTime = self.device.get("otherConf", {}).get("refundProtectionTime", "5") consumeModule = result.get("consumeModule") result.update({"refundProtection": refundProtection, "refundProtectionTime": refundProtectionTime, "consumeModule": consumeModule}) return result def set_refundProtection_params(self, configDict): refundProtection = configDict.get("refundProtection", 1) # TODO给前端做兼容 refundProtection = int(refundProtection) refundProtectionTime = configDict.get("refundProtectionTime", "5") otherConf = self.device["otherConf"] otherConf.update({"refundProtection": refundProtection, "refundProtectionTime": refundProtectionTime}) Device.objects.filter(devNo=self.device["devNo"]).update(otherConf=otherConf) Device.invalid_device_cache(self.device.devNo) def set_device_function(self, request, lastSetConf): updateSet = set(request.POST.keys()) nameSet = {"coinSwitch", "cardSwitch", "cardRefund", "autoStop", "chargeFree"} if len(updateSet & nameSet): lastSetConf.update(request.POST) self.set_function_switch(lastSetConf) nameSet = {"refundProtection"} if len(updateSet & nameSet): lastSetConf.update(request.POST) self.set_refundProtection_params(lastSetConf) nameSet = {"reset"} if len(updateSet & nameSet): self.reset_factory_Mode() nameSet = {"reboot"} if len(updateSet & nameSet): self.reset_device() def set_device_function_param(self, request, lastSetConf): update = request.POST.copy() lastSetConf = lastSetConf.copy() if "logicalCode" in update: update.pop("logicalCode", None) if "logicalCode" in lastSetConf: lastSetConf.pop("logicalCode", None) update = dict(set(update.items()) - set(lastSetConf.items())) updateSet = set(update.keys()) if not updateSet: return nameSet = {"cardTime", "cardElec", "coinElec", "remoteElec", "coinTime", "remoteTime","time","serviceFee"} if len(updateSet & nameSet): lastSetConf.update(update) self.set_consumption_rules(lastSetConf) nameSet = {"powerPs1", "powerPs2", "powerPs3", "powerPs4", "powerPs5", "powerPs6", "powerPs7", "powerPs8", "powerPs9", "powerPs10", } if len(updateSet & nameSet): lastSetConf.update(update) self.set_powerPs(lastSetConf) nameSet = {"icMoney", "maxPower", "floatingPower", "floatingTime"} if len(updateSet & nameSet): lastSetConf.update(update) self.set_icMoney_maxPower_floatingCharge(lastSetConf) nameSet = {"noloadTime", "volume"} if len(updateSet & nameSet): lastSetConf.update(update) self.set_noloadTime_volume(lastSetConf) nameSet = {"refundProtectionTime"} if len(updateSet & nameSet): lastSetConf.update(update) self.set_refundProtection_params(lastSetConf) nameSet = {"consumeModule"} if len(updateSet & nameSet): lastSetConf.update(update) self.set_function_switch(lastSetConf) nameSet = {"hour", "day", "month", "second", "year", "date", "minute"} if len(updateSet & nameSet): lastSetConf.update(update) self.set_device_time(lastSetConf) def active_deactive_port(self, port, active): if active == False: self.stop(port) def updata_no_balance_list(self, devNo, card, mode="update"): # type: (str,Card,str) -> None key = "100255_no_balance_list_%s" % (devNo) if not serviceCache.get(key): no_balance_list = [] else: no_balance_list = serviceCache.get(key) if mode == "update": cardModel = { "cardId": card.id, "cardNo": card.cardNo, "balance": card.balance, } if card.cardNo: cardModel.update({"cardName": card.cardName}) if card.phone: cardModel.update({"phone": card.phone}) for item in no_balance_list: if card.cardNo == item["cardNo"]: no_balance_list.remove(item) if len(no_balance_list) > 49: no_balance_list = no_balance_list[:49] no_balance_list.insert(0, cardModel) elif mode == "remove": for item in no_balance_list: if card.cardNo == item["cardNo"]: no_balance_list.remove(item) serviceCache.set(key, no_balance_list) def get_no_balance_list(self, devNo): key = "100255_no_balance_list_%s" % (devNo) if not serviceCache.get(key): no_balance_list = [] else: no_balance_list = serviceCache.get(key) return no_balance_list def remote_charge_ic_card_by_dealer(self, cardIds, amount): """ 经销商远程充值接口 """ cards = Card.objects.filter(id__in=cardIds) for card in cards: openId = None cardId = card.id cardNo = card.cardNo money = 0 coins = VirtualCoin(amount) group = Group.get_group(self.device["groupId"]) rechangeOrder = None cardRechangeOrder = CardRechargeOrder.new_one(openId, cardId, cardNo, money, coins, group, rechangeOrder, rechargeType="offline") cardRechangeOrder.update(remarks="经销商线下充卡") self.recharge_ic_card(card.cardNo, amount) cardRechangeOrder.update(status="finished") self.updata_no_balance_list(self.device.devNo, card, mode="remove") def set_service_fee_info(self, payload): # 显示展示给用户信息部分 if payload.get('displaySwitchs'): displaySwitchs = payload.get('displaySwitchs') else: displaySwitchs = {'displayCoinsSwitch': False, 'displayTimeSwitch': True, 'displayPriceSwitch': True, 'setPulseAble': False, 'setBasePriceAble': False} # 套餐部分 packages = payload.get('packages') # 调整SN套餐顺序 for i, item in enumerate(packages): item['sn'] = i # 套餐id 去重 existIds = list(map(lambda _: _.get('id'), packages)) washConfig = {} for rule in packages: if 'price' in rule: if not is_number(rule['price'] and is_number(rule.get('time', 0))): raise InvalidParameter(u'金币数目或者时间或者价格必须是数字') if RMB(rule['price']) >= self.device.owner.maxPackagePrice: raise InvalidParameter(u'套餐金额超限') if len(rule['name']) > 20: raise InvalidParameter(u'套餐名字只能取1-20位') if 'id' in rule: ruleId = rule['id'] else: ruleId = list(set(range(1, 71)) - set([int(ruleId) for ruleId in washConfig.keys()]) - set(existIds))[0] washConfig[str(ruleId)] = { 'billingMethod': CONSUMETYPE.BILL_AS_SERVICE, 'name': rule['name'], 'coins': float(rule['price']), 'price': float(rule['price']), 'time': float(rule.get('time', 0)), 'description': rule.get('description', ''), 'imgList': rule.get('imgList', []), 'unit': rule.get('unit', u'分钟'), 'switch': rule.get('switch', True), 'sn': rule.get('sn') } self.device.update_device_obj(**{ 'washConfig': washConfig, 'otherConf.displaySwitchs': displaySwitchs, }) dealer = self.device.owner dealer.defaultWashConfig[self.device.devTypeId] = self.device['washConfig'].values() dealer.save() def get_service_fee_info(self): ruleList = [] billAsService = self.device.bill_as_service_feature billAsService['on'] = billAsService.on billAsService['elecCharge'] = billAsService.elec_charge billAsService['serviceCharge'] = billAsService.service_charge method = "电费+服务费计费" if billAsService.on else "电量计费" for packageId, rule in self.device['washConfig'].items(): item = { 'id': packageId, 'name': rule['name'], 'coins': rule['coins'], 'price': rule.get('price', rule['coins']), 'time': rule.get('time', 20), 'description': rule.get('description', ''), 'imgList': rule.get('imgList', []), 'unit': rule.get('unit', u'分钟'), 'switch': rule.get('switch', True) } if 'sn' in rule: item['sn'] = rule['sn'] ruleList.append(item) devData = { 'id': self.device.devNo, 'isManager': True, 'groupName': self.device.group['groupName'], 'groupNumber': self.device['groupNumber'], 'devNo': self.device.devNo, 'devTypeName': self.device.devTypeName, 'devTypeCode': self.device.devTypeCode } if "displaySwitchs" in self.device.my_obj.otherConf: displaySwitchs = self.device.my_obj.otherConf.get('displaySwitchs') else: displaySwitchs = { 'displayCoinsSwitch': True, 'displayTimeSwitch': True, 'displayPriceSwitch': True, "setPulseAble": False, "setBasePriceAble": False } ruleList = sorted(ruleList, key=lambda x: (x.get('sn'), x.get('id'))) return { 'ruleList': ruleList, 'billAsService': billAsService, 'devData': devData, 'displaySwitchs': displaySwitchs, 'method': method } def format_upload_power(self, power): return float(power / 10)