# -*- coding: utf-8 -*- # !/usr/bin/env python import logging import time from apilib.utils_datetime import timestamp_to_dt from apps.web.constant import MQTT_TIMEOUT, DeviceCmdCode, Const from apps.web.core.adapter.base import SmartBox, fill_2_hexByte from apps.web.core.exceptions import ServiceException from apps.web.core.networking import MessageSender from apps.web.device.models import Device logger = logging.getLogger(__name__) class FuncCode(object): # 发送设备事件 GET_PORT_INFO = "01" START_DEV = "02" class HangXinBox(SmartBox): AUTO_STOP_COINS_MOTO = 250 # 摩托充满自停的金币数量 AUTO_STOP_COINS_CAR = 1000 # 汽车充满自停的金币数量 MIN_RECHARGE_MONEY = 20 QUOTA_ELEC = 500 WARNING_ELEC = 30 SEND_DATA = "{funCode}{sendData}000000" PORT_MAP = { "1": "A1", "2": "A2", "3": "A3", "4": "A4", "5": "A5", "6": "A6", "7": "A7", "8": "A8", "9": "A9", "10": "A0", } PORT_STR_MAP = {v: k for k, v in PORT_MAP.items()} def __sendData(self, funCode, sendData = "0000", portStr = None, timeout = MQTT_TIMEOUT.NORMAL, orderNo = None): if portStr is None and funCode == FuncCode.GET_PORT_INFO: sendPort = "FA" # 全部的端口状态查询 else: if not HangXinBox.PORT_MAP.has_key(portStr): raise ServiceException({'result': 2, 'description': u'无效的端口号'}) sendPort = HangXinBox.PORT_MAP[portStr] data = self.SEND_DATA.format(funCode = funCode, sendData = sendData) result = MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, payload = {"IMEI": self._device["devNo"], "funCode": sendPort, "data": data}, timeout = timeout) if result.has_key("rst") and result["rst"] != 0: if result['rst'] == -1: raise ServiceException({'result': 2, 'description': u'充电桩正在玩命找网络,请稍候再试'}) elif result['rst'] == 1: raise ServiceException({'result': 2, 'description': u'充电桩主板连接故障'}) else: raise ServiceException({'result': 2, 'description': u'系统错误'}) return result @staticmethod def __parse_port_data(data): portStr = HangXinBox.PORT_STR_MAP.get(data[: 2]) statusBytes = data[2: 4] tempData = { "portStr": portStr, "statusBytes": statusBytes } elecNum = int(data[4: 8], 16) # 解析端口状态 若是存在剩余电量 添加入解析信息 if statusBytes == "04": status = Const.DEV_WORK_STATUS_FAULT elif statusBytes == "07": if elecNum: status = Const.DEV_WORK_STATUS_WORKING tempData.update({"elecNum": str(elecNum)}) else: status = Const.DEV_WORK_STATUS_IDLE else: status = None tempData.update({"status": status}) return tempData def __get_one_port_info(self, port): result = self.__sendData(FuncCode.GET_PORT_INFO, portStr = port) return self.__parse_port_data(result.get("data")) def analyze_event_data(self, data): portStr = HangXinBox.PORT_STR_MAP.get(data[: 2]) if not portStr: return elecNum = str(int(data[4: 8], 16)) chargeStatus = data[8: 10] if chargeStatus == "01": desc = u"电量已经充满" elif chargeStatus == "02": desc = u"充电结束" elif chargeStatus == "03": desc = u"充电结束" else: desc = "" logger.error("undefined chargeStatus is %s" % chargeStatus) return {"leftElec": elecNum, "portStr": portStr, "chargeStatus": chargeStatus, "desc": desc} def start_device(self, package, openId, attachParas): if not isinstance(attachParas, dict): raise ServiceException({"result": 2, "description": u"请选择合适的充电桩"}) port = attachParas.get("chargeIndex", None) if not port: raise ServiceException({'result': 2, 'description': u"请选择合适的充电桩"}) portStr = str(port) # 获取启动设备的金币 coins = int(package['coins']) hexCoins = fill_2_hexByte(hex(coins), 4) orderNo = attachParas.get('orderNo') # 启动设备 对于此协议如果此端口有负载则不会响应 mqtt超时 result = self.__sendData(FuncCode.START_DEV, sendData = hexCoins, portStr = portStr, timeout = MQTT_TIMEOUT.START_DEVICE, orderNo = orderNo) tempStatus = self.__parse_port_data(result.get("data")) if not tempStatus.get("elecNum"): raise ServiceException({'result': 2, 'description': u"设备回应端口故障,请试试其他端口"}) start_timestamp = int(time.time()) result["finishedTime"] = start_timestamp + 3600 * 24 portDict = { 'status': Const.DEV_WORK_STATUS_WORKING, 'finishedTime': result["finishedTime"], 'coins': float(coins), 'isStart': True, 'openId': openId, 'vCardId': self._vcard_id, "needElec": tempStatus.get("elecNum"), "startTime": timestamp_to_dt(start_timestamp).strftime('%Y-%m-%d %H:%M:%S') } Device.update_dev_control_cache(self._device["devNo"], {str(port): portDict}) return result def is_port_can_use(self, port, canAdd = False): port = str(int(port)) portInfo = self.__get_one_port_info(port) if portInfo: status = portInfo.get("status", None) if status == Const.DEV_WORK_STATUS_IDLE: canUse = True desc = u"" elif status == Const.DEV_WORK_STATUS_WORKING: canUse = False desc = u'该线路正在工作,暂时不能继续使用,请您使用其他线路,或者等待该线路工作完毕' elif status == Const.DEV_WORK_STATUS_FAULT: canUse = False desc = u'该线路故障,暂时不能使用,请您使用其他线路' elif status == Const.DEV_WORK_STATUS_FORBIDDEN: canUse = False desc = u'该线路已被禁用,暂时不能使用,请您使用其他线路' else: canUse = False desc = u'该线路未知状态,暂时不能使用' else: canUse = False desc = u"未知充电端口,无法使用" return canUse, desc def get_port_info(self, line = None): tempStatus = self.__get_one_port_info(line) elec = tempStatus.get("elecNum") portInfo = Device.get_dev_control_cache(self._device["devNo"]).get(str(line)) if not portInfo: logger.error("no port info") raise ServiceException({'result': 2, 'description': u'暂无端口信息,请稍后再试'}) coins = portInfo.get("coins") if not coins: logger.error("port info has no coins info") # 类型判断是汽车还是摩托车 两种车的类型不同 对应的充满自停的费用不同 if int(coins) == HangXinBox.AUTO_STOP_COINS_MOTO: data = {"consumeMoney": u"%s币" % str(HangXinBox.AUTO_STOP_COINS_MOTO - int(elec))} elif int(coins) == HangXinBox.AUTO_STOP_COINS_CAR: data = {"consumeMoney": u"%s币" % str(HangXinBox.AUTO_STOP_COINS_CAR - int(elec))} else: data = {"leftMoney": u"%s币" % elec} data.update({"leftElec": elec}) return data def set_device_function_param(self, request, lastSetConf): minRechargeMoney = request.POST.get("minRechargeMoney") quotaElec = request.POST.get("quotaElec") warningElec = request.POST.get("warningElec") if quotaElec < warningElec: raise ServiceException({"result": 2, "description": u"设置失败,请确认您的电量余额是否已经小于报警电量"}) if minRechargeMoney: lastSetConf.update({"minRechargeMoney": minRechargeMoney}) if quotaElec: lastSetConf.update({"quotaElec": quotaElec}) if warningElec: lastSetConf.update({"warningElec": warningElec}) Device.objects.filter(devNo = self._device["devNo"]).update(otherConf = lastSetConf) Device.invalid_device_cache(self._device["devNo"]) def get_dev_setting(self): dev = Device.objects.get(devNo = self._device["devNo"]) otherConf = dev.otherConf return { "minRechargeMoney": otherConf.get("minRechargeMoney", self.MIN_RECHARGE_MONEY), "quotaElec": otherConf.get("quotaElec", self.QUOTA_ELEC), "warningElec": otherConf.get("warningElec", self.WARNING_ELEC) } @property def show_pay_unit(self): return u"金币"