# -*- coding: utf-8 -*- # !/usr/bin/env python import datetime import logging import time from decimal import Decimal from typing import TYPE_CHECKING from apilib.utils import is_number from apilib.monetary import RMB from apilib.utils_string import split_str from apps.web.constant import Const, DeviceCmdCode, ErrorCode, DeviceErrorCodeDesc, MQTT_TIMEOUT, CONSUMETYPE from apps.web.core.adapter.base import SmartBox, fill_2_hexByte, make_six_bytes_session_id, make_dianchuan_order_no, \ asc_to_string, hexbyte_2_bin, reverse_hex from apps.web.core.exceptions import ServiceException, InvalidParameter from apps.web.core.networking import MessageSender from apps.web.device.models import Device, DeviceType from apps.web.south_intf.yuhuan_fire import YuhuanNorther from apilib.utils_datetime import to_datetime logger = logging.getLogger(__name__) if TYPE_CHECKING: from apps.web.device.models import GroupDict class ChargingRongheBox(SmartBox): def __init__(self, device): super(ChargingRongheBox, self).__init__(device) def _check_package(self, package): """ 获取设备启动的发送数据 根据设备的当前模式以及套餐获取 :param package: :return: """ unit = package.get("unit", u"分钟") _time = float(package.get("time", 0)) # 按时间计费 if unit == u"小时": _time = _time * 60 billingType = "time" elif unit == u"天": _time = _time * 24 * 60 billingType = "time" elif unit == u"秒": _time = _time / 60 billingType = "time" elif unit == u"分钟": _time = _time billingType = "time" elif unit == u'度': _time = _time * 1000 billingType = "elec" else: raise ServiceException({"result": 2, "description": u"套餐单位错误,请联系经销商"}) return _time, unit, billingType def _check_feedback_result(self, cmd, devInfo): if devInfo.has_key('rst') and devInfo['rst'] != 0: if devInfo['rst'] == -1: raise ServiceException({'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'}) elif devInfo['rst'] == 1: raise ServiceException({'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) if not devInfo.has_key('data'): return def translate_funcode(self, funCode): funCodeDict = { 'A2': u'远程控制', 'A9': u'对时请求应答', 'B0': u'平台读取充电桩所有端口状态', 'B2': u'平台读取充电桩某一端口状态', 'B7': u'远程启动', 'B9': u'远程停止', 'C3': u'设置本地参数配置表', 'C5': u'读取本地参数表', } return funCodeDict.get(funCode, '') def translate_event_cmdcode(self, cmdCode): cmdDict = { 'A0': u'向平台上传主板模块信息', 'A8': u'对时请求', 'B4': u'本地启动上报', 'B6': u'本地在线卡刷卡上报', 'BB': u'订单结束上报', 'C0': u'本地故障、传感器上报', 'C2': u'本地充电分档上报', 'C7': u'获取平台参数配置表', } return cmdDict.get(cmdCode, '') def is_port_can_use(self, port, canAdd=False): # 电川的在启动的时候去判断是否需要续充 return True, '' def check_dev_status(self, attachParas=None): """ 如果超过两个心跳周期没有报心跳,并且最后一次更新时间在2个小时内,需要从设备获取状态 否则以缓存状态为准。 :param attachParas: :return: """ if attachParas is None: raise ServiceException({'result': 0, 'description': u'请您选择合适的充电端口、电池类型信息'}) if not attachParas.has_key('chargeIndex'): raise ServiceException({'result': 0, 'description': u'请您选择合适的充电端口'}) if not self.device.need_fetch_online: raise ServiceException( {'result': 2, 'description': DeviceErrorCodeDesc.get(ErrorCode.DEVICE_CONN_CHECK_FAIL)}) self.get_port_status_from_dev() group = self.device.group # type: GroupDict if group.is_free: logger.debug('{} is free. no need to check continue pay.'.format(repr(self.device))) return # 处理是否能够续充 portDict = self.get_port_status() port = str(attachParas['chargeIndex']) if port in portDict: isCanAdd = self.device['devType'].get('payableWhileBusy', False) if portDict[port]['status'] == Const.DEV_WORK_STATUS_IDLE: return elif portDict[port]['status'] == Const.DEV_WORK_STATUS_FAULT: raise ServiceException({'result': 0, 'description': u'该端口故障,暂时不能使用'}) elif portDict[port]['status'] == Const.DEV_WORK_STATUS_WORKING: if isCanAdd: return else: raise ServiceException({'result': 0, 'description': u'该端口正在工作不能使用,请您使用其他端口'}) elif portDict[port]['status'] == Const.DEV_WORK_STATUS_FORBIDDEN: raise ServiceException({'result': 0, 'description': u'该端口已被禁止使用,请您使用其他端口'}) elif portDict[port]['status'] == Const.DEV_WORK_STATUS_CONNECTED: return else: raise ServiceException({'result': 0, 'description': u'端口状态未知,暂时不能使用'}) else: raise ServiceException({'result': 0, 'description': u'未知端口,暂时不能使用'}) def test(self, coins): hexPort = fill_2_hexByte(hex(int(1)), 2) orderNo = make_dianchuan_order_no(self._device['devNo']) data = hexPort + orderNo[-16::] + '00' + '02' + '0100' + '00000000' + '0000' devInfo = self.remote_start_charge(1, data) return devInfo def port_is_busy(self, 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 'price' not in port_dict: return False billingType = port_dict['billingType'] if billingType not in ['time', 'elec']: return False if 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 remote_start_charge(self, port, data): devInfo = MessageSender.send( device=self.device, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, payload={ 'IMEI': self._device['devNo'], "funCode": 'B7', 'data': data }, timeout=MQTT_TIMEOUT.START_DEVICE) if 'rst' not in devInfo: raise ServiceException({'result': 2, 'description': u'启动设备失败,您的支付金额已经退还,请重新扫码设备试试(1001)'}) if devInfo['rst'] != 0: if devInfo['rst'] == -1: raise ServiceException({'result': 2, 'description': u'充电桩正在玩命找网络,您的金币还在,重试不需要重新付款,建议您试试旁边其他设备,或者稍后再试哦'}) elif devInfo['rst'] == 1: self.check_serial_port_for_startcmd(port) else: raise ServiceException({'result': 2, 'description': u'启动设备失败,您的支付金额已经退回,请重新扫码设备({})'.format(devInfo['rst'])}) else: if 'data' not in devInfo: logger.warning('no data in success response. result = {}'.format(str(devInfo))) raise ServiceException({'result': 2, 'description': u'启动设备失败,您的支付金额已经退回,请重试'}) else: data = devInfo['data'][18::] result = data[0:2] if result == '00': # 成功 pass elif result == '01': newValue = {str(port): {'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 == '02': raise ServiceException({'result': 2, 'description': u'充电类型或者消费类型不一致'}) elif result == '03': raise ServiceException({'result': 2, 'description': u'无此端口'}) else: raise ServiceException({ 'result': 2, 'description': u'启动设备失败({}),您的支付金额已经退回,请重新扫码设备'.format(result) }) return devInfo def start_device(self, package, openId, attachParas): if attachParas is None: raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路、电池类型信息'}) if not attachParas.has_key('chargeIndex'): raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路'}) price = float(package['price']) port = int(attachParas['chargeIndex']) hexPort = fill_2_hexByte(hex(port), 2) _time, unit, billingType = self._check_package(package) hexBillingType = '00' if billingType == 'time' else '01' coins = float(package['coins']) hexTime = fill_2_hexByte(hex(int(_time)), 4, True) onPoints = attachParas.get('onPoints') if onPoints: # 远程上分 orderNo = make_dianchuan_order_no(self._device['devNo']) logger.info('dealer onPoints package<{}> order<{}>'.format(package, orderNo)) else: orderNo = str(attachParas.get('orderNo')) data = hexPort + orderNo[-16::] + hexBillingType + '02' + hexTime + '00000000' + '0000' devInfo = self.remote_start_charge(port, data) portDict = { 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'status': Const.DEV_WORK_STATUS_WORKING, 'coins': coins, 'price': price, 'billingType': billingType, 'openId': openId, 'vCardId': self._vcard_id, 'payInfo': list() } ctrInfo = Device.get_dev_control_cache(self._device.devNo) lastPortInfo = ctrInfo.get(str(port), {}) if self.port_is_busy(lastPortInfo) and \ lastPortInfo.get('billingType') == billingType and \ lastPortInfo.get('openId') == openId: is_continus = True portDict['coins'] = float(coins) + lastPortInfo['coins'] portDict['price'] = float(price) + lastPortInfo['price'] else: is_continus = False portDict['coins'] = float(coins) portDict['price'] = float(price) if attachParas.get('redpackId'): if is_continus: redpackInfo = lastPortInfo.get("redpackInfo", list()) if not redpackInfo: logger.warning("miss redpackInfo! {}-{}".format(self._device["devNo"], port)) else: redpackInfo = list() redpackInfo.append({'redpackId': str(attachParas['redpackId'])}) portDict['redpackInfo'] = redpackInfo else: if is_continus: portDict['redpackInfo'] = lastPortInfo.get("redpackInfo", list()) else: portDict['redpackInfo'] = list() if 'linkedRechargeRecordId' in attachParas and attachParas.get('isQuickPay', False): if is_continus: payInfo = lastPortInfo.get("payInfo", list()) if not payInfo: logger.warning("miss payInfo! {}-{}".format(self._device["devNo"], port)) else: payInfo = list() payInfo.append({'rechargeRcdId': str(attachParas['linkedRechargeRecordId'])}) portDict['payInfo'] = payInfo else: if is_continus: portDict['payInfo'] = lastPortInfo.get("payInfo", list()) else: portDict['payInfo'] = list() if billingType == 'time': if is_continus: portDict['needTime'] = _time + lastPortInfo['needTime'] else: portDict['needTime'] = _time finishedTime = int(time.time()) + int(portDict['needTime'] * 60) else: if is_continus: portDict['needElec'] = _time / 1000.0 + lastPortInfo['needElec'] else: portDict['needElec'] = _time / 1000.0 finishedTime = int(time.time()) + 60 * 60 * 12 portDict.update({'finishedTime': finishedTime}) if 'orderNo' in attachParas: portDict.update({'orderNo': attachParas['orderNo']}) Device.update_dev_control_cache( self._device['devNo'], { str(port): portDict }) devInfo['finishedTime'] = finishedTime return devInfo def analyze_event_data(self, data): cmdCode = data[4:6] if cmdCode == 'A0': # 启动上报主板信息 boardNo = data[18:34] hardVersion = asc_to_string(data[34:40]) softId = asc_to_string(data[40:56]) softVersion = int(data[56:60], 16) return {'cmdCode':cmdCode, 'boardNo':boardNo, 'hardVersion':hardVersion, 'softId':softId, 'softVersion':softVersion} elif cmdCode == 'A8': return {'cmdCode':cmdCode} elif cmdCode == 'B1': # 端口状态 portNum = int(data[26:28], 16) nextData = data[(28 + 2 * portNum)::] chargedPortNum = int(nextData[0:2]) result = {'cmdCode':cmdCode} portDict = {} for ii in range(chargedPortNum): portData = nextData[2 + 26 * ii:2 + 26 * ii + 26] port = int(portData[0:2], 16) power = int(reverse_hex(portData[22:26]), 16) / 10.0 portDict.update({str(port):power}) result.update({'portDict':portDict}) return result elif cmdCode == 'B4': # 本地开始充电上报平台 port = int(data[18:20], 16) status = int(data[20:22], 16) orderNo = self._device['devNo'] + data[22:38] startTime = to_datetime('20' + data[38:50], '%Y%m%d%H%M%S').strftime('%Y-%m-%d %H:%M:%S') leftMinute = int(reverse_hex(data[50:54]), 16) leftSecond = int(data[54:56], 16) leftTime = leftMinute * 60 + leftSecond chargeType = int(data[56:58], 16) durationMinute = int(reverse_hex(data[58:62]), 16) durationSecond = int(data[62:64], 16) duration = durationMinute * 60 + durationSecond consumeType = int(data[64:66], 16) leftElec = int(reverse_hex(data[66:70]), 16) / 1000.0 elec = int(reverse_hex(data[70:74]), 16) / 1000.0 gear = int(data[74:76], 16) chargedMoney = int(data[76:78], 16) cardNo = int(reverse_hex(data[78:86]), 16) return {'cmdCode': cmdCode, 'status': status, 'port': port, 'orderNo':orderNo, 'startTime':startTime, 'leftTime':leftTime, 'chargeType':chargeType, 'duration':duration, 'consumeType':consumeType, 'leftElec':leftElec, 'elec':elec, 'gear':gear, 'chargedMoney':chargedMoney, 'cardNo':cardNo} elif cmdCode == 'B6': # 本地刷在线卡上报平台 port = int(data[18:20], 16) cardNo = int(reverse_hex(data[20:28]), 16) money = int(data[28:30], 16) return {'port': port, 'cardNo': cardNo, 'money': money, 'cardNoStr':data[20:28]} elif cmdCode == 'BB': # 充电订单 port = int(data[20:22], 16) orderNo = self._device['devNo'] + data[22:38] startTime = to_datetime('20' + data[38:50], '%Y%m%d%H%M%S').strftime('%Y-%m-%d %H:%M:%S') endTime = data[50:62] leftMinute = int(reverse_hex(data[62:66]), 16) leftSecond = int(data[66:68], 16) leftTime = leftMinute * 60 + leftSecond chargeType = int(data[68:70], 16) durationMinute = int(reverse_hex(data[70:74]), 16) durationSecond = int(data[74:76], 16) duration = durationMinute * 60 + durationSecond consumeType = int(data[76:78], 16) leftElec = int(reverse_hex(data[78:82]), 16) / 1000.0 elec = int(reverse_hex(data[82:86]), 16) / 1000.0 cardNo = int(reverse_hex(data[86:94]), 16) chargedMoney = int(reverse_hex(data[94:98]), 16) / 100.0 reasonDict = {'0':u'时间用完', '1':u'移除充电器', '2':u'充满自停', '3':u'故障停止', '4':u'功率过大停止', '5':u'离线卡退费停止', '6':u'启动充电未连接,充电器停止', '7':u'远程停止', '10':u'电量用完'} reason = int(data[98:100], 16) return {'cmdCode':cmdCode, 'port': port, 'orderNo': orderNo, 'startTime': startTime, 'endTime':endTime, 'leftTime':leftTime, 'chargeType':chargeType, 'duration':duration, 'consumeType':consumeType, 'leftElec':leftElec, 'elec':elec, 'cardNo':cardNo, 'chargedMoney':chargedMoney, 'reasonCode':reason, 'reason':reasonDict.get(str(reason), ''), 'uartData':data} elif cmdCode == 'C0': # 本地故障报警 port = int(data[18:20], 16) alarmType = int(data[20:22], 16) temperature = int(data[22:24], 16) voltage = int(reverse_hex(data[24:28]), 16) / 10.0 infoDict = {'03':u'输出故障', '04':u'粘连故障', 'AA':u'温度高', 'BB':u'烟雾告警', 'CC':u'输入电压异常'} statusInfo = infoDict.get(data[20:22], '') return {'cmdCode': cmdCode, 'port': port, 'alarmType':alarmType, 'temperature':temperature, 'voltage':voltage, 'statusInfo':statusInfo, 'uart':data, 'FaultCode':alarmType} elif cmdCode == 'C2': # 充电桩充电过程中发送分档上报平台 port = int(data[18:20], 16) timeBeforeGeared = int(reverse_hex(data[20:24]), 16) gearBeforeGeared = int(data[24:26], 16) timeAfterGeared = int(reverse_hex(data[26:30]), 16) gearAfterGeared = int(data[30:32], 16) gear = int(reverse_hex(data[32:36]), 16) return {'cmdCode': cmdCode, 'port': port, 'timeBeforeGeared': timeBeforeGeared, 'gearBeforeGeared': gearBeforeGeared, 'timeAfterGeared': timeAfterGeared, 'gearAfterGeared':gearAfterGeared, 'gear':gear} elif cmdCode == 'C7': return {'cmdCode':cmdCode} return None def get_dev_consume_count(self): devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], "funCode": '07', 'data': '00'}) self._check_feedback_result('07', devInfo) data = devInfo['data'][18::] cardFee = int(data[2:6], 16) / 10.0 # 以角为单位 coinFee = int(data[6:10], 16) # 以元为单位 return {'cardFee': cardFee, 'coinFee': coinFee} def get_port_info(self, line): support_06 = self._device.get('otherConf', {}).get('support_06', True) if not support_06: return {'port': line} data = fill_2_hexByte(hex(int(line)), 2) devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], "funCode": 'B2', 'data': data}) self._check_feedback_result('B2', devInfo) data = devInfo['data'] port = int(data[18:20], 16) status = int(data[20:22], 16) orderNo = self._device['devNo'] + data[22:38] startTime = to_datetime('20' + data[38:50], '%Y%m%d%H%M%S').strftime('%Y-%m-%d %H:%M:%S') leftMinute = int(reverse_hex(data[50:54]), 16) leftSecond = int(data[54:56], 16) leftTime = leftMinute * 60 + leftSecond chargeType = int(data[56:58], 16) durationMinute = int(reverse_hex(data[58:62]), 16) durationSecond = int(data[62:64], 16) duration = durationMinute * 60 + durationSecond consumeType = int(data[64:66], 16) leftElec = int(reverse_hex(data[66:70]), 16) / 1000.0 elec = int(reverse_hex(data[70:74]), 16) / 1000.0 power = int(reverse_hex(data[74:78]), 16) / 10.0 gear = int(data[78:80], 16) chargedMoney = int(data[80:82], 16) cardNo = int(reverse_hex(data[82:90]), 16) statusDict = {'0':Const.DEV_WORK_STATUS_IDLE, '1':Const.DEV_WORK_STATUS_WORKING, '3':Const.DEV_WORK_STATUS_FAULT, '4':Const.DEV_WORK_STATUS_FAULT_RELAY_CONNECT, '255':Const.DEV_WORK_STATUS_FAULT} statusDesc = {'3':u'充电故障', '4':u'继电器粘连', '255':u'无此端口'} if status in [3, 4, 255]: # 故障情况下,直接返回 return {'port':port, 'status':statusDict.get(str(status), Const.DEV_WORK_STATUS_FAULT), 'reason':statusDesc.get(str(status), '')} elif status == 0: # 空闲也直接返回 return {'port':port, 'status':Const.DEV_WORK_STATUS_IDLE} result = {'port':port, 'status':Const.DEV_WORK_STATUS_WORKING, 'orderNo':orderNo, 'startTime':startTime, 'usedTime':round(duration / 60.0, 2), 'power':power, 'gear':gear} # 充电的时候,根据不同类型,返回不同的数据 if chargeType == 0: result.update({'leftTime':round(leftTime / 60.0, 2)}) # 界面显示的是分钟,这里需要转化单位 else: result.update({'leftElec':leftElec}) result.update({'elec':elec}) consumeTypeDict = {'0':'coin', '1':'card', '2':'mobile', '3':'card', '4':'free'} portCache = Device.get_dev_control_cache(self._device['devNo']).get(str(port)) if str(consumeType) != '2': result.update({'consumeType':consumeTypeDict.get(str(consumeType))}) else: if portCache['vCardId']: result.update({'consumeType':'mobile_vcard'}) else: result.update({'consumeType':'mobile'}) if consumeType in [0, 1]: # 投币和离线卡 result.update({'chargedMoney':chargedMoney}) if consumeType in [1, 3]: result.update({'cardNo':cardNo}) return result # 访问设备,获取设备端口信息 def get_port_status_from_dev(self): devInfo = MessageSender.send(device=self.device, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, payload={ 'IMEI': self._device['devNo'], 'funCode': 'B0', 'data': '00' }) self._check_feedback_result('B0', devInfo) data = devInfo['data'][18::] result = {} portNum = int(data[8:10], 16) portData = data[10::] ii = 0 while ii < portNum: statusTemp = portData[ii * 2:ii * 2 + 2] if statusTemp == '00': status = {'status': Const.DEV_WORK_STATUS_IDLE} elif statusTemp == '01': status = {'status': Const.DEV_WORK_STATUS_WORKING} elif statusTemp == '03': status = {'status': Const.DEV_WORK_STATUS_FAULT} elif statusTemp == '04': status = {'status': Const.DEV_WORK_STATUS_FAULT_RELAY_CONNECT} # 不再上述状态之列的 统一为故障状态 else: status = {'status': Const.DEV_WORK_STATUS_FAULT} ii += 1 result[str(ii)] = status allPorts, usedPorts, usePorts = self.get_port_static_info(result) # 这里存在多线程更新缓存的场景,可能性比较低,但是必须先取出来,然后逐个更新状态,然后再记录缓存 ctrInfo = Device.get_dev_control_cache(self._device['devNo']) for strPort, info in result.items(): if ctrInfo.has_key(strPort): if info['status'] == Const.DEV_WORK_STATUS_WORKING: ctrInfo[strPort].update({'status': info['status']}) else: ctrInfo[strPort] = info else: ctrInfo[strPort] = info ctrInfo.update({ 'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts }) Device.update_dev_control_cache(self.device.devNo, ctrInfo) return result def get_default_port_nums(self): default_num = 10 if self.device.devTypeCode == Const.DEVICE_TYPE_CODE_CHARGING_DIANCHUAN_HIGH: default_num = 2 return default_num def get_port_status(self, force=False): if force: return self.get_port_status_from_dev() 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 tempDict.has_key('status'): statusDict[str(ii + 1)] = {'status': tempDict.get('status')} elif tempDict.has_key('isStart'): 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 lock_unlock_port(self, port, lock=True): if lock: Device.update_dev_control_cache(self._device['devNo'], {str(port): {'status': Const.DEV_WORK_STATUS_FORBIDDEN}}) else: Device.update_dev_control_cache(self._device['devNo'], {str(port): {'status': Const.DEV_WORK_STATUS_IDLE}}) def active_deactive_port(self, port, active): if active: raise ServiceException({'result': 2, 'description': u'该设备不支持直接打开端口'}) self.stop_charging_port(port) devInfo = Device.get_dev_control_cache(self._device['devNo']) portCtrInfo = devInfo.get(str(port), {}) portCtrInfo.update({'isStart': False, 'status': Const.DEV_WORK_STATUS_IDLE, 'needTime': 0, 'leftTime': 0, 'endTime': datetime.datetime.now().strftime(Const.DATETIME_FMT)}) newValue = {str(port): portCtrInfo} Device.update_dev_control_cache(self._device['devNo'], newValue) def stop_charging_port(self, port): portStr = fill_2_hexByte(hex(int(port)), 2) devInfo = MessageSender.send(device=self.device, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, payload={'IMEI': self._device['devNo'], "funCode": 'B9', 'data': portStr}, timeout=MQTT_TIMEOUT.SHORT) self._check_feedback_result('B9', devInfo) return {"port": int(devInfo['data'][18:20], 16)} def stop(self, port=None): return self.stop_charging_port(port) @property def isHaveStopEvent(self): return True def get_dev_all_settings(self): devInfo = MessageSender.send(device=self.device, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, payload={ "IMEI": self._device["devNo"], "funCode": "C5", "data": "00", }, timeout=MQTT_TIMEOUT.SHORT) self._check_feedback_result('C5', devInfo) data = devInfo['data'][18::] resultDict = { "runMode": int(data[0:2], 16), "voice": int(data[2:4], 16), "coinTime": int(reverse_hex(data[4:8]), 16), "cardTime": int(reverse_hex(data[8:12]), 16), "icMoney": int(data[12:14], 16) / 10.0, "cardRefund": '1',#str(int(data[14:16], 16)), "power1": int(reverse_hex(data[16:20]), 16) / 10.0, "power2": int(reverse_hex(data[20:24]), 16) / 10.0, "power3": int(reverse_hex(data[24:28]), 16) / 10.0, "power4": int(reverse_hex(data[28:32]), 16) / 10.0, "power5": int(reverse_hex(data[32:36]), 16) / 10.0, "power1ratio": 100, "power2ratio": int(data[38:40], 16), "power3ratio": int(data[40:42], 16), "power4ratio": int(data[42:44], 16), "power5ratio": int(data[44:46], 16), "autoStop":str(int(data[46:48], 16)), "fuchongPower": int(reverse_hex(data[48:52]), 16) /10.0, "fuchongTime": int(int(reverse_hex(data[52:56]), 16) / 60.0), "noPowerTime": int(int(reverse_hex(data[56:60]), 16) / 60.0), "password": int(reverse_hex(data[60:64]), 16), "temperature": int(data[64:66], 16), } return resultDict def reply_C7(self): devObj = Device.objects.get(devNo=self._device['devNo']) setConf = devObj.otherConf data = '' data += fill_2_hexByte(hex(int(setConf.get('runMode'))), 2) data += fill_2_hexByte(hex(int(setConf.get('voice'))), 2) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('coinTime'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('cardTime'))), 4)) data += fill_2_hexByte(hex(int(setConf.get('icMoney'))), 2) data += fill_2_hexByte(hex(int(setConf.get('cardRefund'))), 2) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('power1'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('power2'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('power3'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('power4'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('power5'))), 4)) data += '64' data += fill_2_hexByte(hex(int(setConf.get('power2ratio'))), 2) data += fill_2_hexByte(hex(int(setConf.get('power3ratio'))), 2) data += fill_2_hexByte(hex(int(setConf.get('power4ratio'))), 2) data += fill_2_hexByte(hex(int(setConf.get('power5ratio'))), 2) data += fill_2_hexByte(hex(int(setConf.get('autoStop'))), 2) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('fuchongPower'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('fuchongTime'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('noPowerTime'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('password'))), 4)) data += fill_2_hexByte(hex(int(setConf.get('temperature'))), 2) data += '00000000000000' devInfo = MessageSender.send(device=self.device, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, payload={ "IMEI": self._device["devNo"], "funCode": "C3", "data": data, }, timeout=MQTT_TIMEOUT.SHORT) self._check_feedback_result('C3', devInfo) # 获取设备配置参数 def get_dev_setting(self): result = self.get_dev_all_settings() otherConf = self._device.get("otherConf", dict()) result.update({'cardFeeMode':otherConf.get('cardFeeMode', 'time')}) result.update({'cardFeeMode':otherConf.get('cardFeeValue', 180)}) devRcd = Device.get_collection().find({'devNo':self._device['devNo']})[0] result.update({'boardNo':devRcd.get('boardNo', '')}) result.update({'hardVersion':devRcd.get('hardVersion', '')}) result.update({'softId':devRcd.get('softId', '')}) result.update({'softVersion':devRcd.get('softVersion', '')}) return result # 获取设备配置参数 def set_dev_setting(self, setConf): # 先保存到数据库中 confDict = { 'runMode':int(setConf.get('runMode')), 'voice':int(setConf.get('voice')), 'coinTime':int(setConf.get('coinTime')), 'cardTime':int(setConf.get('cardTime')), 'icMoney':int(setConf.get('icMoney')), 'cardRefund':int(setConf.get('cardRefund')), 'power1':int(setConf.get('power1')), 'power2':int(setConf.get('power2')), 'power3':int(setConf.get('power3')), 'power4':int(setConf.get('power4')), 'power5':int(setConf.get('power5')), 'power1ratio':100, 'power2ratio':int(setConf.get('power2ratio')), 'power3ratio':int(setConf.get('power3ratio')), 'power4ratio':int(setConf.get('power4ratio')), 'power5ratio':int(setConf.get('power5ratio')), 'autoStop':int(setConf.get('autoStop')), 'fuchongPower':int(setConf.get('fuchongPower')), 'fuchongTime':int(setConf.get('fuchongTime')), 'noPowerTime':int(setConf.get('noPowerTime')), 'password':int(setConf.get('password')), 'temperature':int(setConf.get('temperature')), 'cardFeeMode':setConf.get('cardFeeMode', 'time'), 'cardFeeValue':setConf.get('cardFeeValue', 180) } devObj = Device.objects.get(devNo=self._device['devNo']) devObj.otherConf.update(confDict) devObj.save() data = '' data += fill_2_hexByte(hex(int(setConf.get('runMode'))), 2) data += fill_2_hexByte(hex(int(setConf.get('voice'))), 2) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('coinTime'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('cardTime'))), 4)) data += fill_2_hexByte(hex(int(setConf.get('icMoney'))), 2) data += fill_2_hexByte(hex(int(setConf.get('cardRefund'))), 2) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('power1'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('power2'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('power3'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('power4'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('power5'))), 4)) data += fill_2_hexByte(0x64, 2) data += fill_2_hexByte(hex(int(setConf.get('power2ratio'))), 2) data += fill_2_hexByte(hex(int(setConf.get('power3ratio'))), 2) data += fill_2_hexByte(hex(int(setConf.get('power4ratio'))), 2) data += fill_2_hexByte(hex(int(setConf.get('power5ratio'))), 2) data += fill_2_hexByte(hex(int(setConf.get('autoStop'))), 2) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('fuchongPower'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('fuchongTime'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('noPowerTime'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('password'))), 4)) data += fill_2_hexByte(hex(int(setConf.get('temperature'))), 2) data += '00000000000000' devInfo = MessageSender.send(device=self.device, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, payload={ "IMEI": self._device["devNo"], "funCode": "C3", "data": data, }, timeout=MQTT_TIMEOUT.SHORT) if devInfo.has_key('rst') and devInfo['rst'] != 0: if devInfo['rst'] == -1: raise ServiceException({'result': 2, 'description': u'充电桩离线,配置信息没有下发到充电桩,设备重新上电上网后,会将配置下发到设备。建议您设备在线的时候配置'}) elif devInfo['rst'] == 1: raise ServiceException({'result': 2, 'description': u'充电桩繁忙,配置信息没有下发到充电桩,设备重新上电上网后,会将配置下发到设备。建议您设备在线的时候配置'}) ################ api相关接口 def apiGetPortStatusFromDc(self, record): return self.get_port_status_from_dev() def apiGetDevSettingsFromDc(self, record): return self.get_dev_setting() def apisetDevSettingsFromDc(self, record): self.set_dev_setting(record) return {} def apiGetPortInfoFromDc(self, record): return self.get_port_info(record['port']) def apiStartDeviceForDc(self, record): coins = float(record['price']) port = hex(int(record['port'])) data = '' data += fill_2_hexByte(port, 2) orderNo = make_dianchuan_order_no(self._device['devNo']) data += orderNo[-16::] data += '00' # 按时间方式充电 data += '02' # 在线扫码方式 data += reverse_hex(fill_2_hexByte(hex(int(record['time'])), 4)) data += '00000000' data += '0000' devInfo = MessageSender.send( device=self.device, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, payload={ 'IMEI': self._device['devNo'], "funCode": 'B7', 'data': data }, timeout=MQTT_TIMEOUT.START_DEVICE) usePort = int(record['port']) if 'rst' not in devInfo: raise ServiceException( { 'result': 2, 'description': u'启动设备失败,您的支付金额已经退还,请重新扫码设备试试(1001)' }) if devInfo['rst'] != 0: if devInfo['rst'] == -1: raise ServiceException( { 'result': 2, 'description': u'充电桩正在玩命找网络,您的金币还在,重试不需要重新付款,建议您试试旁边其他设备,或者稍后再试哦' }) elif devInfo['rst'] == 1: raise ServiceException( {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) else: raise ServiceException( { 'result': 2, 'description': u'启动设备失败,您的支付金额已经退回,请重新扫码设备({})'.format(devInfo['rst']) }) portDict = { 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'status': Const.DEV_WORK_STATUS_WORKING, 'coins': coins, 'price': coins, 'isApi': True } Device.update_dev_control_cache( self._device['devNo'], { str(usePort): portDict }) return devInfo # 回复设备上报的设备基础信息 def reply_A0(self): nowTime = datetime.datetime.now() year = nowTime.year - 2000 strTime = str(year) + nowTime.strftime('%m%d%H%M%S') devInfo = MessageSender.send(device=self.device, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, # 注意这个用OPERATE_DEV_SYNC,故意让服务器这边多等一会,等超时1分钟 payload={'IMEI': self._device['devNo'], "funCode": 'A1', 'data': '00' + strTime}, timeout=MQTT_TIMEOUT.SHORT) self._check_feedback_result('A1', devInfo) def control_device(self, controlType, IDSelect='00', newId='0000000000000000'): devInfo = MessageSender.send(device=self.device, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, payload={'IMEI': self._device['devNo'], "funCode": 'A2', 'data': controlType + IDSelect + newId}, timeout=MQTT_TIMEOUT.SHORT) self._check_feedback_result('A2', devInfo) # 回复对时请求 def reply_A7(self): nowTime = datetime.datetime.now() year = nowTime.year - 2000 strTime = str(year) + nowTime.strftime('%m%d%H%M%S') MessageSender.send(device=self.device, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, payload={'IMEI': self._device['devNo'], "funCode": 'A9', 'data': strTime}, timeout=MQTT_TIMEOUT.SHORT) # 回复本地充电上报 def reply_B4(self, port): data = fill_2_hexByte(hex(int(port)), 2) MessageSender.send(device=self.device, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, payload={'IMEI': self._device['devNo'], "funCode": 'B5', 'data': data}, timeout=MQTT_TIMEOUT.SHORT) def reply_BB(self, port): data = fill_2_hexByte(hex(int(port)), 2) MessageSender.send(device=self.device, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, payload={'IMEI': self._device['devNo'], "funCode": 'BC', 'data': data}, timeout=MQTT_TIMEOUT.SHORT) def reply_C0(self, port): data = fill_2_hexByte(hex(int(port)), 2) MessageSender.send(device=self.device, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, payload={'IMEI': self._device['devNo'], "funCode": 'C1', 'data': '00' + data}, timeout=MQTT_TIMEOUT.SHORT) def set_device_function(self, request, lastSetConf): if request.POST.has_key('reboot'): reboot = request.POST.get("reboot", False) if reboot: self.control_device('01') if request.POST.has_key('remoteUpgrade'): remoteUpgrade = request.POST.get("remoteUpgrade", False) if remoteUpgrade: self.control_device('02') def set_device_function_param(self, request, lastSetConf): devObj = Device.objects.get(devNo=self._device['devNo']) setConf = devObj.otherConf if request.POST.has_key('password'): password = request.POST.get("password","8888") setConf.update({'password': password}) if request.POST.has_key('cardTime'): cardTime = request.POST.get("cardTime", '240') setConf.update({'cardTime': cardTime}) if request.POST.has_key('runMode'): runMode = request.POST.get("runMode", '1') setConf.update({'runMode': runMode}) if request.POST.has_key('icMoney'): icMoney = request.POST.get("icMoney", '1') setConf.update({'icMoney': icMoney}) if request.POST.has_key('voice'): voice = request.POST.get("voice", '4') setConf.update({'voice': voice}) if request.POST.has_key('coinTime'): coinTime = request.POST.get("coinTime", '240') setConf.update({'coinTime': coinTime}) if request.POST.has_key('temperature'): temperature = request.POST.get("temperature", '255') setConf.update({'temperature': temperature}) if request.POST.has_key('power1'): power1 = int(request.POST.get("power1", '3000')) setConf.update({'power1': power1*10}) if request.POST.has_key('power2'): power2 = int(request.POST.get("power2", '4000')) setConf.update({'power2': power2*10}) if request.POST.has_key('power3'): power3 = int(request.POST.get("power3", '5000')) setConf.update({'power3': power3*10}) if request.POST.has_key('power4'): power4 = int(request.POST.get("power4", '6000')) setConf.update({'power4': power4*10}) if request.POST.has_key('power5'): power5 = int(request.POST.get("power5", '7000')) setConf.update({'power5': power5*10}) if request.POST.has_key('power2ratio'): power2ratio = int(request.POST.get("power2ratio", '100')) setConf.update({'power2ratio': power2ratio}) if request.POST.has_key('power3ratio'): power3ratio = int(request.POST.get("power3ratio", '100')) setConf.update({'power3ratio': power3ratio}) if request.POST.has_key('power4ratio'): power4ratio = int(request.POST.get("power4ratio", '100')) setConf.update({'power4ratio': power4ratio}) if request.POST.has_key('power5ratio'): power5ratio = int(request.POST.get("power5ratio", '100')) setConf.update({'power5ratio': power5ratio}) if request.POST.has_key('fuchongPower'): fuchongPower = int(request.POST.get("fuchongPower", '30')) setConf.update({'fuchongPower': fuchongPower*10}) if request.POST.has_key('fuchongTime'): fuchongTime = int(request.POST.get("fuchongTime", '120')) setConf.update({'fuchongTime': fuchongTime*60}) if request.POST.has_key('noPowerTime'): noPowerTime = int(request.POST.get("noPowerTime", '1')) setConf.update({'noPowerTime': noPowerTime*60}) # 先保存到数据库中 confDict = { 'runMode':int(setConf.get('runMode')), 'voice':int(setConf.get('voice')), 'coinTime':int(setConf.get('coinTime')), 'cardTime':int(setConf.get('cardTime')), 'icMoney':int(setConf.get('icMoney')), 'cardRefund':int(setConf.get('cardRefund')), 'power1':int(setConf.get('power1')), 'power2':int(setConf.get('power2')), 'power3':int(setConf.get('power3')), 'power4':int(setConf.get('power4')), 'power5':int(setConf.get('power5')), 'power1ratio':100, 'power2ratio':int(setConf.get('power2ratio')), 'power3ratio':int(setConf.get('power3ratio')), 'power4ratio':int(setConf.get('power4ratio')), 'power5ratio':int(setConf.get('power5ratio')), 'autoStop':int(setConf.get('autoStop')), 'fuchongPower':int(setConf.get('fuchongPower')), 'fuchongTime':int(setConf.get('fuchongTime')), 'noPowerTime':int(setConf.get('noPowerTime')), 'password':int(setConf.get('password')), 'temperature':int(setConf.get('temperature')), 'cardFeeMode':setConf.get('cardFeeMode', 'time'), 'cardFeeValue':setConf.get('cardFeeValue', 180) } devObj.otherConf.update(confDict) devObj.save() data = '' data += fill_2_hexByte(hex(int(setConf.get('runMode'))), 2) data += fill_2_hexByte(hex(int(setConf.get('voice'))), 2) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('coinTime'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('cardTime'))), 4)) data += fill_2_hexByte(hex(int(setConf.get('icMoney'))*10), 2) data += fill_2_hexByte(hex(int(setConf.get('cardRefund'))), 2) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('power1'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('power2'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('power3'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('power4'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('power5'))), 4)) data += '64' data += fill_2_hexByte(hex(int(setConf.get('power2ratio'))), 2) data += fill_2_hexByte(hex(int(setConf.get('power3ratio'))), 2) data += fill_2_hexByte(hex(int(setConf.get('power4ratio'))), 2) data += fill_2_hexByte(hex(int(setConf.get('power5ratio'))), 2) data += fill_2_hexByte(hex(int(setConf.get('autoStop'))), 2) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('fuchongPower'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('fuchongTime'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('noPowerTime'))), 4)) data += reverse_hex(fill_2_hexByte(hex(int(setConf.get('password'))), 4)) data += fill_2_hexByte(hex(int(setConf.get('temperature'))), 2) data += '00000000000000' devInfo = MessageSender.send(device=self.device, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, payload={ "IMEI": self._device["devNo"], "funCode": "C3", "data": data, }, timeout=MQTT_TIMEOUT.SHORT) if devInfo.has_key('rst') and devInfo['rst'] != 0: if devInfo['rst'] == -1: raise ServiceException({'result': 2, 'description': u'充电桩离线,配置信息没有下发到充电桩,设备重新上电上网后,会将配置下发到设备。建议您设备在线的时候配置'}) elif devInfo['rst'] == 1: raise ServiceException({'result': 2, 'description': u'充电桩繁忙,配置信息没有下发到充电桩,设备重新上电上网后,会将配置下发到设备。建议您设备在线的时候配置'})