# -*- coding: utf-8 -*- # !/usr/bin/env python import copy import datetime import logging import random import time from typing import TYPE_CHECKING from apps.web.constant import Const from apps.web.core.adapter.base import SmartBox, start_error_timer from apps.web.core.device_define.baolai import send_request from apps.web.core.exceptions import ServiceException from apps.web.device.models import Device from apps.web.user.models import MyUser, ConsumeRecord if TYPE_CHECKING: pass logger = logging.getLogger(__name__) class ChargingGatewayPlugBox(SmartBox): """ """ def __init__(self, device): super(ChargingGatewayPlugBox, self).__init__(device) devObj = self.device.my_obj self.nodeDict = devObj.nodeDict self.billingType = devObj.otherConf.get('billingType', 1) # 0 :电量 1:时间 2:功率 self.config_list = devObj.otherConf.get('config_list', []) self.onceCard = devObj.otherConf.get('onceCard', 100) self.cardTime = devObj.otherConf.get('cardTime', 180) self.cardElec = devObj.otherConf.get('cardElec', 1) self.node_index = None for nodeIndex, nodeDevNo in devObj.nodeDict.items(): if nodeDevNo == device['devNo']: self.node_index = int(nodeIndex) break else: continue self.gatewayDevNo = Device.get_collection().find({'devNo':devObj.devNo})[0]['gateImei'] @property def isHaveStopEvent(self): return True def translate_funcode(self, funCode): funCodeDict = { } return funCodeDict.get(funCode, '') def translate_event_cmdcode(self, cmdCode): cmdDict = { } return cmdDict.get(cmdCode, '') def get_port_from_ab(self, portAB): portConf = {'A': 0, 'B': 1, 'C': 2} if portAB in portConf: return portConf[portAB] return portAB def get_abport_from_index(self, port): portConf = {'0': 'A', '1': 'B', '2': 'C'} return portConf.get(port) def send_request(self, cmdPath, jsonPara,cmdKind=2): return send_request(self.device.devNo, self.gatewayDevNo, cmdPath, jsonPara,cmdKind) def test(self, coins, port=1): return self.send_request('device/plug/list', {}) @start_error_timer(missMessages=[u"请您选择合适的充电线路、电池类型信息", u"请您选择合适的充电线路", u"该端口正在使用中"]) 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'请您选择合适的充电线路'}) port = int(self.get_port_from_ab(attachParas['chargeIndex'])) attachParas['chargeIndex'] = port unit = package.get('unit', u'分钟') needTime, needElec = None, None jsonPara = {'node_index': self.node_index, 'port_index': port, 'switch_state': 1} cmdPath = 'cmd/write-node' if self.billingType == 1: if unit == u'秒': if int(package['time']) < 60: raise ServiceException({'result': 2, 'description': u'套餐的最小时间不能小于60秒'}) needTime = int(float(package['time']) / 60.0) jsonPara.update({'charge_time': needTime, 'charge_mode': 1, 'charge_energy': 0}) elif unit == u'分钟': needTime = int(float(package['time'])) jsonPara.update({'charge_time': needTime, 'charge_mode': 1, 'charge_energy': 0}) elif unit == u'小时': needTime = int(float(package['time']) * 60) jsonPara.update({'charge_time': needTime, 'charge_mode': 1, 'charge_energy': 0}) elif unit == u'天': needTime = int(float(package['time']) * 60 * 24) jsonPara.update({'charge_time': needTime, 'charge_mode': 1, 'charge_energy': 0}) else: raise ServiceException({'result': 2, 'description': u'运营商没有配置正确的套餐,请运营商配置正确的套餐'}) elif self.billingType == 0: if unit == u'度': needElec = int(float(package['time']) * 1000) # 微度 jsonPara.update({'charge_energy': needElec, 'charge_mode': 0, 'charge_time': 0}) else: raise ServiceException({'result': 2, 'description': u'运营商没有配置正确的套餐,请运营商配置正确的套餐'}) else: if not self.config_list: raise ServiceException({'result': 2, 'description': u'运营商没有配置正确的分档功率,请运营商配置正确的分档功率'}) cmdPath = 'cmd/write-node-with-power' coins = int(float(package['coins']) * 100) # 单位为分 jsonPara.update({'power': {'money': coins, 'config_list': self.config_list}}) devInfo = self.send_request(cmdPath, jsonPara) if devInfo['data']['code'] != 0: raise ServiceException({'result': 2, 'description': u'设备服务器返回错误,建议您重试,或者联系客服'}) if devInfo['rst'] == 0: # 成功 newValue = { str(port): { 'status': Const.DEV_WORK_STATUS_WORKING, 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') } } else: # TODO result的枚举列出原因 raise ServiceException({'result': 2, 'description': u'充电插座响应异常,请您稍后再试哦'}) servicedInfo = {} if self.billingType == 1: finishedTime = int(time.time()) + needTime * 60 devInfo['needTime'] = needTime servicedInfo = {'needTime': needTime, 'billingType': 'time'} elif self.billingType == 0: finishedTime = int(time.time()) + 60 * 60 * 10 # 设定10个小时,确实很难知道可以用多久结束 devInfo['needElec'] = float(package['time']) servicedInfo = {'needElec': float(package['time']), 'billingType': 'elec'} else: finishedTime = int(time.time()) + 60 * 60 * 10 # 设定10个小时,确实很难知道可以用多久结束 servicedInfo = {'billingType': 'power'} newValue[str(port)].update({'power':10}) # 端口下的数据依赖心跳上报,但是心跳有时间间隔,这样查看服务的时候,因为功率是0,会认为已经结束,这里给一个初始值 newValue.update({'finishedTime': finishedTime}) Device.clear_port_control_cache(self._device['devNo'], port) Device.update_dev_control_cache(self._device['devNo'], newValue) devInfo['finished_time'] = finishedTime devInfo['sequanceNo'] = devInfo['data']['data']['transaction_id'] devInfo['servicedInfo'] = servicedInfo return devInfo # 获取设备配置参数 def get_dev_setting(self): if not self.node_index: raise ServiceException({'result': 2, 'description': u'读取失败!请您先把插座本身作为子节点添加进来,选择扫码增加子节点,然后扫码添加即可'}) devInfo = self.send_request('cmd/get-node-config', {'node_index': self.node_index},3) if devInfo['data']['code'] != 0: raise ServiceException({'result': 2, 'description': u'设备服务器返回错误,建议您重试,或者联系客服'}) config = devInfo['data']['data'] billDict = {'0': 'elec', '1': 'time', '2': 'power'} config.update({ 'chargeType': billDict.get(str(self.billingType)), 'once_card': self.onceCard*0.01, 'time': self.cardTime, 'elec': self.cardElec, 'config_list': self.config_list, 'charge_full_timeout':config['charge_full_time_threshold'], 'no_load_timeout':config['no_load_time_threshold'], }) return config # 设置设备配置参数 def set_dev_setting(self, setConf): setConf.update({'node_index': self.node_index}) devObj = Device.objects.get(devNo=self._device['devNo']) billDict = {'elec': 0, 'time': 1, 'power': 2} devObj.otherConf.update({ 'billingType': billDict.get(setConf['chargeType']), 'onceCard': int(float(setConf['once_card'])*100), 'cardTime': int(setConf['time']), 'cardElec': int(setConf['elec']), 'config_list': setConf['config_list'], }) devObj.save() setConf.pop('chargeType') setConf.pop('once_card') setConf.pop('time') setConf.pop('elec') setConf.pop('config_list') setConf.update({ 'charge_full_time_threshold':int(setConf['charge_full_timeout']), 'no_load_time_threshold':int(setConf['no_load_timeout']) }) setConf.pop('charge_full_timeout') setConf.pop('no_load_timeout') devInfo = self.send_request('cmd/set-node-config', setConf,3) if devInfo['data']['code'] != 0: raise ServiceException({'result': 2, 'description': u'设备服务器返回错误,建议您重试,或者联系客服'}) return devInfo def get_port_status_from_dev(self): # 先到设备上,把所有子节点的信息取出来,记录到主节点的缓存 devInfo = self.send_request('cmd/get-status', {'node_index': self.node_index}) if devInfo['data']['code'] != 0: raise ServiceException({'result': 2, 'description': u'设备服务器返回错误,建议您重试,或者联系客服'}) allPorts, usedPorts = 0, 0 result = {} for portInfo in devInfo['data']['data']['port_list']: portId = str(portInfo['index']) portDict = { 'status': self.__translate_status_from_str(str(portInfo['charge_status'])), 'watt': portInfo['power'], 'ampr': portInfo['current'] * 0.001, 'voltage': portInfo['voltage'], 'elec': portInfo['energy_consumed'] * 0.001, 'usedTime': portInfo['time_consumed'], 'duration': portInfo['time_consumed'] } sequanceNo = portInfo['transaction_id'] if sequanceNo: try: rcd = ConsumeRecord.objects.get(sequanceNo=sequanceNo) portDict['openId'] = rcd['openId'] portDict['coins'] = float(str(rcd['coin'])) # 都用coins portDict['money'] = float(str(rcd['money'])) portDict['sequanceNo'] = sequanceNo if u'虚拟卡' in rcd.remarks: portDict['consumeType'] = 'mobile_vcard' elif u'刷卡' in rcd.remarks: portDict['consumeType'] = 'card' portDict['cardNo'] = rcd.servicedInfo.get('cardNo') else: portDict['consumeType'] = 'mobile' portDict['billingType'] = 'power' if rcd.servicedInfo['billingType'] == 'time': portDict['billingType'] = 'time' portDict['needTime'] = rcd.servicedInfo['needTime'] portDict['leftTime'] = rcd.servicedInfo['needTime'] - portInfo['time_consumed'] or 0 if rcd.servicedInfo['billingType'] == 'elec': portDict['billingType'] = 'elec' portDict['needElec'] = rcd.servicedInfo['needElec'] user = MyUser.objects(openId=portDict['openId']).first() if user: portDict['nickName'] = user.nickname except Exception, e: # IC卡,如果没有绑定,不会有consumeRcd,应该直接从订单中拿数据 pass result[portId] = portDict if portInfo['charge_status'] == 1: usedPorts += 1 allPorts += 1 result.update({'usedPorts': usedPorts, 'allPorts': allPorts, 'usePorts': allPorts - usedPorts}) Device.update_dev_control_cache(self._device['devNo'], result) return result def get_port_info(self, line): line = self.get_port_from_ab(line) portCache = Device.get_dev_control_cache(self.device.devNo) return portCache.get(str(line), {}) def __translate_status_from_str(self, status): dictConf = { '0': Const.DEV_WORK_STATUS_IDLE, '1': Const.DEV_WORK_STATUS_WORKING, '2': Const.DEV_WORK_STATUS_IDLE, '3': Const.DEV_WORK_STATUS_IDLE, } return dictConf.get(status, Const.DEV_WORK_STATUS_FAULT) def get_port_status(self, force=False): if force: self.get_port_status_from_dev() portCache = Device.get_dev_control_cache(self._device['devNo']) result = {} for ii in range(5): if str(ii) in portCache: if ii == 0: result['A'] = portCache[str(ii)] elif ii == 1: result['B'] = portCache[str(ii)] elif ii == 2: result['C'] = portCache[str(ii)] return result def lock_unlock_port(self, port, lock=True): port = self.get_port_from_ab(port) portInfo = self.get_port_info(port) if portInfo['status'] == Const.DEV_WORK_STATUS_WORKING: raise ServiceException({'result': 2, 'description': u'当前端口正在使用,请您先关闭掉后,再操作'}) 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 stop_charging_port(self, port): port = self.get_port_from_ab(port) portInfo = self.get_port_info(port) if portInfo.get('billingType', None) == 'power': devInfo = self.send_request('cmd/write-node-with-power', {'node_index': self.node_index, 'port_index': port, 'switch_state': 0, 'power': {'money': 0, 'config_list': [{'power': 200, 'price': 100, 'time': 240}]}}) else: devInfo = self.send_request('cmd/write-node', {'node_index': self.node_index, 'port_index': port, 'switch_state': 0, 'charge_mode': 0, 'charge_time': 0, 'charge_energy': 0}) if devInfo['data']['code'] != 0: raise ServiceException({'result': 2, 'description': u'设备服务器返回错误,建议您重试,或者联系客服'}) if devInfo['rst'] == 0: Device.update_dev_control_cache(self._device['devNo'], {str(port): {'status': Const.DEV_WORK_STATUS_IDLE}}) return True if devInfo['rst'] == 0 else False def add_to_gateway(self, gatewayDevNo): self.add_node(self._device['devNo']) def remove_from_gateway(self, gatewayDevNo): self.remove_node(self._device['devNo']) # 如果 # 柏来有两条刷卡事件,一个是查询余额,对应的是余额播报回复;一个是开始充电事件,对应的是启动设备充电 def response_card_start(self, portIndex, orderNo): jsonParas = {'node_index': self.node_index, 'port_index': portIndex, 'transaction_id': orderNo, 'switch_state': 1, 'charge_type': self.billingType} if self.billingType == 0: # 按电量 jsonParas.update({'charge_type': 2}) jsonParas.update({'charge_energy': self.onceCard * 0.01 * self.cardElec * 1000}) elif self.billingType == 1: # 按时间 jsonParas.update({'charge_type': 1}) jsonParas.update({'charge_time': self.onceCard * 0.01 * self.cardTime}) else: # 按功率 jsonParas.update( {'charge_type': 3, 'charge_power': {'money': self.onceCard, 'config_list': self.config_list}}) devInfo = self.send_request('cmd/control-nfc-charge', jsonParas) if devInfo['data']['code'] != 0: raise ServiceException({'result': 2, 'description': u'设备服务器返回错误,建议您重试,或者联系客服'}) return devInfo def active_deactive_port(self, port, active): port = self.get_port_from_ab(port) if active: raise ServiceException({'result': 2, 'description': u'该设备不支持直接打开端口'}) return self.stop_charging_port(port) def set_device_function_param(self, request, lastSetConf): newConf = copy.deepcopy(request.POST) newConf.pop('logicalCode', None) self.set_dev_setting(newConf) def response_card_balance(self, cardNo, nodeIndex, portIndex, balance): devInfo = self.send_request('cmd/write-card-query-response', { 'node_index': nodeIndex, 'port_index': portIndex, 'card_no': cardNo, 'balance': int(float(balance * 100)), 'timeout': 10 }) if devInfo['data']['code'] != 0: raise ServiceException({'result': 2, 'description': u'设备服务器返回错误,建议您重试,或者联系客服'}) return devInfo def get_node(self, node): nodeInfo = { 'signal':node.signal, 'rssi':node.get('rssi',39), 'online':node.online, } if 'suid' in node: nodeInfo.update({ 'devNo':node['suid'], 'suid':node['suid'], }) if 'devNo' in node: nodeInfo.update({ 'devNo':node['devNo'], 'suid':node['devNo'], }) return nodeInfo def get_dev_online_status(self): devInfo = self.send_request('device/gateway/detail', {}) return devInfo['data']['plug_gateway']['is_online'] def update_dev_info(self): devInfo = self.send_request('device/gateway/update', {'province': '', 'city': '', 'county': '', 'address': '', 'longitude': 0.0, 'latitude': 0.0}) if devInfo['data']['code'] != 0: raise ServiceException({'result': 2, 'description': u'设备服务器返回错误,建议您重试,或者联系客服'}) def add_node(self, nodeDevNo): # 首先检查子节点是否已经注册了 devObj = Device.objects.filter(devNo = nodeDevNo).first() if devObj is None: raise ServiceException({'result': 2, 'description': u'请先扫码注册子设备插座,然后再加入网关'}) # 检查是否绑定在其他主节点,如果绑定了其他主节点,必须提示 if devObj.gatewayNode and devObj.gatewayNode != self._device['devNo']: raise ServiceException({'result': 2, 'description': u'该子节点已经绑定在其他主节点下,请先解绑后,再重新添加'}) nodeList = self.get_node_list() # 首先检查是否已经添加进来了 exist = devObj.gatewayNode == self._device['devNo'] sendList = [{'node_index': node['node_index'], 'plug_id': node['suid']} for node in nodeList] if exist: # 如果已经存在了,直接下发就Ok pass else: nodeIndexList = [node['node_index'] for node in nodeList] ii = 1 for ii in range(1, 50): if ii not in nodeIndexList: break else: continue sendList.append({'node_index': ii, 'plug_id': nodeDevNo}) devInfo = self.send_request('cmd/build-network', {'channel': random.randint(1, 8), 'plug_list': sendList, 'timeout': 15}) if devInfo['data']['code'] != 0: raise ServiceException({'result': 2, 'description': u'设备服务器返回错误,建议您重试,或者联系客服'}) # 主节点内容更新 devObj = Device.objects.get(devNo = self._device['devNo']) for sendInfo in sendList: devObj.nodeDict.update({str(sendInfo['node_index']):sendInfo['plug_id']}) devObj.save() # 子节点的也要更新 devObj = Device.objects.get(devNo = nodeDevNo) devObj.gatewayNode = self._device['devNo'] devObj.save() def remove_node(self, nodeDevNo): nodeList = self.get_node_list() sendList = [] index = -1 nodeDict = {} for node in nodeList: if node['suid'] != nodeDevNo: sendList.append({'node_index': node['node_index'], 'plug_id': node['suid']}) nodeDict[str(node['node_index'])] = node['suid'] else: index = node['node_index'] if index == -1: return devInfo = self.send_request('cmd/build-network', {'channel': random.randint(1, 8), 'plug_list': sendList, 'timeout': 5}) if devInfo['data']['code'] != 0: raise ServiceException({'result': 2, 'description': u'设备服务器返回错误,建议您重试,或者联系客服'}) # 主节点更新 try: devObj = Device.objects.get(devNo = self._device['devNo']) devObj.nodeDict = nodeDict devObj.save() except Exception, e: pass # 子节点更新 try: devObj = Device.objects.get(devNo = nodeDevNo) devObj.gatewayNode = '' devObj.save() except Exception, e: return # 如果是一体板的,需要把端口删除掉 if nodeDevNo == self._device['devNo']: Device.invalid_device_control_cache(nodeDevNo) def get_signal(self): result = {'rst':0} devInfo = Device.get_dev(self._device['devNo']) if devInfo['online']: result.update({'signal':devInfo['signal']}) return result else: return {'signal':0,'rst':0} def get_node_list(self): result = [] for index,nodeDevNo in self.nodeDict.items(): nodeDevInfo = Device.get_dev(nodeDevNo) nodeInfo = self.get_node(nodeDevInfo) nodeInfo.update({'node_index':int(index)}) result.append(nodeInfo) return result def get_node_devNo(self, nodeIndex): return self.nodeDict.get(str(nodeIndex))