# -*- coding: utf-8 -*- # !/usr/bin/env python import copy import datetime import time import uuid from decimal import Decimal from apilib.utils_AES import EncryptDate from apilib.utils_datetime import to_datetime from apps.web.constant import DeviceCmdCode, Const, MQTT_TIMEOUT, FAULT_CODE, ErrorCode 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 from apps.web.user.models import ConsumeRecord, Card cardKey = 'FR4e1OFCnDdrYA7u' class ChargingWEIFULEBox(SmartBox): def __init__(self, device): super(ChargingWEIFULEBox, self).__init__(device) def translate_funcode(self, fun_code): fun_codeDict = { '01': u'查询所有端口状态', '02': u'查询端口详细信息', '03': u'上报投币充电事件', '04': u'上报刷卡事件', '05': u'上报充电结束事件', '06': u'远程停止充电', '07': u'远程启动充电', '08': u'查询投币总额', '09': u'清除投币总数', '0A': u'查询订单信息', } return fun_codeDict.get(fun_code, '') def translate_event_cmdcode(self, cmdCode): cmdDict = { } return cmdDict.get(cmdCode, '') def test(self, coins): data = {'fun_code': 0x07, 'order_id': '1111', 'coins': coins, 'port': 1, 'time': 60} devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], 'data': data}) return devInfo def check_feedback_result(self, devInfo): if not devInfo.has_key('rst'): raise ServiceException({'result': 2, 'description': u'报文异常'}) if devInfo['rst'] == -1: raise ServiceException({'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'}) if devInfo['rst'] == 1: raise ServiceException({'result': 2, 'description': u'串口通讯失败,您稍候再试,或者联系客服'}) if devInfo['rst'] == 2: raise ServiceException({'result': 2, 'description': u'端口被禁用'}) if devInfo['rst'] == 3: raise ServiceException({'result': 2, 'description': u'端口计量器故障'}) if devInfo['rst'] == 4: raise ServiceException({'result': 2, 'description': u'设备订单已达上限'}) if devInfo['rst'] == 5: raise ServiceException({'result': 2, 'description': u'设备正在自检'}) def _check_package(self, package): unit = package.get("unit") _time = package.get("time") chrmt = self.device['otherConf'].get('chrmt') if not chrmt: chrmt = self.get_dev_setting().get('chrmt') if chrmt == 'TIME' or chrmt == 'POWER': if unit == u'秒': if int(package['time']) < 60: raise ServiceException({'result': 2, 'description': u'套餐的最小时间不能小于60秒'}) _time = int(package['time']) unit = '秒' elif unit == u'分钟': _time = int(package['time']) * 60 unit = '秒' elif unit == u'小时': _time = int(float(package['time']) * 60 * 60) unit = '秒' elif unit == u'天': _time = int(float(package['time']) * 60 * 60 * 24) unit = '秒' else: raise ServiceException({"result": 2, "description": u"套餐单位错误,应选取单位(时间),请联系经销商"}) elif chrmt == 'ELEC': if unit == u'度': _time = int(float(package['time']) * 1000000) # 微度,需要乘于16个零 else: raise ServiceException({"result": 2, "description": u"套餐单位错误,应选取单位(度),请联系经销商"}) else: raise ServiceException({"result": 2, "description": u"套餐单位错误,应选取单位(时间,度),请联系经销商"}) return _time, unit def check_dev_status(self, attachParas = None): if attachParas.get("isTempPackage") == True: washConfig = self.device.get("tempWashConfig", {}) else: washConfig = self.device.get("washConfig", {}) packageId = attachParas.get("packageId") if not packageId: # 快捷支付过来的 第一次拉起支付的时候已经校验通过了 return package = washConfig.get(packageId) self._check_package(package) if package.get('name') == '充满自停' and package.get('coins') == 0 and package.get('price') == 0: port = int(attachParas['chargeIndex']) lineInfo = Device.get_dev_control_cache(self.device.devNo).get(str(port), {}) if lineInfo.get('status') == Const.DEV_WORK_STATUS_WORKING: raise ServiceException({'result': 2, 'description': 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(attachParas['chargeIndex']) unit = package.get('unit', u'分钟') needTime, power = None, None coins = int(float(package['coins']) * 100) # 单位为分 onPoints = attachParas.get('onPoints') if onPoints: # 远程上分 self.stop_charging_port(port) order_no = ConsumeRecord.make_no() else: order_no = str(attachParas.get("orderNo")) if package.get('name') == '充满自停' and package.get('coins') == 0 and package.get('price') == 0: lineInfo = Device.get_dev_control_cache(self.device.devNo).get(str(port), {}) if lineInfo.get('status') == Const.DEV_WORK_STATUS_WORKING: raise ServiceException({'result': 2, 'description': u'当前端口已处于工作状态, 请换个端口使用充电'}) needTime = 999 * 60 data = {'fun_code': 0x07, 'order_id': order_no, 'coins': 10 * 100, 'port': port, 'time': needTime} else: if unit == u'秒': if int(package['time']) < 60: raise ServiceException({'result': 2, 'description': u'套餐的最小时间不能小于60秒'}) needTime = int(float(package['time'])) data = {'fun_code': 0x07, 'order_id': order_no, 'coins': coins, 'port': port, 'time': needTime} elif unit == u'分钟': needTime = int(package['time']) * 60 data = {'fun_code': 0x07, 'order_id': order_no, 'coins': coins, 'port': port, 'time': needTime} elif unit == u'小时': needTime = int(float(package['time']) * 60 * 60) data = {'fun_code': 0x07, 'order_id': order_no, 'coins': coins, 'port': port, 'time': needTime} elif unit == u'天': needTime = int(float(package['time']) * 60 * 60 * 24) data = {'fun_code': 0x07, 'order_id': order_no, 'coins': coins, 'port': port, 'time': needTime} elif unit == u'度': power = int(float(package['time']) * 1000000) # 微度,需要乘于16个零 data = {'fun_code': 0x07, 'order_id': order_no, 'coins': coins, 'port': port, 'elec': power} else: needTime = int(package['time']) data = {'fun_code': 0x07, 'order_id': order_no, 'coins': coins, 'port': port, 'time': needTime} devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], 'data': data}, timeout=MQTT_TIMEOUT.START_DEVICE) self.check_feedback_result(devInfo) data = devInfo['data'] if devInfo['rst'] == 0: # 成功 devInfo['consumeOrderNo'] = order_no 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'充电桩响应异常,请您稍后再试哦'}) if needTime: finishedTime = int(time.time()) + needTime devInfo['needTime'] = needTime / 60 if power: finishedTime = int(time.time()) + 60 * 60 * 10 # 设定10个小时,确实很难知道可以用多久结束 devInfo['needElec'] = float(package['time']) newValue.update({'finishedTime': finishedTime}) Device.update_dev_control_cache(self._device['devNo'], newValue) devInfo['finished_time'] = finishedTime devInfo['consumeOrderNo'] = order_no return devInfo def analyze_event_data(self, data): if data.get('fun_code') == '34': # 如果是结束事件,需要把reason翻译出来 descDict = { '1': u'开始充电,但是没有接充电器', '2': u'充电过程中,插座脱落', '3': u'用户按下关闭按钮关闭', '4': u'用户刷卡结束充电', '5': u'远程结束充电', '6': u'充电端口故障,为了安全主动关闭', '7': u'订购的时间使用完毕', '8': u'订购的电量使用完毕', '9': u'本端口功率过载,主动关闭', '10': u'整机功率过载,主动关闭', '11': u'其他异常导致的关闭' } order = data['order'] order['reason'] = descDict.get(str(order['closeType']), u'') data['order'] = order return data def get_dev_consume_count(self): devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x08}}) self.check_feedback_result(devInfo) data = devInfo['data'] return {'cardFee': data['total_card'] / 100.0, 'coinFee': data['total_coin'] / 100.0} # 单位为分 def get_many_port_info(self, portList): devInfo = MessageSender.send(self.device, self.make_random_cmdcode(), {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x12}}) self.check_feedback_result(devInfo) pay_unit = self.show_pay_unit resultDict = {} for port, data in devInfo['data']['details'].items(): if port not in portList: continue result = {'index': port} result['status'] = self.__translate_status_from_str(data['status']) result['power'] = round(data.get('watt', 0), 2) result['ampere'] = round(data.get('ampr', 0) / 1000.0, 2) result['voltage'] = round(data.get('volt'), 2) _wait = [] for exec_order in data['orders']: if exec_order.get('status') == 'running': result['coins'] = round(exec_order.get('coins', 0) * 0.01, 2) result['usedTime'] = round(exec_order.get('time', 0) / 60.0, 1) result['usedElec'] = round(exec_order.get('elec') / 1000000.0, 2) result['startTime'] = datetime.datetime.fromtimestamp(int(exec_order.get('execute_time', 0))).strftime( '%m-%d %H:%M:%S') if exec_order.get('chrmt') == 'TIME': result['needTime'] = round(exec_order.get('amount_time', 0) / 60.0, 1) result['leftTime'] = round(exec_order.get('left_time', 0) / 60.0, 1) leftMoney = round((result['coins'] * exec_order.get('left_time', 0) / exec_order.get('amount_time', 0)), 2) consumeMoney = round((result['coins'] - leftMoney), 2) if exec_order.get('order_type') == 'apps_start': result['consumeType'] = 'mobile' result['consumeMoney'] = '{}{}'.format(consumeMoney, pay_unit) result['leftMoney'] = '{}{}'.format(leftMoney, pay_unit) try: orderNo = exec_order.get('id', 0) order = ConsumeRecord.objects.get(orderNo=orderNo) result['nickName'] = order.user.nickname package = order.package if package.get('name') == '充满自停' and package.get('coins') == 0 and package.get( 'price') == 0: # 后付费 result['consumeType'] = 'postpaid' result['needTime'] = '充满自停' result.pop('needElec', None) result.pop('leftMoney', None) result.pop('leftTime', None) result.pop('leftElec', None) except: pass if exec_order.get('order_type') == 'card_start': result['consumeType'] = 'card' result['cardNo'] = str(int(exec_order.get('card_no', 0), 16)) result['cardConsumeMoney'] = '{}{}'.format(consumeMoney, pay_unit) result['cardLeftMoney'] = '{}{}'.format(leftMoney, pay_unit) result['cardBalance'] = '{}{}'.format(round(exec_order.get('balance', 0) * 0.01, 2), pay_unit) try: card = Card.objects.get(cardNo=str(int(exec_order.get('card_no', 0), 16))) result['nickName'] = card.cardName or card.nickName if card.cardType == 'ID': # id卡 订单余额显示有问题 result['cardBalance'] = '{}{}'.format(round(exec_order.get('balance', 0) * 0.001, 2), pay_unit) except: pass elif exec_order.get('chrmt') == 'ELEC': result['needElec'] = round(exec_order.get('amount_elec', 0) * 0.000001, 2) result['leftElec'] = round(exec_order.get('left_elec', 0) * 0.000001, 4) leftMoney = round((result['coins'] * exec_order.get('left_elec', 0) / exec_order.get('amount_elec', 0)), 2) consumeMoney = round((result['coins'] - leftMoney), 2) if exec_order.get('order_type') == 'apps_start': result['consumeType'] = 'mobile' result['consumeMoney'] = '{}{}'.format(consumeMoney, pay_unit) result['leftMoney'] = '{}{}'.format(leftMoney, pay_unit) try: orderNo = exec_order.get('id') order = ConsumeRecord.objects.get(orderNo=orderNo) result['nickName'] = order.user.nickname package = order.package if package.get('name') == '充满自停' and package.get('coins') == 0 and package.get( 'price') == 0: # 后付费 result['consumeType'] = 'postpaid' result['needTime'] = '充满自停' result.pop('needElec', None) result.pop('leftMoney', None) result.pop('leftTime', None) result.pop('leftElec', None) except: pass if exec_order.get('order_type') == 'card_start': result['consumeType'] = 'card' result['cardNo'] = str(int(exec_order.get('card_no', 0), 16)) result['cardConsumeMoney'] = '{}{}'.format(consumeMoney, pay_unit) result['cardLeftMoney'] = '{}{}'.format(leftMoney, pay_unit) result['cardBalance'] = '{}{}'.format(round(exec_order.get('balance', 0) * 0.01, 2), pay_unit) try: card = Card.objects.get(cardNo=str(int(exec_order.get('card_no', 0), 16))) result['nickName'] = card.cardName or card.nickName if card.cardType == 'ID': # id卡 订单余额显示有问题 result['cardBalance'] = '{}{}'.format(round(exec_order.get('balance', 0) * 0.001, 2), pay_unit) except: pass else: pass elif exec_order.get('status') == 'waiting': _one = {} if exec_order.get('chrmt') == 'TIME': _one['leftTime'] = round(exec_order.get('left_time', 0) / 60.0, 1) _one['needTime'] = '{}分钟'.format(round(exec_order.get('amount_time', 0) / 60.0, 1)) elif exec_order.get('chrmt') == 'ELEC': _one['leftElec'] = round(exec_order.get('left_elec', 0) * 0.000001, 4) _one['needElec'] = round(exec_order.get('amount_elec', 0) * 0.000001, 2) else: pass if exec_order.get('order_type') == 'apps_start': _one['consumeType'] = 'mobile' _one['coins'] = '{}{}'.format(round(exec_order.get('coins', 0) / 100.0, 2), pay_unit) try: order = ConsumeRecord.objects.get(orderNo=exec_order.get('id', 0)) _one['nickName'] = order.user.nickname except Exception: pass elif exec_order.get('order_type') == 'card_start': _one['consumeType'] = 'card' _one['coins'] = '{}{}'.format(round(exec_order.get('coins', 0) / 100.0, 2), pay_unit) _one['cardNo'] = str(int(exec_order.get('card_no', 0), 16)) _one['cardBalance'] = '{}{}'.format(round(exec_order.get('balance', 0) * 0.01, 2), pay_unit) try: card = Card.objects.get(cardNo=str(int(exec_order.get('card_no', 0), 16))) _one['nickName'] = card.cardName or card.nickName if card.cardType == 'ID': # id卡 订单余额显示有问题 _one['cardBalance'] = '{}{}'.format(round(exec_order.get('balance', 0) * 0.001, 2), pay_unit) except: pass else: pass _wait.append(_one) else: pass if _wait: result['waittingOrder'] = _wait resultDict[port] = result return resultDict def get_port_info(self, line): devInfo = MessageSender.send(self.device, self.make_random_cmdcode(), {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x02, 'port': int(line)}}) self.check_feedback_result(devInfo) portInfo = devInfo.get('data', {}) result = {} pay_unit = self.show_pay_unit exec_orders = portInfo.get('orders', []) _wait = [] for exec_order in exec_orders: if exec_order['status'] == 'running': result['voltage'] = round(portInfo.get('volt', 0), 2) result['power'] = round(portInfo.get('watt', 0), 2) result['ampere'] = round((portInfo.get('ampr', 0) * 0.001), 2) result['coins'] = '{}'.format(round(exec_order['coins'] / 100.0, 2)) result['usedTime'] = round(exec_order.get('time', 0) / 60, 1) result['usedElec'] = round(exec_order.get('elec', 0) * 0.000001, 4) if exec_order['chrmt'] == 'TIME': result['leftTime'] = round(exec_order.get('left_time', 0) / 60, 1) result['needTime'] = '{}分钟'.format(round(exec_order['amount_time'] / 60.0, 1)) elif exec_order['chrmt'] == 'ELEC': result['needElec'] = round(exec_order.get('amount_elec', 0) * 0.000001, 2) result['leftElec'] = round(exec_order.get('left_elec', 0) * 0.000001, 4) else: pass if exec_order['order_type'] == 'apps_start': result['consumeType'] = 'mobile' result['consumeMoney'] = '{}{}'.format(round(exec_order.get('money', 0) * 0.01, 2), pay_unit) result['leftMoney'] = '{}{}'.format(round(exec_order.get('left_money', 0) * 0.01, 2), pay_unit) try: orderNo = exec_order['id'] order = ConsumeRecord.objects.get(orderNo=orderNo) result['nickName'] = order.user.nickname package = order.package if package.get('name') == '充满自停' and package.get('coins') == 0 and package.get( 'price') == 0: # 后付费 result['consumeType'] = 'postpaid' result['needTime'] = '充满自停' result.pop('needElec', None) result.pop('leftMoney', None) result.pop('leftTime', None) result.pop('leftElec', None) except: pass if exec_order['order_type'] == 'card_start': result['consumeType'] = 'card' result['cardNo'] = str(int(exec_order['card_no'], 16)) result['cardConsumeMoney'] = '{}{}'.format(round(exec_order.get('money', 0) * 0.01, 2), pay_unit) result['cardLeftMoney'] = '{}{}'.format(round(exec_order.get('left_money', 0) * 0.01, 2), pay_unit) result['cardBalance'] = '{}{}'.format(round(exec_order['balance'] * 0.01, 2), pay_unit) try: card = Card.objects.get(cardNo=exec_order['card_no']) result['nickName'] = card.cardName or card.nickName except: pass else: pass if exec_order['status'] == 'waiting': _one = {} if exec_order['chrmt'] == 'TIME': _one['leftTime'] = round(exec_order.get('left_time', 0) / 60, 1) _one['needTime'] = '{}分钟'.format(round(exec_order['amount_time'] / 60.0, 1)) elif exec_order['chrmt'] == 'ELEC': _one['needElec'] = round(exec_order.get('amount_elec', 0) * 0.000001, 2) _one['leftElec'] = round(exec_order.get('left_elec', 0) * 0.000001, 4) else: pass if exec_order['order_type'] == 'apps_start': _one['consumeType'] = 'mobile' _one['coins'] = '{}{}'.format(round(exec_order['coins'] / 100.0, 2), pay_unit) try: order = ConsumeRecord.objects.get(orderNo=exec_order['id']) _one['nickName'] = order.user.nickname except Exception: pass elif exec_order['order_type'] == 'card_start': _one['consumeType'] = 'card' _one['coins'] = '{}{}'.format(round(exec_order['coins'] / 100.0, 2), pay_unit) _one['cardNo'] = str(int(exec_order['card_no'], 16)) _one['cardBalance'] = '{}{}'.format(round(exec_order['balance'] * 0.01, 2), pay_unit) try: card = Card.objects.get(cardNo=exec_order['card_no']) _one['nickName'] = card.cardName or card.nickName except Exception: pass else: pass _wait.append(_one) if _wait: result['waittingOrder'] = _wait return result def __translate_status_from_str(self, status): dictConf = { 'idle': Const.DEV_WORK_STATUS_IDLE, 'busy': Const.DEV_WORK_STATUS_WORKING, 'forbid': Const.DEV_WORK_STATUS_FORBIDDEN, 'fault': Const.DEV_WORK_STATUS_FAULT, 'running': Const.DEV_WORK_STATUS_WORKING } return dictConf.get(status, Const.DEV_WORK_STATUS_IDLE) # 访问设备,获取设备端口信息 def get_port_status_from_dev(self): devInfo = MessageSender.send(self.device, self.make_random_cmdcode(), {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x01}}) self.check_feedback_result(devInfo) data = devInfo['data'] result = {} portNum = data.get('total', 10) ii = 0 while ii < portNum: ii += 1 result[str(ii)] = {'status': self.__translate_status_from_str(data['status'].get(str(ii)))} allPorts, usedPorts, usePorts = self.get_port_static_info(result) Device.update_dev_control_cache(self._device['devNo'], {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts}) # 这里存在多线程更新缓存的场景,可能性比较低,但是必须先取出来,然后逐个更新状态,然后再记录缓存 ctrInfo = Device.get_dev_control_cache(self._device['devNo']) for strPort, info in result.items(): if ctrInfo.has_key(strPort): ctrInfo[strPort].update({'status': info['status']}) else: ctrInfo[strPort] = info Device.update_dev_control_cache(self._device['devNo'], ctrInfo) return result 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 not ctrInfo.has_key('allPorts'): self.get_port_status_from_dev() ctrInfo = Device.get_dev_control_cache(self._device['devNo']) allPorts = ctrInfo.get('allPorts', 10) 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} 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 lock_unlock_port(self, port, lock=True): portInfo = self.get_port_info(port) if portInfo and portInfo['status'] == Const.DEV_WORK_STATUS_WORKING and lock: raise ServiceException({'result': 2, 'description': u'端口正忙,请先关闭端口后,再禁止端口'}) typeStr = 'deactive' if lock else 'active' devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], 'data': {'port': int(port), 'type': typeStr, 'fun_code': 0x0D}}) self.check_feedback_result(devInfo) if devInfo['rst'] == 0: 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}}) 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'该设备不支持直接打开端口'}) return self.stop_charging_port(port) # 停止该端口下的所有任务 def stop_charging_port(self, port): devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x06, 'port': port}}) self.check_feedback_result(devInfo) 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 get_order(self, order_no): devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x0A, 'order_id': order_no}}) self.check_feedback_result(devInfo) data = devInfo.get('data', {}) exec_order = data.get('order', {}) result = {} if not exec_order.get('order_type'): # 有可能没有订单,应该返回空 return result pay_unit = self.show_pay_unit # result['ampere'] = round(data.get('ampr', 0) / 1000.0, 2) # result['voltage'] = round(data.get('volt'), 2) result['status'] = exec_order.get('status', 0) result['coins'] = round(exec_order.get('coins', 0) * 0.01, 2) result['usedTime'] = round(exec_order.get('time', 0) / 60.0, 1) result['usedElec'] = round(exec_order.get('elec') / 1000000.0, 2) if exec_order.get('status') == 'running': result['startTime'] = datetime.datetime.fromtimestamp(int(exec_order.get('execute_time', 0))).strftime( '%m-%d %H:%M:%S') result['power'] = round(exec_order.get('watt', 0), 2) if exec_order.get('chrmt') == 'TIME': result['needTime'] = round(exec_order.get('amount_time', 0) / 60.0, 1) result['leftTime'] = round(exec_order.get('left_time', 0) / 60.0, 1) leftMoney = round((result['coins'] * exec_order.get('left_time', 0) / exec_order.get('amount_time', 0)), 2) consumeMoney = round((result['coins'] - leftMoney), 2) if exec_order.get('order_type') == 'apps_start': result['consumeType'] = 'mobile' result['consumeMoney'] = '{}{}'.format(consumeMoney, pay_unit) result['leftMoney'] = '{}{}'.format(leftMoney, pay_unit) try: orderNo = exec_order.get('id', 0) order = ConsumeRecord.objects.get(orderNo=orderNo) result['nickName'] = order.user.nickname package = order.package if package.get('name') == '充满自停' and package.get('coins') == 0 and package.get( 'price') == 0: # 后付费 result['consumeType'] = 'postpaid' result['needTime'] = '充满自停' result.pop('needElec', None) result.pop('leftMoney', None) result.pop('leftTime', None) result.pop('leftElec', None) except: pass if exec_order.get('order_type') == 'card_start': result['consumeType'] = 'card' result['cardNo'] = str(int(exec_order.get('card_no', 0), 16)) result['cardConsumeMoney'] = '{}{}'.format(consumeMoney, pay_unit) result['cardLeftMoney'] = '{}{}'.format(leftMoney, pay_unit) result['cardBalance'] = '{}{}'.format(round(exec_order.get('balance', 0) * 0.01, 2), pay_unit) try: card = Card.objects.get(cardNo=str(int(exec_order.get('card_no', 0), 16))) result['cardName'] = card.cardName or card.nickName if card.cardType == 'ID': # id卡 订单余额显示有问题 result['cardBalance'] = '{}{}'.format(round(exec_order.get('balance', 0) * 0.001, 2), pay_unit) except: pass elif exec_order.get('chrmt') == 'ELEC': result['needElec'] = round(exec_order.get('amount_elec', 0) * 0.000001, 2) result['leftElec'] = round(exec_order.get('left_elec', 0) * 0.000001, 4) leftMoney = round((result['coins'] * exec_order.get('left_elec', 0) / exec_order.get('amount_elec', 0)), 2) consumeMoney = round((result['coins'] - leftMoney), 2) if exec_order.get('order_type') == 'apps_start': result['consumeType'] = 'mobile' result['consumeMoney'] = '{}{}'.format(consumeMoney, pay_unit) result['leftMoney'] = '{}{}'.format(leftMoney, pay_unit) try: orderNo = exec_order.get('id', 0) order = ConsumeRecord.objects.get(orderNo=orderNo) result['nickName'] = order.user.nickname package = order.package if package.get('name') == '充满自停' and package.get('coins') == 0 and package.get( 'price') == 0: # 后付费 result['consumeType'] = 'postpaid' result['needTime'] = '充满自停' result.pop('needElec', None) result.pop('leftMoney', None) result.pop('leftTime', None) result.pop('leftElec', None) except: pass if exec_order.get('order_type') == 'card_start': result['consumeType'] = 'card' result['cardNo'] = str(int(exec_order.get('card_no', 0), 16)) result['cardConsumeMoney'] = '{}{}'.format(consumeMoney, pay_unit) result['cardLeftMoney'] = '{}{}'.format(leftMoney, pay_unit) result['cardBalance'] = '{}{}'.format(round(exec_order.get('balance', 0) * 0.01, 2), pay_unit) try: card = Card.objects.get(cardNo=str(int(exec_order.get('card_no', 0), 16))) result['cardName'] = card.cardName or card.nickName if card.cardType == 'ID': # id卡 订单余额显示有问题 result['cardBalance'] = '{}{}'.format(round(exec_order.get('balance', 0) * 0.001, 2), pay_unit) except: pass else: pass return result def response_card_balance(self, cardNo, balance, result): devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], 'data': {'fun_code': 35, 'card_no': cardNo, 'balance': int(100 * float(balance)), 'result': result}}) self.check_feedback_result(devInfo) # 获取设备配置参数 def get_dev_setting(self): devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x0C}}) self.check_feedback_result(devInfo) result = devInfo['data'] result.update({ 'fcharge_watt': result['fcharge']['watt'], 'fcharge_time': result['fcharge']['time'], 'fcharge_check': result['fcharge'].get('check', 10), }) # 按功率分档收费 TIME = result['price']['TIME'] # [{"max":"xxx","time":"xxx"}] result['package_time'] = [] for _ in TIME: item = {} item['power'] = _[0] item['time'] = int(_[1]) / 60 # 时间未秒转换为分 result['package_time'].append(item) # 按功率收费显示 package_power = [] for _item in TIME: package_power.append({'power': _item[0], 'price': round(1.0 / (_item[1] / 60.0 / 60.0), 2)}) result['package_power'] = package_power # 按电量收费 result['package_elec'] = [] elec = Decimal(result['price']['ELEC']) / 1000000 # 默认为毫度 price = result['price'].get('price', 1) # 默认为一块钱 result['package_elec'].append({'price': price, 'elec': elec}) noload_check = result.get('noload_check') if not noload_check: noload_check = { "time": 99, "watt": 99 }, result['emptyPower'] = noload_check['watt'] result['emptyTime'] = noload_check['time'] defaultVolume = 0 volumeList = [] for k, v in result['volume'].items(): if k == 'default': defaultVolume = v else: tempList = k.split('-') volumeList.append({'start': tempList[0], 'end': tempList[1], 'volume': v}) result.pop('volume') result['volume'] = defaultVolume result['volume_list'] = volumeList result['minAfterStartCoins'] = self.device['otherConf'].get('minAfterStartCoins', 0) result['refundProtection'] = self.device['otherConf'].get('refundProtection', 5) result['chrmt'] = self.device['otherConf'].get('chrmt') or result['chrmt'] return result # 获取设备配置参数 def set_dev_setting(self, setConf): # 时间套餐不能为空 if setConf.has_key('package_time') and len(setConf['package_time']) == 0: raise ServiceException({'result': 2, 'description': u'按时间计费的套餐不能为空'}) # 检查数据 if not (int(setConf['fcharge_time']) >= 1 and int(setConf['fcharge_time']) <= 300): raise ServiceException({'result': 2, 'description': u'浮充时间必须大于等于1分钟,小于等于300分钟'}) if not (int(setConf['fcharge_check']) >= 2 and int(setConf['fcharge_check']) <= 30): raise ServiceException({'result': 2, 'description': u'浮充检测时间必须大于等于2分钟,小于等于30分钟'}) if not (int(setConf['fcharge_watt']) >= 20 and int(setConf['fcharge_watt']) <= 100): raise ServiceException({'result': 2, 'description': u'浮充功率必须大于等于20瓦,小于等于100瓦'}) if not (int(setConf['volume']) >= 0 and int(setConf['volume']) <= 7): raise ServiceException({'result': 2, 'description': u'音量必须大于等于0,小于等于7'}) volumeDict = {'default': int(setConf['volume'])} for vl in setConf['volume_list']: startTime = to_datetime('2020-01-01 %s:00' % vl['start']) endTime = to_datetime('2020-01-01 %s:00' % vl['end']) if endTime <= startTime: raise ServiceException({'result': 2, 'description': u'结束时间一定要大于起始时间。比如起始时间为07:00,结束时间为23:59'}) volumeDict['%s-%s' % (vl['start'], vl['end'])] = int(vl['volume']) setConf.pop('volume') setConf.pop('volume_list') setConf['volume'] = volumeDict # setConf['once_card'] = int(float(setConf['once_card']) * 100) # setConf['once_coin'] = int(float(setConf['once_coin']) * 100) setConf['max_watt'] = int(setConf['max_watt']) if not (int(setConf['max_watt']) >= 50 and int(setConf['max_watt']) <= 1000): raise ServiceException({'result': 2, 'description': u'最大功率必须大于等于100瓦,小于等于1000瓦'}) setConf.update({'fun_code': 0x0B}) setConf['fcharge'] = { 'watt': int(setConf['fcharge_watt']), 'time': int(setConf['fcharge_time']), 'check': int(setConf['fcharge_check']), } setConf.pop('fcharge_watt') setConf.pop('fcharge_time') setConf.pop('fcharge_check') chrmt = setConf.get('chrmt') if chrmt: package_time = setConf.get('package_time') package_elec = setConf.get('package_elec') package_power = setConf.get('package_power') price = {'ELEC': int(float(package_elec[0]['elec']) * 1000000)} # 组织功率收费价格参数 TIME = [] if chrmt == 'POWER': # 主板实际并没有power计费方式 实际为功率分档模式的换一种显示 for _item in package_power: if float(_item['price']) == 0: raise ServiceException({'result': 2, 'description': u'计费参数设置(按功率收费)价格不能为0'}) TIME.append( [int(_item['power']), round(1.0 / float(_item['price']) * 60 * 60, 1)] # 价格倍率 * 固定时间(60分钟) * 60秒 ) if not TIME: raise ServiceException({'result': 2, 'description': u'计费参数设置(按功率收费)缺少收费标准'}) setConf['chrmt'] = 'TIME' elif chrmt == 'TIME': # 时间模式下 的时间计费规则 for _item in package_time: TIME.append( [int(_item['power']), int(_item['time']) * 60] ) if not TIME: raise ServiceException({'result': 2, 'description': u'计费参数设置(按时间收费)缺少收费标准'}) price['TIME'] = TIME setConf['chrmt'] = 'TIME' elif chrmt == 'ELEC': # 同样也处理一次时间计费规则 用于下发给设备 for _item in package_time: TIME.append( [int(_item['power']), int(_item['time']) * 60] ) setConf['chrmt'] = 'ELEC' else: pass price['TIME'] = TIME setConf.update({'price': price}) setConf.pop('package_time', None) setConf.pop('package_power', None) setConf.pop('package_elec', None) # 空载检测 setConf['noload_check'] = {} setConf['noload_check']['watt'] = int(setConf.pop('emptyPower')) setConf['noload_check']['time'] = int(setConf.pop('emptyTime')) devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], 'data': setConf}) self.check_feedback_result(devInfo) # 服务器侧需要保存的参数处理 unit_price = None if chrmt == 'TIME': unit_price = {'unit': '分钟', 'value': max(map(lambda _: _[1] / 60.0, setConf['price']['TIME']))} elif chrmt == 'POWER': unit_price = {'unit': '分钟', 'value': max(map(lambda _: _[1] / 60.0, setConf['price']['TIME']))} elif chrmt == 'ELEC': unit_price = {'unit': '度', 'value': round(setConf['price']['ELEC'] / 1000000.0, 2)} if unit_price: Device.get_collection().update_one(filter={'devNo': self.device.devNo}, update={'$set': { 'otherConf.unit_price': unit_price, 'otherConf.chrmt': chrmt, }}) Device.invalid_device_cache(self.device.devNo) def ack_event(self, orderNo, funCode): devInfo = MessageSender.send(self.device, DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE, {'IMEI': self._device['devNo'], 'data': {'fun_code': funCode, 'order_id': orderNo}}) self.check_feedback_result(devInfo) def clear_dev_feecount(self): devInfo = MessageSender.send(self.device, self.make_random_cmdcode(), {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x09, 'total_coin': True, 'total_card': True}}) self.check_feedback_result(devInfo) def response_card_charge_result(self, cardNo, result): MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SERVER_ASYNC, {'IMEI': self._device['devNo'], 'data': {'fun_code': 37, 'card_no': cardNo, 'result': result}}) def reboot_device(self): data = {'fun_code': 0x0B, 'reset_mcu': True} MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], 'data': data}) MessageSender.async_send(self.device, DeviceCmdCode.SET_DEVINFO, {'IMEI': self._device['devNo'], 'restart': True}) def set_device_function(self, request, lastSetConf): if request.POST.has_key('clearSum'): self.clear_dev_feecount() elif request.POST.has_key('reboot'): self.reboot_device() def set_device_function_param(self, request, lastSetConf): newConf = copy.deepcopy(request.POST) newConf.pop('logicalCode', None) if 'minAfterStartCoins' in newConf: minAfterStartCoins = round(float(newConf.get('minAfterStartCoins', 0.0)), 1) Device.get_collection().update_one(filter={'devNo': self.device.devNo}, update={'$set': {'otherConf.minAfterStartCoins': minAfterStartCoins}}) Device.invalid_device_cache(self.device.devNo) if 'refundProtection' in newConf: refundProtection = round(float(newConf.get('refundProtection', 0.0)), 1) Device.get_collection().update_one(filter={'devNo': self.device.devNo}, update={'$set': {'otherConf.refundProtection': refundProtection}}) Device.invalid_device_cache(self.device.devNo) self.set_dev_setting(newConf) def get_card_pwd(self): devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x11}}) self.check_feedback_result(devInfo) result = devInfo['data'] enObj = EncryptDate(cardKey) cardPwd = enObj.decrypt(result['card_pwd']) if not cardPwd: cardPwd = '' return {'card_pwd': cardPwd} def translante_card_no(self, hexCardNo): return int(hexCardNo, 16) def check_pwd(self, pwd): if len(pwd) != 6: raise ServiceException({'result': 2, 'description': u'密码必须是6位纯数字密码'}) for char in pwd: if not (char >= '0' and char <= '9'): raise ServiceException({'result': 2, 'description': u'密码必须是6位纯数字密码'}) return def set_card_pwd(self, pwd): self.check_pwd(pwd) enObj = EncryptDate(cardKey) devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x0B, 'card_pwd': enObj.encrypt(pwd)}}) self.check_feedback_result(devInfo) def set_card_mode(self, setConf): cardMode = int(setConf.get('card_mode')) valueDict = {'fun_code': 0x10} if cardMode == 0: valueDict.update({'mode': cardMode}) elif cardMode == 1: # 格式化为离线卡 self.check_pwd(setConf['new_pwd']) newPwd = setConf['new_pwd'] if not str(setConf['balance']).isdigit(): raise ServiceException({'result': 2, 'description': u'余额必须是数字'}) balance = int(setConf['balance']) if not (balance >= 0 and balance <= 5000): raise ServiceException({'result': 2, 'description': u'余额必须在0和5000元之间'}) balance = balance * 10 # 硬件模块记录的单位是角 enObj = EncryptDate(cardKey) valueDict.update({'mode': 1, 'new_pwd': enObj.encrypt(str(newPwd)), 'balance': balance}) elif cardMode == 2: self.check_pwd(setConf['old_pwd']) self.check_pwd(setConf['new_pwd']) newPwd = setConf['new_pwd'] oldPwd = setConf['old_pwd'] enObj = EncryptDate(cardKey) valueDict.update({'mode': 2, 'old_pwd': enObj.encrypt(str(oldPwd)), 'new_pwd': enObj.encrypt(str(newPwd))}) devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], 'data': valueDict}) self.check_feedback_result(devInfo) def get_card_mode(self): devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x11}}) self.check_feedback_result(devInfo) cardMode = devInfo['data']['card_mode'] return {'card_mode': cardMode} def check_alarm(self, alarm): if alarm.faultCode == FAULT_CODE.MCU_REBOOT: return u'此告警,建议您多观察,如果比较频繁不停上报单台设备的告警,可能会运行不稳定。需要您联系技术支持确认。' elif alarm.faultCode == FAULT_CODE.COUNTER_FAULT: portInfo = self.get_port_info(alarm.portNo) if portInfo['status'] == Const.DEV_WORK_STATUS_FAULT: return u'此端口目前已经为故障状态,继电器可能运行不稳定,建议您联系技术支持,以确认设备运行情况。' else: return u'端口状态检查正常,继电器或存偶尔无法获取数据。暂不影响使用。' elif alarm.faultCode == FAULT_CODE.RELAY_FAULT: return u'无法进行远程诊断,建议您到现场,直接插上插座,然后检查是否不用付款,就能够充电。如果是的,就属于继电器粘连。' elif alarm.faultCode == FAULT_CODE.DEV_OVERLOAD: return u'整机功率最大限定为7500瓦,接入的负载超过此负载,为了安全,将强行关闭所有充电端口。' elif alarm.faultCode == FAULT_CODE.COPY_CARD: return u'出现一模一样的卡,可能是用户复制了另外一张离线卡,然后使用,会给您造成经济上的损失,建议冻结。' return '' def get_part_info(self): # devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, # {'IMEI': self._device['devNo'], 'data': {'fun_code': 0x05}}) # self.check_feedback_result(devInfo) # # partInfo = devInfo['data'] # result = {} # nameDesc = {'networkBoard': u'网络板', 'chargeBoard': u'充电板', 'cardBoard': u'刷卡板'} # for k, v in partInfo.items(): # if k not in nameDesc: # continue # # result[nameDesc.get(k)] = {'SN': v} # # return result return {} def recharge_card(self, cardNo, money, orderNo=None): hex_cardNo = hex(int(cardNo))[2::].replace('L', '').upper() result = MessageSender.send(self.device, self.make_random_cmdcode(), {'IMEI': self.device['devNo'], 'data': {'fun_code': 37, 'result': 1, 'card_no': hex_cardNo, 'charge': int(money * 100), 'order_id': orderNo}}) # 返回验证 self.check_feedback_result(result) card = Card.objects.filter(cardNo=cardNo,dealerId=self.device.ownerId).first() if not card: return { 'result': ErrorCode.EXCEPTION, 'description': '' }, None balance = card.balance + money return { 'result': ErrorCode.SUCCESS, 'description': '' }, balance def recharge_card_async(self, cardNo, money, orderNo): cardNo = hex(int(cardNo))[2::].replace('L', '').upper() MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SERVER_ASYNC, {'IMEI': self._device['devNo'], 'data': {'fun_code': 37, 'result': 1, 'card_no': cardNo, 'charge': int(money * 100), 'order_id': orderNo}}) @property def isHaveStopEvent(self): return True # api相关 def apiStartDeviceForYtb(self, record): port = int(record['port']) packageId = record['packageId'] device = Device.objects(devNo=self.device['devNo']).first() package = device.washConfig[str(packageId)] coins = int(float(package['coins']) * 100) order_no = 'YTB' + str(uuid.uuid4()) needTime, power = None, None unit = package.get('unit', u'分钟') if unit == u'秒': if int(package['time']) < 60: raise ServiceException({'result': 2, 'description': u'套餐的最小时间不能小于60秒'}) needTime = int(float(package['time'])) data = {'fun_code': 0x07, 'order_id': order_no, 'coins': coins, 'port': port, 'time': needTime} elif unit == u'分钟': needTime = int(package['time']) * 60 data = {'fun_code': 0x07, 'order_id': order_no, 'coins': coins, 'port': port, 'time': needTime} elif unit == u'小时': needTime = int(float(package['time']) * 60 * 60) data = {'fun_code': 0x07, 'order_id': order_no, 'coins': coins, 'port': port, 'time': needTime} elif unit == u'天': needTime = int(float(package['time']) * 60 * 60 * 24) data = {'fun_code': 0x07, 'order_id': order_no, 'coins': coins, 'port': port, 'time': needTime} elif unit == u'度': power = int(float(package['time']) * 1000000) # 微度,需要乘于16个零 data = {'fun_code': 0x07, 'order_id': order_no, 'coins': coins, 'port': port, 'elec': power} else: needTime = int(package['time']) data = {'fun_code': 0x07, 'order_id': order_no, 'coins': coins, 'port': port, 'time': needTime} devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], 'data': data}, timeout=MQTT_TIMEOUT.START_DEVICE) self.check_feedback_result(devInfo) value = { str(port): { 'status': Const.DEV_WORK_STATUS_WORKING, 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'port': port, 'coins': coins, 'order_id': order_no, 'needTime': needTime, 'power': power, 'isAPI': True } } Device.update_dev_control_cache(self._device['devNo'], value) return devInfo def apiGetPortInfoFromYtb(self, record): return self.get_port_info(int(record['port'])) def apiGetPortStatusFromYtb(self, record): return self.get_port_status_from_dev() def apiStopChargingPortForYtb(self, record): self.stop_charging_port(int(record['port'])) return {} @property def show_pay_unit(self): """ 前台显示付费的时候,目前有不同的客户希望 显示不同的单位 有的显示金币 有的显示元, 这个地方处理下 :return: """ if self.device['otherConf'].get('pay_unit'): return self.device['otherConf'].get('pay_unit') return u"币" def get_port_using_detail(self, port, ctrInfo, isLazy=False): return self.get_port_info(port) def stop(self, port = None): return self.stop_charging_port(port) def get_customize_score_unit(self): return u'元' def get_customize_package_unit(self): chrmt = self.device['otherConf'].get('chrmt') if not chrmt: chrmt = self.get_dev_setting().get('chrmt') # 按时间计费 if chrmt == 'TIME' or chrmt == 'POWER': return u'分钟' # 按电量计费 elif chrmt == 'ELEC': return u'度' return u'元' def start_customize_point(self,pointNum,openId,port): unit = self.get_customize_package_unit() package = {'name':'customizePoint','price':pointNum,'coins':pointNum,'unit':unit,'time':999} attachParas = {'chargeIndex':port,'onPoints':True} return self.start_device(package, openId, attachParas)