# -*- coding: utf-8 -*- # !/usr/bin/env python import datetime import json import logging import re import threading from decimal import Decimal from typing import TYPE_CHECKING, Optional from apilib.utils_datetime import to_datetime from apps.web.constant import Const, DeviceCmdCode, MQTT_TIMEOUT from apps.web.core.adapter.base import SmartBox from apps.web.core.exceptions import ServiceException from apps.web.core.networking import MessageSender from apps.web.device.models import Device, Group from apps.web.user.models import ConsumeRecord, MyUser, Card logger = logging.getLogger(__name__) if TYPE_CHECKING: pass # from apps.web.device.models import Device, Group # from apps.web.user.models import ConsumeRecord, MyUser, Card class CmdHelper(object): def __init__(self): pass @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, base=16, to_num=True): # type:(str,Optional[float, int],int, bool) -> Optional[float, str] """ ratio:比率单位转换 """ if not isinstance(data, str): data = str(data) if to_num: return int(data, base) * ratio else: 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)))) @staticmethod def split_data_to_list(split_str, data): # type:(str,str) ->Optional[list, None] """ return: list """ part = '({})' all_ = sum(map(lambda _: int(_) * 2, split_str)) pattern = reduce(lambda a, b: a + b, map(lambda _: part.format('..' * int(_)), split_str)) result = re.match(pattern, data[:all_]) if result: return result.groups() @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 _port_info_01(self, encode=None, decode=None): """ 查询端口状态 :param encode: :param decode: :return: """ if decode: data = decode.get('data') port = self.decode_str(data[10:12]) status = self.decode_str(data[12:14]) left_value = self.decode_str(data[14:18]) power = round(self.decode_str(data[18:22], ratio=0.1), 2) return {'port': port, 'status': status, 'left_value': left_value, 'power': power, } else: port = encode['port'] cmd = '01' port = self.encode_str(port) return {'funCode': 52, 'data': cmd + port} def _start_port_02(self, encode=None, decode=None): """ 开启端口 :param encode: :param decode: :return: """ if decode: data = decode.get('data') port = self.decode_str(data[2:4], ) status = self.decode_str(data[4:6], ) power = self.decode_str(data[6:10], ratio=0.1, ) return {'port': port, 'status': status, 'power': power, } else: port, start_type, value = encode['port'], encode['start_type'], encode['value'] cmd = '02' port = self.encode_str(port) start_type = self.encode_str(start_type) value = self.encode_str(value, length=4) return {'funCode': '5202', 'data': cmd + port + start_type + value} def _port_stop_03(self, encode=None, decode=None): """ 停止端口 :param encode: :param decode: :return: """ if decode: data = decode.get('data') port = self.decode_str(data[10:12], ) status = self.decode_str(data[12:14], ) used_elec = self.decode_str(data[14:18], ratio=0.01) # 度 left_value = self.decode_str(data[18:22]) power = self.decode_str(data[22:26], ratio=0.1) # 瓦 return {'port': port, 'status': status, 'used_elec': used_elec, 'left_value': left_value, 'power': power} else: port = encode['port'] cmd = '03' port = self.encode_str(port) return {'funCode': 52, 'data': cmd + port} def _get_gear_param_04(self, decode=None): """ 读取分档 :param decode: :return: """ if decode: data = decode.get('data', '') all_data = self.split_data_to_list('2121212121', data[10:]) power1 = self.decode_str(all_data[0]) power1ratio = self.decode_str(all_data[1]) power2 = self.decode_str(all_data[2]) power2ratio = self.decode_str(all_data[3]) power3 = self.decode_str(all_data[4]) power3ratio = self.decode_str(all_data[5]) power4 = self.decode_str(all_data[6]) power4ratio = self.decode_str(all_data[7]) power5 = self.decode_str(all_data[8]) power5ratio = self.decode_str(all_data[9]) return { 'power1': power1, 'power1ratio': power1ratio, 'power2': power2, 'power2ratio': power2ratio, 'power3': power3, 'power3ratio': power3ratio, 'power4': power4, 'power4ratio': power4ratio, 'power5': power5, 'power5ratio': power5ratio, } else: cmd = '04' return {'funCode': 52, 'data': cmd} def _set_gear_param_05(self, encode=None, decode=None): """ 设置分档 :param encode: :param decode: :return: """ if decode: data = decode.get('data') status = self.decode_str(data[10:12], ) return {'status': status} else: kw = encode.get('gear_param') cmd = '05' data = '' data += self.encode_str(kw.get('power1'), length=4) data += self.encode_str(kw.get('power1ratio', )) data += self.encode_str(kw.get('power2'), length=4) data += self.encode_str(kw.get('power2ratio', )) data += self.encode_str(kw.get('power3'), length=4) data += self.encode_str(kw.get('power3ratio', )) data += self.encode_str(kw.get('power4'), length=4) data += self.encode_str(kw.get('power4ratio', )) data += self.encode_str(kw.get('power5'), length=4) data += self.encode_str(kw.get('power5ratio', )) return {'funCode': 52, 'data': cmd + data} def _get_dev_param_06(self, decode=None): """ 读取设备参数 :param decode: :return: """ if decode: data = decode.get('data', '') all_data = self.split_data_to_list('22222111111122111114111', data[10:]) port_max_power = self.decode_str(all_data[0]) dev_max_power = self.decode_str(all_data[1]) coin_quote = self.decode_str(all_data[2]) card_quote = self.decode_str(all_data[3]) free_time = self.decode_str(all_data[4]) remove_load_time = self.decode_str(all_data[5]) noload_wait_time = self.decode_str(all_data[6]) free_mode_en = self.decode_str(all_data[7]) refund_mode_en = self.decode_str(all_data[8]) full_stop_en = self.decode_str(all_data[9]) coin_en = self.decode_str(all_data[10]) card_en = self.decode_str(all_data[11]) float_power = self.decode_str(all_data[12]) float_time = self.decode_str(all_data[13]) onec_card_fee = self.decode_str(all_data[14]) volume = self.decode_str(all_data[15]) consumeModule = self.decode_str(all_data[16]) temp_max_limit = self.decode_str(all_data[17]) smork_max_limit = self.decode_str(all_data[18]) password = self.decode_str(all_data[19]) power_report_interval = self.decode_str(all_data[20]) smork_temp_report_interval = self.decode_str(all_data[21]) port_status_interval = self.decode_str(all_data[22]) return { 'port_max_power': port_max_power, 'dev_max_power': dev_max_power, 'coin_quote': coin_quote, 'card_quote': card_quote, 'free_time': free_time, 'remove_load_time': remove_load_time, 'noload_wait_time': noload_wait_time, 'free_mode_en': free_mode_en, 'refund_mode_en': refund_mode_en, 'full_stop_en': full_stop_en, 'coin_en': coin_en, 'card_en': card_en, 'float_power': float_power, 'float_time': float_time, 'onec_card_fee': onec_card_fee, 'volume': volume, 'consumeModule': consumeModule, 'temp_max_limit': temp_max_limit, 'smork_max_limit': smork_max_limit, 'password': password, 'power_report_interval': power_report_interval, 'smork_temp_report_interval': smork_temp_report_interval, 'port_status_interval': port_status_interval, } else: cmd = '06' return {'funCode': 52, 'data': cmd} def _set_dev_param_07(self, encode=None, decode=None): """ 设置设备参数 :param encode: :param decode: :return: """ if decode: data = decode.get('data', '') status = self.decode_str(data[10:12]) return {'status': status} else: kw = encode.get('dev_param') cmd = '07' data = '' data += self.encode_str(kw.get('port_max_power'), length=4) data += self.encode_str(kw.get('dev_max_power'), length=4) data += self.encode_str(kw.get('coin_quote'), length=4) data += self.encode_str(kw.get('card_quote'), length=4) data += self.encode_str(kw.get('free_time'), length=4) data += self.encode_str(kw.get('remove_load_time')) data += self.encode_str(kw.get('noload_wait_time')) data += self.encode_str(kw.get('free_mode_en')) data += self.encode_str(kw.get('refund_mode_en')) data += self.encode_str(kw.get('full_stop_en')) data += self.encode_str(kw.get('coin_en')) data += self.encode_str(kw.get('card_en')) data += self.encode_str(kw.get('float_power'), length=4) data += self.encode_str(kw.get('float_time'), length=4) data += self.encode_str(kw.get('onec_card_fee')) data += self.encode_str(kw.get('volume')) data += self.encode_str(kw.get('consumeModule')) data += self.encode_str(kw.get('temp_max_limit')) data += self.encode_str(kw.get('smork_max_limit')) data += self.encode_str(kw.get('password'), length=8) data += self.encode_str(kw.get('power_report_interval')) data += self.encode_str(kw.get('smork_temp_report_interval')) data += self.encode_str(kw.get('port_status_interval')) return {'funCode': 52, 'data': cmd + data} def _get_card_param_08(self, decode=None): """ 读取读卡参数 :param decode: :return: """ if decode: data = decode.get('data', '') cardPassword = self.decode_str(data[10:22]) sector = self.decode_str(data[22:24]) return {'cardPassword': cardPassword, 'sector': sector} else: cmd = '08' return {'funCode': 52, 'data': cmd} def _set_card_param_09(self, encode=None, decode=None): """ 设置读卡参数 :param encode: :param decode: :return: """ if decode: data = decode.get('data', '') status = self.decode_str(data[10:12]) return {'status': status} else: kw = encode.get('card_param') cmd = '09' data = '' data += self.encode_str(kw.get('cardPassword'), length=12) data += self.encode_str(kw.get('sector')) return {'funCode': 52, 'data': cmd + data} def _set_port_enable_0A(self, encode=None, decode=None): """ 设置端口禁用/启用 :param encode: :param decode: :return: """ if decode: data = decode.get('data', '') status = self.decode_str(data[10:12]) return {'status': status} else: port, enable = encode['port'], encode['enable'] cmd = '0A' data = '' data += self.encode_str(port) if enable == True: data += '01' else: data += '00' return {'funCode': 52, 'data': cmd + data} def _set_dev_power_status_0B(self, encode=None, decode=None): """ 设置设备电源状态 开机/待机 :param encode: :param decode: :return: """ if decode: data = decode.get('data', '') status = self.decode_str(data[10:12]) return {'status': status} else: power_on = encode['power_on'] cmd = '0B' data = '' if power_on == True: data += '01' else: data += '00' return {'funCode': 52, 'data': cmd + data} def _get_dev_consume_total_0C(self, decode=None): """ 获取设备投币/刷卡总量 :param decode: :return: """ if decode: data = decode.get('data', '') coin_total = round(self.decode_str(data[10:14])) card_total = round(self.decode_str(data[14:18], ratio=0.1), 1) return {'coin_total': coin_total, 'card_total': card_total} else: cmd = '0C' return {'funCode': 52, 'data': cmd} def _set_dev_fualt_recovery_0D(self, decode=None): """ 设备故障状态清除 未做 :param decode: :return: """ if decode: data = decode.get('data', '') status = self.decode_str(data[2:4]) return {'status': status} else: cmd = '0D' return {'funCode': 52, 'data': cmd} def _get_dev_version_0E(self, decode=None): """ 获取设备版本信息 :param decode: :return: """ if decode: data = decode.get('data', '') data = data[8:] hw_ver_len = int(data[2:4], 16) hw_ver = data[4: 4 + hw_ver_len * 2] soft_ver_len = int(data[4 + hw_ver_len * 2: 6 + hw_ver_len * 2], 16) soft_ver = data[6 + hw_ver_len * 2: 6 + soft_ver_len * 2] return {'hw_ver': hw_ver, 'soft_ver': soft_ver} else: cmd = '0E' return {'funCode': 52, 'data': cmd} def _get_dev_all_status_0F(self, decode=None): """ 获取设备全部状态 :param decode: :return: """ if decode: data = decode.get('data', '') powers = map(lambda _: self.decode_str(_, ratio=1.0), self.split_data_to_list('2' * 10, data[10:50])) left_values = map(lambda _: self.decode_str(_), self.split_data_to_list('2' * 10, data[50:90])) result = {} for i in xrange(len(powers)): result['{}'.format(i + 1)] = { 'port': i + 1, 'power': powers[i], 'left_value': left_values[i], 'status': Const.DEV_WORK_STATUS_IDLE if left_values[i] == 0 and powers[ i] == 0 else Const.DEV_WORK_STATUS_WORKING } return result else: cmd = '0F' return {'funCode': 52, 'data': cmd} class GaoBoRuiChargingBox(SmartBox, CmdHelper): def __init__(self, device): super(GaoBoRuiChargingBox, self).__init__(device) self.ctrInfo = Device.get_dev_control_cache(device.devNo) self.consumeMode = self.device['otherConf'].get('consumeMode') def check_dev_status(self, attachParas=None): if attachParas is None: raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路、电池类型信息'}) if not attachParas.has_key('chargeIndex'): raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路'}) port = attachParas['chargeIndex'] openId = attachParas['openId'] result = self.send_mqtt(data={'funCode': '88', 'data': port}) if 'uid' in result: if openId != result['uid']: raise ServiceException({'result': 2, 'description': u'当前端口繁忙'}) def get_dev_info(self): return self._get_dev_version_0E(decode=self.send_mqtt(self._get_dev_version_0E())) def test(self, coins): raise NotImplementedError(u'设备未实现 `test`') def start_device(self, package, openId, attachParas): raise NotImplementedError('cannot call `start_device` of base class SmartBox') def start(self, packageId, openId=None, attachParas=None): if not attachParas: attachParas = {} washConfig = self.device.get('washConfig', dict()) package = washConfig.get(packageId) if not package: raise ServiceException({'result': 2, 'description': u'未找到套餐,请联系经销商!'}) # new return self.start_device(package, openId, attachParas) def stop(self, port=None): encode = {'port': port} return self._port_stop_03(decode=self.send_mqtt(self._port_stop_03(encode=encode))) def calc_stop_back_coins(self, totalFee, remainderTime, totalTime): refundFee = round(totalFee * (float(remainderTime) / totalTime), 2) if refundFee > totalFee: refundFee = totalFee if refundFee <= 0.0: refundFee = 0.00 return refundFee def stop_charging_port(self, port): return self.send_mqtt({'port': int(port), 'funCode': 'stopPort'}) # 访问设备,获取设备信息 def getDevInfo(self): pass def analyze_event_data(self, data): cmdCode = data[2:4] if cmdCode == '53' or cmdCode == '4C': subCmd = data[8:10] result = {'cmdCode': cmdCode, 'subCmd': subCmd} if subCmd == '36': # 在线卡请求余额 result['session'] = data[6:8] result['card_no'] = data[10:18] result['fee'] = data[18:20] elif subCmd == '35': # 离线卡上报开始 result['session'] = data[6:8] result['port'] = int(data[10:12], 16) result['card_no'] = data[12:20] result['fee'] = round(int(data[20:22], 16) * 0.1) result['balance'] = round(int(data[22:26], 16) * 0.1) # 元 if data[26:28] == '01': result['billingType'] = 'time' result['needTime'] = int(data[28:32], 16) else: result['billingType'] = 'elec' result['needElec'] = round(int(data[28:32], 16) * 0.01, 2) result['power'] = round(int(data[32:36], 16) * 0.1, 2) elif subCmd == '3B': result['port'] = int(data[10:12], 16) result['card_no'] = data[12:20] result['fee'] = round(int(data[20:22], 16) * 0.1, 2) result['balance'] = round(int(data[22:26], 16) * 0.1, 2) elif subCmd == '32': upload = data[10:-2] points = re.findall('..' * 3, upload) powers = {} for item in points: port = self.decode_str(item[:2], to_num=False) power = int(self.decode_str(item[2:], ratio=0.1)) powers[port] = power result['powers'] = powers elif subCmd == '3E': result = {} decode = {'data': data[10:60]} gear_params = self._get_gear_param_04(decode=decode) result.update(gear_params) decode = {'data': data[60:]} dev_params = self._get_dev_param_06(decode=decode) result.update(dev_params) return result elif subCmd == '3F': FAULT_CODE = { '1': '键盘故障', '2': '显示器故障', '3': '刷卡板故障', '4': '子网故障', '5': '存储故障', '6': '过温故障', '7': '烟雾故障', '8': '漏电', '9': '短路', 'A': '过压', 'B': '欠压', 'C': '整机过流', 'D': '恶意操作', 'E': '继电器粘连', 'F': '其它故障', } errCode = data[12:14][-1] statusInfo = FAULT_CODE.get(errCode) if errCode == '6': # 过温(1 字节) temp = int(data[14:16], 16) statusInfo = statusInfo + '(过温,当前{}度)'.format(temp) result.update({'statusInfo': statusInfo, 'FaultCode': errCode, 'uart': data}) elif errCode == 'A': # 过压(2 字节) overVoltage = int(data[14:16], 16) statusInfo = statusInfo + '(过压,当前{}V)'.format(overVoltage) result.update({'statusInfo': statusInfo, 'FaultCode': errCode, 'uart': data}) elif errCode == 'B': # 欠压(1 字节) overVoltage = int(data[14:16], 16) statusInfo = statusInfo + '(欠压,当前{}V)'.format(overVoltage) result.update({'statusInfo': statusInfo, 'FaultCode': errCode, 'uart': data}) elif errCode == 'C': # 过流(2 字节) overCurrent = int(data[14:16], 16) statusInfo = statusInfo + '(过流,当前{}A)'.format(overCurrent) result.update({'statusInfo': statusInfo, 'cmdCode': cmdCode, 'FaultCode': errCode, 'uart': data}) else: # 通道号(1字节) port = int(data[14:16], 16) if 0 < port < 11: result.update({'statusInfo': statusInfo, 'cmdCode': cmdCode, 'FaultCode': errCode, 'uart': data}) else: result.update({'statusInfo': statusInfo, 'cmdCode': cmdCode, 'port': port, 'FaultCode': errCode, 'uart': data}) return result def active_deactive_port(self, port, active): if active == False: self.stop_charging_port(port) def lock_unlock_port(self, port, lock=True): encode = {'port': port, 'enable': not lock} result = self._set_port_enable_0A(decode=self.send_mqtt(self._set_port_enable_0A(encode=encode))) if result.get('status') == 1: pass else: raise ServiceException({'result': 2, 'description': '设备端口( {} ) {}失败'.format(port, '禁用' if lock else '启用')}) # 端口是否可用,如果canAdd为True,表示可以追加钱 def is_port_can_use(self, port, canAdd=False): port = str(port) try: portDict = self.get_port_status() except ServiceException, e: return False, e.result.get('description') except Exception, e: logger.error('error = %s' % e) return False, u'获取端口状态失败' if port in portDict: if portDict[port]['status'] == Const.DEV_WORK_STATUS_IDLE: return True, '' elif portDict[port]['status'] == Const.DEV_WORK_STATUS_FAULT: return False, u'该线路故障,暂时不能使用,请您使用其他线路' elif portDict[port]['status'] == Const.DEV_WORK_STATUS_WORKING: if canAdd: return True, '' return False, u'该线路正在工作,暂时不能继续使用,请您使用其他线路,或者等待该线路工作完毕' elif portDict[port]['status'] == Const.DEV_WORK_STATUS_FORBIDDEN: return False, u'该线路已被禁止使用,请您使用其他线路' elif portDict[port]['status'] == Const.DEV_WORK_STATUS_CONNECTED: return True, u'' else: return False, u'线路未知状态,暂时不能使用' return False, u'未知端口,无法使用' def get_port_status(self, force=False): if force: self._get_dev_all_status_0F(decode=self.send_mqtt(self._get_dev_all_status_0F())) result = {} for k,v in self.ctrInfo.items(): if k.isdigit(): result[k] = v return result def dealer_get_port_status(self): """ 远程上分的时候获取端口状态 :return: """ return self.get_port_status() def get_dev_setting(self): result = {} params = self._get_dev_param_06(decode=self.send_mqtt(self._get_dev_param_06())) result.update(params) gear_params = self._get_gear_param_04(decode=self.send_mqtt(self._get_gear_param_04())) result.update(gear_params) card_params = self._get_card_param_08(decode=self.send_mqtt(self._get_card_param_08())) result.update(card_params) consumeTotal = self._get_dev_consume_total_0C(decode=self.send_mqtt(self._get_dev_consume_total_0C())) result.update(consumeTotal) devVersion = self._get_dev_version_0E(decode=self.send_mqtt(self._get_dev_version_0E())) result.update(devVersion) disableDevice = self.device['otherConf'].get('disableDevice', False) result.update({'disableDevice': disableDevice}) refundProtectionTime = self.device.get('otherConf', {}).get('refundProtectionTime', 5) result.update({'refundProtectionTime': refundProtectionTime}) return result def set_device_function(self, request, lastSetConf): if 'disable' in request.POST: result = self._set_dev_param_07(decode=self.send_mqtt(self._set_dev_param_07())) if result.get('status') == 1: pass else: raise ServiceException({'result': 2, 'description': '设备设置失败'}) def set_device_function_param(self, request, lastSetConf): def check_update(name_list): updata_dict = {} update = False for _ in name_list: if request.POST.get(_) != lastSetConf.get(_): update = True updata_dict[_] = request.POST.get(_) else: updata_dict[_] = lastSetConf.get(_) return update, updata_dict gear_param_name = ['power1', 'power1ratio', 'power2', 'power2ratio', 'power3', 'power3ratio', 'power4', 'power4ratio', 'power5', 'power5ratio', ] update, gear_param = check_update(gear_param_name) if update: encode = {'gear_param': gear_param} result = self._set_gear_param_05(decode=self.send_mqtt(self._set_gear_param_05(encode=encode))) if result.get('status') == 1: pass else: raise ServiceException({'result': 2, 'description': '设备分档参数设置失败'}) card_param_name = ['cardPassword', 'sector', ] update, gear_param = check_update(card_param_name) if update: encode = {'card_param': gear_param} result = self._set_card_param_09(decode=self.send_mqtt(self._set_card_param_09(encode=encode))) if result.get('status') == 1: pass else: raise ServiceException({'result': 2, 'description': '卡参数设置失败'}) dev_param_name = ['port_max_power', 'dev_max_power', 'coin_quote', 'card_quote', 'free_time', 'remove_load_time', 'noload_wait_time', 'free_mode_en', 'refund_mode_en', 'full_stop_en', 'coin_en', 'card_en', 'float_power', 'float_time', 'onec_card_fee', 'volume', 'consumeModule', 'temp_max_limit', 'smork_max_limit', 'password', 'power_report_interval', 'smork_temp_report_interval', 'port_status_interval', ] update, dev_param = check_update(dev_param_name) if update: encode = {'dev_param': dev_param} result = self._set_dev_param_07(decode=self.send_mqtt(self._set_dev_param_07(encode=encode))) if result.get('status') == 1: pass else: raise ServiceException({'result': 2, 'description': '设备设备参数设置失败'}) if 'disableDevice' in request.POST: disableDevice = self.device['otherConf'].get('disableDevice', False) if disableDevice == request.POST['disableDevice']: pass else: disableDevice = request.POST['disableDevice'] self.set_dev_disable(disableDevice) if 'refundProtectionTime' in request.POST: refundProtectionTime = self.device['otherConf'].get('refundProtectionTime', False) if refundProtectionTime == request.POST['refundProtectionTime']: pass else: refundProtectionTime = request.POST['refundProtectionTime'] Device.get_collection().update_one({'devNo': self._device['devNo']}, { '$set': {'otherConf.refundProtectionTime': refundProtectionTime}}) Device.invalid_device_cache(self.device.devNo) def set_dev_disable(self, disable): """ 设备端锁定解锁设备 :param disable: :return: """ if disable: encode = {'power_on': False} else: encode = {'power_on': True} result = self._set_dev_power_status_0B(decode=self.send_mqtt(self._set_dev_power_status_0B(encode=encode))) if result.get('status') == 1: Device.get_collection().update_one({'devNo': self._device['devNo']}, { '$set': {'otherConf.disableDevice': disable}}) Device.invalid_device_cache(self.device.devNo) else: raise ServiceException({'result': 2, 'description': '设备待机/开机设置失败'}) def get_port_static_info(self, portDict): allPorts, usedPorts = 0, 0 for k, v in portDict.items(): if k.isdigit(): allPorts += 1 if ('isStart' in v and v['isStart']) or ('status' in v and v['status'] != Const.DEV_WORK_STATUS_IDLE): usedPorts += 1 return allPorts, usedPorts, allPorts - usedPorts def get_port_status_from_dev(self): data = self._get_dev_all_status_0F() result = self.send_mqtt(data) result = self._get_dev_all_status_0F(decode=result) for strPort, info in result.items(): if strPort in self.ctrInfo: self.ctrInfo[strPort].update({'status': info['status']}) else: self.ctrInfo[strPort] = info allPorts, usedPorts, usePorts = self.get_port_static_info(self.ctrInfo) self.ctrInfo.update({'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts}) Device.update_dev_control_cache(self._device['devNo'], self.ctrInfo) return result def get_port_info(self, port): encode = {'port': port} result = self._port_info_01(decode=self.send_mqtt(self._port_info_01(encode=encode))) return result def async_update_portinfo_from_dev(self): class Sender(threading.Thread): def __init__(self, smartBox): super(Sender, self).__init__() self._smartBox = smartBox def run(self): try: self._smartBox.get_port_status_from_dev() except Exception as e: logger.info('get port stats from dev,e=%s' % e) sender = Sender(self) sender.start() def get_port_using_detail(self, port, ctrInfo, isLazy=False): """ 获取设备端口的详细信息 :param port: :param ctrInfo: :return: """ detailDict = ctrInfo.get(str(port), {}) try: portInfo = self.get_port_info(str(port)) # 有的主机报的信息leftTime错误 if portInfo.has_key('leftTime') and portInfo['leftTime'] < 0: portInfo.pop('leftTime') detailDict.update(portInfo) except Exception, e: logger.exception('get port info from dev=%s err=%s' % (self.device.devNo, e)) return detailDict portData = {} startTimeStr = detailDict.get('startTime', None) if startTimeStr is not None and 'usedTime' not in detailDict: startTime = to_datetime(startTimeStr) usedTime = int(round((datetime.datetime.now() - startTime).total_seconds() / 60.0)) portData['usedTime'] = usedTime elif detailDict.get('usedTime'): usedTime = detailDict.get('usedTime') portData['usedTime'] = usedTime else: usedTime = None if 'cType' in detailDict: if detailDict['cType'] == 1: detailDict['leftTime'] = detailDict.get('left_value') else: detailDict['leftElec'] = round(detailDict.get('left_value') * 0.01, 2) if detailDict.has_key('leftTime') and (usedTime > 0): if detailDict['leftTime'] == 65535: portData['leftTime'] = 65535 detailDict['usedTime'] = 0 detailDict['actualNeedTime'] = 0 else: portData['actualNeedTime'] = int(detailDict['leftTime']) + int(usedTime) if detailDict.has_key('needTime') and portData['actualNeedTime'] > detailDict['needTime']: portData['actualNeedTime'] = detailDict['needTime'] portData['leftTime'] = detailDict['leftTime'] if detailDict.has_key('coins'): if self.device.is_auto_refund: portData['leftMoney'] = round( float(detailDict['coins']) * int(detailDict['leftTime']) / ( int(detailDict['leftTime']) + int(usedTime)), 2) portData['consumeMoney'] = round( float(detailDict['coins']) * int(portData['usedTime']) / ( int(detailDict['leftTime']) + usedTime), 2) elif detailDict.has_key('leftTime'): portData['leftTime'] = detailDict['leftTime'] if (not detailDict.has_key('leftTime')) and (usedTime is not None): if detailDict.has_key('needTime'): portData['leftTime'] = detailDict['needTime'] - usedTime if detailDict.has_key('coins') and float(detailDict['coins']) != 0: # 只有支持退费的设备才显示可退费数据 if self.device.is_auto_refund: portData['leftMoney'] = round( float(detailDict['coins']) * portData['leftTime'] / detailDict['needTime'], 2) portData['consumeMoney'] = round( float(detailDict['coins']) * portData['usedTime'] / detailDict['needTime'], 2) if detailDict.has_key('openId'): user = MyUser.objects(openId=detailDict['openId']).first() if user: portData['nickName'] = user.nickname if detailDict.has_key('cardId'): if not detailDict.has_key('consumeType'): portData['consumeType'] = 'card' card = Card.objects.get(id=detailDict['cardId']) if card.cardName: portData['cardName'] = card.cardName portData['cardNo'] = card.cardNo # 注意,如果是IC卡,不支持余额回收,这里也不要显示出来 if card.cardType == 'IC' and portData.has_key('leftMoney'): portData.pop('leftMoney') elif detailDict.has_key('openId') and (not detailDict.has_key('consumeType')): if detailDict.get('vCardId'): portData['consumeType'] = 'mobile_vcard' else: portData['consumeType'] = 'mobile' elif detailDict.has_key('consumeType') and detailDict['consumeType'] == 'coin': portData['consumeType'] = 'coin' # 硬币的都无法退费 if portData.has_key('leftMoney'): portData.pop('leftMoney') # 做个特殊处理 if portData.has_key('needTime'): if portData['needTime'] == '999' or portData['needTime'] == '充满自停': portData['needTime'] = u'充满自停' else: portData['needTime'] = u'%s分钟' % portData['needTime'] # 如果剩余时间为65535,表示未接插头 if portData.has_key('leftTime') and portData['leftTime'] == 65535: portData['leftTime'] = u'(线路空载)' portData['usedTime'] = 0 portData['needTime'] = 0 detailDict.update(portData) for k, v in detailDict.items(): if v < 0: detailDict.pop(k) # 因为前台显示的开始时间如果带年,就显示不下,这里做个切割 if detailDict.has_key('startTime') and detailDict['startTime'].count('-') == 2: detailDict['startTime'] = to_datetime(detailDict['startTime']).strftime('%m-%d %H:%M:%S') return detailDict @property def isHaveStopEvent(self): return True def get_many_port_info(self, portList): return None def start_device_realiable(self, order): # type:(ConsumeRecord)->dict if order.orderNo[:10] == self.device.ownerId[-10:]: # 此时为远程上分 先停一次 self.stop_charging_port(order.used_port) attachParas = order.attachParas package = order.package unit = package.get('unit') if unit == '分钟': cType = 1 cValue = int(package.get('time')) elif unit == '度': cType = 2 cValue = int(package.get('time') * 100) else: raise ServiceException({'result': 2, 'description': '设备单位配置错误'}) port = int(attachParas['chargeIndex']) data = { 'funCode': 'startPort', 'port': port, 'cType': cType, # 1 按时间 2 按电量 'cValue': cValue, 'order_id': order.orderNo, 'uid': order.openId } # 订单中记录一份下发收到源数据 uart_source = [] uart_source.append( {'write_start': json.dumps(data, indent=4), 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}) result = MessageSender.send(device=self.device, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, payload=data, timeout=120) if result['rst'] == 9: raise ServiceException({'result': 2, 'description': '启动失败,当前端口已被其他人使用'}) uart_source.append( {'rece_start': json.dumps(result, indent=4), 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}) order.update(uart_source=uart_source) return result def calc_elec_fee(self, spend_elec): group = Group.objects.get(id=self.device['groupId']) return float(group.otherConf.get('elecFee', 0)) * spend_elec @property def support_monthly_package(self): return False def stop_by_order(self, port, orderNo): return self.stop_charging_port(port) def isHaveFaultHandle(self): return True def faultHandle(self, **kw): self._set_dev_fualt_recovery_0D(decode=self.send_mqtt(self._set_dev_fualt_recovery_0D(), timeout=5)) def send_mqtt(self, data=None, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, timeout=MQTT_TIMEOUT.NORMAL): """ 发送mqtt 指令默认210 返回data """ if 'cmd' not in data: data.update({'cmd': cmd}) if 'IMEI' not in data: data.update({'IMEI': self.device.devNo}) result = MessageSender.send(self.device, cmd, data, timeout=timeout) 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}) elif result['rst'] == 8: raise ServiceException( {'result': 2, 'description': u'停止指令串口通讯已完成,设备未响应', 'rst': 1}) else: if cmd in [DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE]: return return result