12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736 |
- # -*- 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)
|