123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- import json
- import logging
- from apilib.monetary import RMB
- from apps.web.constant import DeviceCmdCode, MQTT_TIMEOUT
- from apps.web.core.adapter.base import SmartBox
- from apps.web.core.device_define.kehang import KeHangDeviceSettingsValidator, STATUS_MAP, BillingType
- from apps.web.core.exceptions import ServiceException
- from apps.web.core.networking import MessageSender
- from apps.web.device.models import Device
- from apps.web.user.models import ConsumeRecord
- logger = logging.getLogger(__name__)
- class KeHangBox(SmartBox):
- def _send_data(self, funCode, data = None, timeout = MQTT_TIMEOUT.NORMAL):
- if data is None:
- data = "00"
- result = MessageSender.send(
- device = self.device,
- cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
- payload = {
- "IMEI": self._device["devNo"],
- "funCode": funCode,
- "data": data
- },
- timeout = timeout
- )
- if "rst" in result and result.get("rst") != 0:
- if result.get("rst") == -1:
- raise ServiceException({"result": 2, "description": u"设备网络故障,请重新"})
- if result.get("rst") == 1:
- raise ServiceException({"result": 2, "description": u"充电桩无响应,请稍后再试试"})
- return result
- def _get_port_status(self):
- result = self._send_data("01")
- data = result["data"]
- portNum = int(data[18: 20], 16)
- portStatus = dict()
- for _i in range(portNum):
- _port = str(_i + 1)
- _offset = 20 + _i * 2
- portStatus[_port] = data[_offset: _offset+2]
- return portStatus
- def _get_port_charge_info(self, port):
- """ 获取端口的充电信息 """
- data = "{:02X}".format(int(port))
- result = self._send_data("06", data)
- return {
- "port": str(int(result["data"][18:20], 16)),
- "left": int(result["data"][20: 24], 16),
- "power": int(result["data"][24: 28], 16)
- }
-
- def _get_consume_total(self):
- """ 获取总的消费数据 """
- result = self._send_data("07")
- return {
- "cardMoney": int(result["data"][18: 22], 16),
- "coinMoney": int(result["data"][22: 26], 16),
- "totalUsedTime": int(result["data"][26: 30], 16),
- "totalUsedElec": int(result["data"][30: 34], 16)
- }
- def _set_card_coin_time(self, maxPower, cardCost, time1, time2, time3):
- """ 设置最大功率 刷卡消费 以及投币相应的时间 """
- data = "{:04X}{:02X}{:04X}{:04X}{:04X}".format(maxPower, cardCost, time1, time2, time3)
- result = self._send_data("08", data)
- return result["data"][18: 20] == "01"
- def _set_card_coin_enable(self, isCardEnable, isCoinEnable):
- """ 设置刷卡投币使能 """
- data = "{:02X}{:02X}".format(isCardEnable, isCoinEnable)
- self._send_data("09", data)
- return True
- def _stop(self, port):
- """ 停止端口 """
- data = "{:02X}".format(port)
- result = self._send_data("0B", data)
- return {
- "port": str(int(result["data"][18: 20], 16)),
- "left": int(result["data"][20: 24], 16)
- }
- def _get_card_coin_time(self):
- result = self._send_data("0C")
- return {
- "maxPower": int(result["data"][18: 22], 16),
- "cardCost": int(result["data"][22: 24], 16),
- "coin1Time": int(result["data"][24: 28], 16),
- "coin2Time": int(result["data"][28: 32], 16),
- "coin3Time": int(result["data"][32: 36], 16),
- "isCardRefund": int(result["data"][36: 38], 16),
- "isAutoStop": int(result["data"][38: 40], 16)
- }
- def _set_stop_refund(self, isAutoStop, isCardRefund):
- data = "{:02X}{:02X}".format(isAutoStop, isCardRefund)
- self._send_data("13", data)
- return True
- def _set_power_step(self, power1, power2, power3, power4, power5):
- """ 设置5挡计费 """
- data = "".join(("{:04X}{:02X}".format(_x["power"], _x["ratio"]) for _x in [power1, power2, power3, power4, power5]))
- result = self._send_data("14", data)
- return result["data"][18: 20] == "01"
- def _get_power_step(self):
- result = self._send_data("15")
- return [
- {"power": int(result["data"][18: 24][:4], 16), "ratio": int(result["data"][18: 24][4:6], 16)},
- {"power": int(result["data"][24: 30][:4], 16), "ratio": int(result["data"][24: 30][4:6], 16)},
- {"power": int(result["data"][30: 36][:4], 16), "ratio": int(result["data"][30: 36][4:6], 16)},
- {"power": int(result["data"][36: 42][:4], 16), "ratio": int(result["data"][36: 42][4:6], 16)},
- {"power": int(result["data"][42: 48][:4], 16), "ratio": int(result["data"][42: 48][4:6], 16)},
- ]
- def _set_free_voice(self, isFree, voice):
- """ 设置免费充电以及音量调节 """
- data = "{:02X}{:02X}".format(isFree, voice)
- self._send_data("27", data)
- return True
- def _set_float_and_noload(self, floatPower, floatTime, noloadPower, noloadTime, minConsume=0):
- data = "{:04X}{:04X}{:04X}{:04X}{:02X}".format(floatPower, floatTime, noloadPower, noloadTime, minConsume)
- self._send_data("28", data)
- return True
- def _set_card_time(self, time1, time2, time3):
- """ 设置刷卡的充电时间 """
- data = "{:04X}{:04X}{:04X}".format(time1, time2, time3)
- result = self._send_data("29", data)
- return result["data"][18: 20] == "01"
- def _get_card_time(self):
- result = self._send_data("2A")
- return {
- "card1Time": int(result["data"][18: 22], 16),
- "card2Time": int(result["data"][22: 26], 16),
- "card3Time": int(result["data"][26: 30], 16),
- "isFree": int(result["data"][30: 32], 16),
- "voice": int(result["data"][32: 34], 16),
- "floatPower": int(result["data"][34: 38], 16),
- "floatTime": int(result["data"][38: 42], 16),
- "noloadPower": int(result["data"][42: 46], 16),
- "noloadTime": int(result["data"][46: 50], 16),
- "minConsume": int(result["data"][50: 52], 16)
- }
- def _get_device_params(self):
- """ 获取设备的动态参数 主板暂时没有实现电压值"""
- result = self._send_data("35")
- return {
- "temperature": int(result["data"][18: 20], 16),
- "smokeWarning": int(result["data"][20: 22], 16),
- "voltage": int(result["data"][22: 24], 16)
- }
- def _get_consume_type(self):
- """ 读取设备所有的参数设置 协议有误 只是为了获取消费方式和消费流程"""
- result = self._send_data("36")
- return {
- "consumeType": int(result["data"][-6: -4], 16),
- "billingType": int(result["data"][-4: -2], 16),
- }
- def _set_consume_type(self, consumeType, billingType):
- """ 设置消费流程(先后按键)以及 计费方式(时间/电量) """
- data = "{:02X}{:02X}".format(consumeType, billingType)
- self._send_data("38", data)
- return True
- def _clean_warning(self):
- """ 主板暂时没有实现此功能 """
- self._send_data("39")
- def _reload_device(self):
- self._send_data("40")
- def _get_all_device_settings(self):
- devSettings = dict()
- # 首先获取消费统计 只读信息
- result = self._get_consume_total()
- devSettings["cardMoney"] = str(RMB(result["cardMoney"] / 10.0))
- devSettings["coinMoney"] = str(RMB(result["coinMoney"] / 1.0))
- devSettings["totalUsedTime"] = result["totalUsedTime"]
- devSettings["totalUsedElec"] = result["totalUsedElec"]
- # 获取 设备IC卡 投币 最大功率 刷卡以及充满自停使能
- result = self._get_card_coin_time()
- devSettings["maxPower"] = result["maxPower"]
- devSettings["cardCost"] = str(RMB(result["cardCost"] / 10.0))
- devSettings["coin1Time"] = result["coin1Time"]
- devSettings["coin2Time"] = result["coin2Time"]
- devSettings["coin3Time"] = result["coin3Time"]
- devSettings["isCardRefund"] = bool(result["isCardRefund"])
- devSettings["isAutoStop"] = bool(result["isAutoStop"])
- # 获取功率计费阶梯
- devSettings["powerStep"] = self._get_power_step()
- # 获取设备的刷卡充电时间 充电模式 语音 浮充 空载
- result = self._get_card_time()
- devSettings["card1Time"] = result["card1Time"]
- devSettings["card2Time"] = result["card2Time"]
- devSettings["card3Time"] = result["card3Time"]
- devSettings["isFree"] = bool(result["isFree"])
- devSettings["voice"] = result["voice"]
- devSettings["floatPower"] = result["floatPower"] / 10.0
- devSettings["floatTime"] = result["floatTime"]
- devSettings["noloadPower"] = result["noloadPower"] / 10.0
- devSettings["noloadTime"] = result["noloadTime"]
- devSettings["minConsume"] = str(RMB(result["minConsume"] / 100.0))
- # 获取设备的其他参数
- result = self._get_device_params()
- devSettings["temperature"] = result["temperature"]
- devSettings["voltage"] = result["voltage"]
- # 获取消费模式以及消费流程
- result = self._get_consume_type()
- devSettings["consumeType"] = result["consumeType"]
- devSettings["billingType"] = result["billingType"]
- return devSettings
- def _response_card(self, cardNo, status, balance, openId=""):
- """
- 回复卡余额
- """
- data = {
- "IMEI": self.device.devNo,
- "funCode": "23",
- "balance": int(balance),
- "status": status,
- "open_id": openId,
- "card": int(cardNo)
- }
- MessageSender.send(
- device = self.device,
- cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
- payload = data,
- timeout = 30
- )
- def _response_sync_card_balance(self, cardNo, balance, cardType="0001"):
- data = "{:08X}{:04X}{}".format(cardNo, balance, cardType)
- self._send_data("16", data)
- @staticmethod
- def _parse_11(data):
- return {
- "cardNo": str(int(data[18: 26], 16)),
- "cost": int(data[26: 28], 16) / 10.0,
- "balance": int(data[28: 32], 16) / 10.0,
- "port": str(int(data[40: 42], 16)),
- "opt": str(int(data[42: 44], 16))
- }
- @staticmethod
- def _parse_22(data):
- """EE10220000000000000131332B12AA330091"""
- cardNo = str(int(data[18: 26], 16))
- return {"cardNo": cardNo}
- @staticmethod
- def _parse_41(data):
- pass
- @staticmethod
- def _parse_12(data):
- return {
- "cardNo": str(int(data[18: 26], 16)),
- "balance": int(data[26: 30], 16) / 10.0,
- "type": data[30: 34]
- }
- @staticmethod
- def _parse_16(data):
- return {
- "cardNo": str(int(data[18: 26], 16)),
- "balance": int(data[26: 30], 16) / 10.0,
- "type": data[30: 34],
- "success": data[34: 36] == "01"
- }
- @staticmethod
- def _check_package(package):
- if not package:
- raise ServiceException({"result": 2, "description": u"套餐单位错误(1001)"})
- if package["unit"] == u"度":
- return int(float(package["time"]) * 100)
- if package["unit"] == u"分钟":
- return int(package["time"])
- if package["unit"] == u"小时":
- return int(float(package["time"]) * 60)
- raise ServiceException({"result": 2, "description": u"套餐单位错误(1005)"})
- def get_dev_setting(self):
- devSettings = self._get_all_device_settings()
- # 这两个参数没有读取 只有设置 只能从缓存中读取
- devSettings["isCardEnable"] = self.device["otherConf"].get("isCardEnable", True)
- devSettings["isCoinEnable"] = self.device["otherConf"].get("isCoinEnable", True)
- return devSettings
- def set_device_function_param(self, request, lastSetConf):
- validator = KeHangDeviceSettingsValidator(request.POST)
- if not validator.is_valid():
- raise ServiceException({"result": 2, "description": json.dumps(validator.str_errors)})
- data = validator.suit_data()
- if not self._set_card_coin_time(
- data["maxPower"], data["cardCost"], data["coin1Time"], data["coin2Time"], data["coin3Time"]
- ):
- raise ServiceException({"result": 2, "description": u"参数设置失败,请重试(0001)"})
- if not self._set_card_coin_enable(data["isCardEnable"], data["isCoinEnable"]):
- raise ServiceException({"result": 2, "description": u"参数设置失败,请重试(0002)"})
- if not self._set_stop_refund(data["isAutoStop"], data["isCardRefund"]):
- raise ServiceException({"result": 2, "description": u"参数设置失败,请重试(0003)"})
- if not self._set_power_step(*data["powerStep"]):
- raise ServiceException({"result": 2, "description": u"参数设置失败,请重试(0004)"})
- if not self._set_free_voice(data["isFree"], data["voice"]):
- raise ServiceException({"result": 2, "description": u"参数设置失败,请重试(0005)"})
- if not self._set_float_and_noload(
- data["floatPower"], data["floatTime"], data["noloadPower"], data["noloadTime"], data["minConsume"]
- ):
- raise ServiceException({"result": 2, "description": u"参数设置失败,请重试(0006)"})
- if not self._set_card_time(data["card3Time"], data["card2Time"], data["card3Time"]):
- raise ServiceException({"result": 2, "description": u"参数设置失败,请重试(0007)"})
- if not self._set_consume_type(data["consumeType"], data["billingType"]):
- raise ServiceException({"result": 2, "description": u"参数设置失败,请重试(0008)"})
- # 保存数据库一次
- otherConf = self.device["otherConf"]
- otherConf.update(validator.validated_data)
- Device.objects.filter(devNo=self.device.devNo).update(otherConf=otherConf)
- def set_device_function(self, request, lastSetConf):
- if "cleanSmoke" in request.POST:
- self._clean_warning()
- if "reboot" in request.POST:
- self._reload_device()
- def start_device_realiable(self, order): # type: (ConsumeRecord)->dict
- """ 启动设备 """
- chargeParam = self._check_package(order.package)
- port = int(order.attachParas["chargeIndex"])
- data = {
- "funCode": "02",
- "order_id": order.orderNo,
- "port": port,
- "open_id": order.openId,
- "charge_param": chargeParam,
- "charge_mode": 0 # 默认是充满自停模式(即会自动断电)
- }
- result = MessageSender.send(
- device = self.device,
- cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
- payload = data,
- timeout = 120
- )
- return result
- def _parse_51(self,data):
- return {}
- def analyze_event_data(self, data):
- """ 解析数据 """
- funCode = data[4: 6]
- if funCode == "22":
- result = self._parse_22(data)
- elif funCode == "41":
- result = self._parse_41(data)
- elif funCode == "12":
- result = self._parse_12(data)
- elif funCode == "16":
- result = self._parse_16(data)
- elif funCode == "51":
- result = self._parse_51(data)
- else:
- return
- result["cmdCode"] = funCode
- return result
- def stop(self, port = None):
- """ 停止设备运行 """
- self._stop(int(port))
- def get_port_status(self, force = False):
- """ 获取端口的状态 """
- devCache = Device.get_dev_control_cache(self.device.devNo)
- statusMap = dict()
- for _port, _item in devCache.items():
- if isinstance(_port, (unicode, str)) and _port.isdigit():
- statusMap[_port] = {'status': _item.get("status", 0)}
- if not statusMap or force:
- return self.get_port_status_from_dev()
- return statusMap
- def get_port_status_from_dev(self):
- """ 从设备端获取状态 """
- result = self._get_port_status()
- devCache = Device.get_dev_control_cache(self.device.devNo)
- portStatus = dict()
- for _p, _s in result.items():
- _status = STATUS_MAP.get(_s)
- devCache[_p] = devCache.get(_p) or dict()
- devCache[_p]["status"] = _status
- portStatus[_p] = {"status": _status}
- allPorts, usedPorts, usePorts = self.get_port_static_info(portStatus)
- devCache.update({"allPorts": allPorts, "usedPorts": usedPorts, "usdPorts": usePorts})
- Device.update_dev_control_cache(self.device.devNo, devCache)
- return portStatus
- def get_port_info(self, port):
- """ 获取设备端口的使用详情 """
- result = self._get_port_charge_info(port)
- left = result["left"]
- power = result["power"] / 10.0
- portCache = Device.get_port_control_cache(self.device.devNo, str(port))
- billingType = portCache.get("billingType")
- needKind = portCache.get("needKind")
- orderNo = portCache.get("orderNo")
- openId = portCache.get("openId")
- order = ConsumeRecord.objects.filter(orderNo=orderNo, openId=openId).first() # type: ConsumeRecord
- if order:
- portCache["nickname"] = order.user.nickname
- if needKind and billingType is not None:
- if billingType == BillingType.TIME:
- portCache[needKind] = portCache["needValue"]
- portCache["leftTime"] = left
- elif billingType == BillingType.ELEC:
- portCache[needKind] = portCache["needValue"] / 100.0
- portCache["leftElec"] = left / 100.0
- portCache["power"] = power
- return portCache
- def active_deactive_port(self, port, active):
- if not active:
- self._stop(int(port))
- def custom_push_url(self, order, user, **kw):
- from apps.web.utils import concat_user_center_entry_url
- from apps.web.utils import concat_front_end_url
- return concat_user_center_entry_url(agentId=user.productAgentId, redirect=concat_front_end_url(
- uri='/user/index.html#/user/deviceStatus'))
|