# -*- coding: utf-8 -*- # !/usr/bin/env python import time from apilib.utils_datetime import timestamp_to_dt from apps.web.constant import Const, DeviceCmdCode, MQTT_TIMEOUT from apps.web.core.adapter.base import SmartBox, fill_2_hexByte, hexbyte_2_bin from apps.web.core.exceptions import ServiceException from apps.web.core.networking import MessageSender from apps.web.device.models import Device class DataType(object): # 发送设备事件 GET_PORT_INFO = '02' GET_DEV_SETTING = '01' START_DEV = '51' STOP_DEV = '63' class DeKangBox(SmartBox): DATA = '{port}{dataType}{data}00' def __sendData(self, data, FunCode='A4',timeout = MQTT_TIMEOUT.NORMAL,orderNo=None): result = MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, payload = {'IMEI': self._device['devNo'], 'funCode': FunCode, '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): """ 状态: 00 : 未插 空闲 AA5524019B010000000008CA000000 00 000000000000000000000000000000000000000000B409BB 11 : 插上 检测负载 AA5524019B010000000008CA000000 11 0000000000000000000000000000000000000000002AD9BB 12 : 付款 继电器连接 AA5524019B01000000000000043B3B 12 1400000000000000000000000000000000000000003401BB 13 : 付款 继电器断开 AA5524019B010000000025D404330B 13 1400000000000000000000000000000000000000005529BB 52 : 启动 使用 AA5524019B01000000000000043B3A 52 14000000000000000000000000000000000000000065B1BB :param data: :return: """ portStr = str(int(data[: 2], 16)) power = str(int(data[2: 6], 16) / 10.0) ampere = str(int(data[6: 10], 16) / 1000.0) rightTime = str(int(data[14: 16], 16) * 3600 + int(data[16: 18], 16) * 60 + int(data[18: 20], 16)) portStatus = hexbyte_2_bin(data[20: 22]) # 设备状态是一个字节 解析成bit进行判断 curConsume = str(int(data[22: 24], 16)) canPay = bool(int(portStatus[3: 4])) statusBin = portStatus[4:] leftTime = (int(rightTime) + 59 )/ 60 if statusBin == "0000": # 待机转改 status = Const.DEV_WORK_STATUS_IDLE elif statusBin == "0001": # 负载接入状态 status = Const.DEV_WORK_STATUS_CONNECTED elif statusBin == "0010": # 充电正常运行状态 status = Const.DEV_WORK_STATUS_WORKING elif statusBin == "0011": # 负载断开状态 status = Const.DEV_WORK_STATUS_WORKING elif statusBin == "0100": # 充电完成负载未断开 status = Const.DEV_WORK_STATUS_FINISHED else: status = Const.DEV_WORK_STATUS_FAULT return { portStr: { 'power': power, 'ampere': ampere, 'rightTime': rightTime, 'canPay': canPay, 'status': status, 'curConsume': curConsume, 'portStatus': statusBin, 'leftTime': leftTime, 'port': portStr } } def __get_one_port_info_from_dev(self, port): portStr = fill_2_hexByte(hex(int(port)), num=2) sendData = self.DATA.format(port=portStr, dataType=DataType.GET_PORT_INFO, data=fill_2_hexByte(hex(0), 16)) result = self.__sendData(sendData) data = result['data'][10: -6] tempStatus = self.__parse_port_data(data) data = tempStatus.popitem()[1] data.update({'portStr': portStr}) return data def analyze_event_data(self, data): """ 目前只有状态改变才会有上报事件 上报数据为状态改变的通道当前状态 """ tempStatus = self.__parse_port_data(data[10: -6]) analyzeData = {} for k, v in tempStatus.items(): v.update({'portStr': k}) analyzeData = v return analyzeData def check_dev_status(self, attachParas = None): 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'请选择合适的充电桩'}) # 校验端口状态 canPay, desc = self.is_port_can_use(port) if not canPay: raise ServiceException({'result': 2, 'description': 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 = fill_2_hexByte(hex(int(port)), 2) # 校验远程上分 onPoints = attachParas.get('onPoints') if onPoints: # 校验端口状态 canPay, desc = self.is_port_can_use(port) if not canPay: raise ServiceException({'result': 2, 'description': desc}) # 获取启动设备的金币 coins = float(package['coins']) price = float(package['price']) hexCoins = fill_2_hexByte(hex(int(coins * 10)), 4) finishedTime = int(time.time()) + 3600 * 12 orderNo = attachParas.get('orderNo') # 启动设备 sendData = self.DATA.format(port=portStr, dataType=DataType.START_DEV, data=hexCoins+hexCoins+fill_2_hexByte(hex(0), 8)) result = self.__sendData(sendData,timeout=MQTT_TIMEOUT.START_DEVICE,orderNo=orderNo) # 缓存服务信息 start_timestamp = int(time.time()) result['finishedTime'] = finishedTime portDict = { 'startTime': timestamp_to_dt(start_timestamp).strftime('%Y-%m-%d %H:%M:%S'), 'status': Const.DEV_WORK_STATUS_WORKING, 'finishedTime': result['finishedTime'], 'coins': coins, 'price': price, 'isStart': True, 'openId': openId, 'vCardId': self._vcard_id, 'orderNo': orderNo } if 'linkedRechargeRecordId' in attachParas and attachParas.get('isQuickPay', False): pay_info = [] item = { 'rechargeRcdId': str(attachParas['linkedRechargeRecordId']) } pay_info.append(item) portDict['payInfo'] = pay_info Device.update_dev_control_cache(self._device['devNo'], {str(port): portDict}) return result def stop(self, port=None): """ 停止事件中 返还的数据会有存在对于该端口的剩余时间和剩余的金额 对其进行退款 """ if port is None: return portStr = fill_2_hexByte(hex(int(port)), 2) sendData = self.DATA.format(port=portStr, dataType=DataType.STOP_DEV, data=fill_2_hexByte(hex(0), 16)) return self.__sendData(sendData) def get_port_status_from_dev(self): sendData = self.DATA.format(port='00', dataType=DataType.GET_PORT_INFO, data=fill_2_hexByte(hex(0), 16)) result = self.__sendData(sendData) data = result['data'][10: -6] portInfo = dict() while data: tempStatus = self.__parse_port_data(data) portInfo.update(tempStatus) data = data[64:] allPorts, usedPorts, usePorts = self.get_port_static_info(portInfo) Device.update_dev_control_cache(self._device['devNo'], {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts}) ctrInfo = Device.get_dev_control_cache(self._device['devNo']) for port, info in portInfo.items(): if ctrInfo.has_key(port): lineInfo = ctrInfo[port] if 'openId' in lineInfo and lineInfo.get('isStart') and info['status'] == Const.DEV_WORK_STATUS_IDLE: ctrInfo[port] = info else: ctrInfo[port].update({'status': info['status']}) ctrInfo[port].update({'portStatus': info['portStatus']}) else: ctrInfo[port] = info Device.update_dev_control_cache(self._device['devNo'], ctrInfo) return portInfo def get_port_status(self, force = False): """ 从缓存中获取状态,若是缓存中没有获取到设备状态,直接从设备获取后再从缓存获取 然后从设备的信息中提取出端口状态 最后再次更新设备端口缓存 并返还提取的端口状态 :param force: """ if force: return self.get_port_status_from_dev() curInfo = Device.get_dev_control_cache(self._device['devNo']) if not curInfo.has_key('allPorts'): self.get_port_status_from_dev() curInfo = Device.get_dev_control_cache(self._device['devNo']) allPorts = curInfo.get('allPorts') statusDict = dict() portStr = lambda x:str(x+1) for port in xrange(allPorts): tempDict = curInfo.get(portStr(port), {}) if tempDict.has_key('status'): tempStatus = {'status': tempDict.get('status')} elif tempDict.has_key('isStart'): if tempDict.get('isStart'): tempStatus = {'status': Const.DEV_WORK_STATUS_WORKING} else: tempStatus = {'status': Const.DEV_WORK_STATUS_IDLE} else: tempStatus = {'status': Const.DEV_WORK_STATUS_IDLE} statusDict.setdefault(portStr(port), tempStatus) allPorts, usedPorts, usePorts = self.get_port_static_info(statusDict) Device.update_dev_control_cache( self._device['devNo'], { 'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts } ) return statusDict def is_port_can_use(self, port, canAdd=False): portInfo = self.__get_one_port_info_from_dev(port) canPay = portInfo.get('canPay') desc = '' if canPay else u'请检查充电插座是否连接成功' return canPay, desc def get_port_info(self, line=None): portHex = fill_2_hexByte(hex(int(line)), 2) sendData = self.DATA.format(port=portHex, dataType=DataType.GET_PORT_INFO, data=fill_2_hexByte(hex(0), 16)) result = self.__sendData(sendData) tempStatus = self.__parse_port_data(result.get('data')[10: -6]).get(str(line)) leftTime = int(round(int(tempStatus.get('rightTime')) / 60.0, 0)) power = tempStatus.get('power') ampere = tempStatus.get('ampere') return {'port': str(line), 'leftTime': leftTime, 'power': power, 'ampere':ampere} def get_dev_setting(self): sendData = self.DATA.format(port='00', dataType=DataType.GET_DEV_SETTING, data=fill_2_hexByte(hex(0), 16)) result = self.__sendData(sendData) data = result['data'][10: -6] maxPayment = str(int(data[2: 4], 16)) # 单通道单次最大付款数(角) unitChargeTime = str(int(data[6: 8], 16) * 6) # 单位金额充电时间(1元充电时间) 分钟 倍率是6 也就是说解析出来的数据*6才是真正的时间 单位分钟 maxPower = str(int(data[32: 34], 16) * 10) # 设备最大功率(w) coinsNum = str(int(data[50: 54], 16)) # 投币数量 version = 'V'+str(round(int(data[54: 56], 16) * 0.1, 1)) # 软件版本 totalCardConsume = str(int(data[56: 64], 16) / 10.0) # 刷卡总数(获取数据角,显示元) result = { 'maxPayment': maxPayment, 'unitChargeTime': unitChargeTime, 'maxPower': maxPower, 'coinsNum': coinsNum, 'version': version, 'totalCardConsume': totalCardConsume } return result def active_deactive_port(self, port, active): if not active: self.stop(port) def isHaveStopEvent(self): return True