# -*- coding: utf-8 -*- # !/usr/bin/env python import datetime import logging import random import time from typing import Optional, Dict, TYPE_CHECKING from apilib.monetary import RMB from apilib.utils_datetime import timestamp_to_dt from apps.web.agent.models import Agent from apps.web.constant import DeviceCmdCode, Const, MQTT_TIMEOUT, DeviceErrorCodeDesc, ErrorCode from apps.web.core.adapter.base import SmartBox, fill_2_hexByte, start_error_timer from apps.web.core.device_define.jndz import CMD_CODE from apps.web.core.exceptions import ServiceException, InvalidParameter from apps.web.core.networking import MessageSender from apps.web.dealer.models import Dealer from apps.web.device.models import Device, Group, DeviceType, GroupDict from apps.web.eventer.base import AckEvent from apps.web.user.models import ConsumeRecord from taskmanager.mediator import task_caller from apps.web.common.models import ExceptionLog if TYPE_CHECKING: from apps.web.api.models import APIStartDeviceRecord logger = logging.getLogger(__name__) class ChargingJNDZBox(SmartBox): """ 久恒协议和劲能一致,业务修改和功能扩充需要确定是否两边都修改 """ def __init__(self, device): super(ChargingJNDZBox, self).__init__(device) def async_update_portinfo_from_dev(self): """ 避免频繁刷新状态,并且是并发的。状态如果不对,用户可以强制刷新 :return: """ pass @property def isHaveStopEvent(self): return True def translate_funcode(self, funCode): funCodeDict = { '01': u'获取端口数量', '02': u'获取端口数据', '14': u'移动支付', '07': u'获取刷卡投币统计数据', '15': u'获取设备端口详情', '0F': u'获取端口状态', '0C': u'端口锁操作', '0D': u'端口开关', '1E': u'获取设备设置', '18': u'设置设备参数', '10': u'回复卡余额', } return funCodeDict.get(funCode, '') def translate_event_cmdcode(self, cmdCode): cmdDict = { '06': u'充电结束', '16': u'充电结束', '0A': u'故障', '10': u'刷卡上报', '20': u'启动设备', } return cmdDict.get(cmdCode, '') def check_dev_status(self, attachParas=None): """ 如果超过两个心跳周期没有报心跳,并且最后一次更新时间在2个小时内,需要从设备获取状态 否则以缓存状态为准。 :param attachParas: :return: """ if attachParas is None: raise ServiceException({'result': 0, 'description': u'请您选择合适的充电端口、电池类型信息'}) if 'chargeIndex' not in attachParas: 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)}) 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 port = str(attachParas['chargeIndex']) rv = self.get_port_info(port) # isCanAdd = self.device['devType'].get('payableWhileBusy', False) if rv['status'] == Const.DEV_WORK_STATUS_IDLE: return elif rv['status'] == Const.DEV_WORK_STATUS_FAULT: raise ServiceException({'result': 0, 'description': u'该端口故障,暂时不能使用'}) elif rv['status'] == Const.DEV_WORK_STATUS_WORKING: # if isCanAdd: # return # raise ServiceException({'result': 0, 'description': u'该端口正在工作不能使用,请您使用其他端口'}) return def is_port_can_use(self, port, canAdd = False): return True, '' def test(self, coins,port=1): hexElec = fill_2_hexByte(hex(0), 4) hexPort = fill_2_hexByte(hex(port), 2) hexCoins = fill_2_hexByte(hex(1)) hexTime = fill_2_hexByte(hex(60)) devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], "funCode": '14', 'data': hexPort + hexCoins + hexTime + hexElec}) return devInfo def stop(self, port = None): infoDict = self.stop_charging_port(port) infoDict['remainder_time'] = infoDict['leftTime'] return infoDict def check_serial_port_for_startcmd(self, port): data = fill_2_hexByte(hex(int(port)), 2) devInfo = MessageSender.send(self.device, self.make_random_cmdcode(), {'IMEI': self._device['devNo'], "funCode": '15', 'data': data}) if devInfo['rst'] != 0: raise ServiceException( {'result': 2, 'description': u'设备忙无响应。本次操作没有扣除您的金额,您可以稍后重试或者试试附近其他设备。'}) data = devInfo['data'][6::] ExceptionLog.log(user = self.device.devNo, exception = '', extra = { 'action': 'check_serial_port_for_startcmd:get_port_status_from_dev', 'data': data }) if data[0:2] == '01': # 表示成功 if data[2:4] == '00': raise ServiceException({ 'result': 2, 'description': u'设备忙无响应。本次操作没有扣除您的金额,您可以稍后重试或者试试附近其他设备。'}) else: left_time = int(data[4:8], 16) if left_time <= 0 or left_time == 65535: raise ServiceException({ 'result': 2, 'description': u'设备忙无响应。本次操作没有扣除您的金额,您可以稍后重试或者试试附近其他设备。'}) else: raise ServiceException({ 'result': 2, 'description': u'设备忙无响应。本次操作没有扣除您的金额,您可以稍后重试或者试试附近其他设备。'}) @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 'chargeIndex' not in attachParas: raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路'}) coinElec = self.device.get_other_conf_item('coinElec') if coinElec is None: conf = self.get_dev_setting_1E() coinElec = int(conf.get('coinElec', 10)) else: coinElec = int(coinElec) price = float(package['price']) port = hex(int(attachParas['chargeIndex'])) hexPort = fill_2_hexByte(port, 2) coins = float(package['coins']) hexCoins = fill_2_hexByte(hex(int(coins * 10))) # 注意单位是角 needElec = round((coins * coinElec) / 10.0, 2) # 单位是0.01度电(配置中每次投币的最大用电量单位却是0.1度电,这里要乘以10) hexElec = fill_2_hexByte(hex(int(needElec * 100)), 4) unit = package.get('unit', u'分钟') needTime = int(package['time']) if unit in [u'分钟', u'小时', u'天']: billingType = 'time' if unit == u'小时': needTime = int(package['time']) * 60 elif unit == u'天': needTime = int(package['time']) * 1440 hexTime = fill_2_hexByte(hex(needTime)) elif unit == u'度': billingType = 'elec' needElec = round((package['time']), 2) hexElec = fill_2_hexByte(hex(int(needElec * 100)), 4) hexTime = fill_2_hexByte(hex(Device.get_elec_static_time(self._device['devNo']))) else: billingType = 'elec' devObj = Device.objects.get(devNo=self.device.devNo) if 'isTempPackage' in attachParas: elecFee = float(devObj.otherConf.get('tempElecPrice', 0.9)) else: elecFee = float(devObj.otherConf.get('elecFee', 0.9)) needElec = round(min(float(coins / elecFee), needElec), 2) hexElec = fill_2_hexByte(hex(int(needElec * 100)), 4) hexTime = fill_2_hexByte(hex(Device.get_elec_static_time(self._device['devNo']))) devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, { 'IMEI': self._device['devNo'], "funCode": '14', 'data': hexPort + hexCoins + hexTime + hexElec }, timeout=MQTT_TIMEOUT.START_DEVICE) usePort = int(attachParas['chargeIndex']) if devInfo['rst'] != ErrorCode.SUCCESS: if devInfo['rst'] == ErrorCode.DEVICE_CONN_FAIL: raise ServiceException( { 'result': 2, 'description': u'当前设备信号弱无响应。本次操作没有扣除您的金额,您可以稍后重试或者试试附近其他设备。' } ) elif devInfo['rst'] == ErrorCode.BOARD_UART_TIMEOUT: if devInfo.get('checked', 0) == 0: self.check_serial_port_for_startcmd(attachParas['chargeIndex']) else: logger.debug('{} checked value is: {}'.format(self.device, devInfo.get('checked'))) raise ServiceException( {'result': 2, 'description': u'设备忙无响应。本次操作没有扣除您的金额,您可以稍后重试或者试试附近其他设备。'}) elif devInfo.get('checked', 0) == 0: logger.debug('{} apply start success.'.format(self.device)) data = devInfo['data'][6::] if data[0:2] == '01': # 表示成功 pass else: raise ServiceException({'result': 2, 'description': u'获取端口数据失败,请重试看能否解决'}) result = data[4:6] if result == '01': # 成功 pass elif result == '0B': newValue = {str(usePort): {'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 == '0C': newValue = {str(usePort): {'status': Const.DEV_WORK_STATUS_WORKING, 'statusInfo': u''}} Device.update_dev_control_cache(self._device['devNo'], newValue) raise ServiceException({'result': 2, 'description': u'该端口正在使用中'}) else: logger.warning('{} check 15 result is: {}.'.format(self.device, devInfo.get('checked'))) start_timestamp = int(time.time()) portDict = { 'startTime': timestamp_to_dt(start_timestamp).strftime('%Y-%m-%d %H:%M:%S'), 'status': Const.DEV_WORK_STATUS_WORKING, 'coins': coins, 'isStart': True, 'price': price, 'openId': openId, 'refunded': False, 'needElec': needElec, 'billingType': billingType, 'vCardId': self._vcard_id, 'consumeType': 'server' } lastPortInfo = Device.get_port_control_cache(self.device.devNo, str(usePort)) if lastPortInfo and \ lastPortInfo.get('status', '') == Const.DEV_WORK_STATUS_WORKING and \ lastPortInfo.get('openId') == openId and \ lastPortInfo.get('billingType') == billingType and \ ('consumeType' not in lastPortInfo or (lastPortInfo['consumeType'] == 'server')): is_continus = True else: is_continus = False if is_continus: if 'coins' in lastPortInfo: portDict['coins'] = coins + lastPortInfo['coins'] if 'price' in lastPortInfo: portDict['price'] = price + lastPortInfo['price'] if 'startTime' in lastPortInfo: portDict['startTime'] = lastPortInfo['startTime'] if billingType == 'time': portDict.update({'needTime': needTime, 'needElec': needElec}) if is_continus: if lastPortInfo.has_key('needTime'): portDict['needTime'] = needTime + lastPortInfo['needTime'] if lastPortInfo.has_key('needElec'): portDict['needElec'] = needElec + lastPortInfo['needElec'] finishedTime = int(time.time()) + portDict['needTime'] * 60 else: portDict.update({'needElec': needElec}) if is_continus: lastPortInfo['needElec'] = needElec + lastPortInfo['needElec'] finishedTime = int(time.time()) + 12 * 60 * 60 if attachParas.has_key('orderNo'): portDict.update({'orderNo': attachParas['orderNo']}) if attachParas.get('redpackId'): if is_continus: redpackInfo = lastPortInfo.get("redpackInfo", list()) if not redpackInfo: logger.warning("miss redpackInfo! {}-{}".format(self._device["devNo"], usePort)) 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): item = { 'rechargeRcdId': str(attachParas['linkedRechargeRecordId']) } if is_continus: pay_info = lastPortInfo.get('payInfo', list()) else: pay_info = list() pay_info.append(item) portDict['payInfo'] = pay_info if 'extOrderNo' in attachParas: portDict['extOrderNo'] = attachParas.get('extOrderNo') if 'isTempPackage' in attachParas: portDict['isTempPackage'] = attachParas['isTempPackage'] devInfo['finishedTime'] = finishedTime portDict.update({'finishedTime': finishedTime}) Device.update_dev_control_cache(self.device.devNo, {str(usePort): portDict}) if self.device.owner.supports("supportBeiJingFengTai"): from apps.web.south_intf.bj_north.api import push_port_info,post_charging_meta_info push_port_info(self.device.devNo, attachParas['chargeIndex']) post_charging_meta_info(self.device.devNo, attachParas['chargeIndex']) return devInfo def start_from_api(self, record): # type: (APIStartDeviceRecord)->Optional[Dict] if 'chargeIndex' not in record.attachParas: from apps.web.api.exceptions import ApiParameterError raise ApiParameterError(u'请传入充电桩端口号chargeIndex参数') return super(ChargingJNDZBox, self).start_from_api(record) def analyze_event_data(self, device_event): # 升级兼容. 避免重启过程中老的消息 if 'data' in device_event: data = device_event['data'] else: data = device_event cmdCode = data[4:6] #: 提交充电结束状态 (06) #: 老版本 if cmdCode == CMD_CODE.DEVICE_SUBMIT_CHARGE_FINISHED_06: port = int(data[8:10], 16) leftTime = int(data[10:14], 16) reasonCode = data[14:16] orderPower = device_event.get("orderPower") desc_map = { "00": u'购买的充电时间或电量用完了。', "01": u'可能是插头被拔掉,或者电瓶已经充满。系统判断为异常断电,由于电瓶车充电器种类繁多,可能存在误差。如有问题,请您及时联系商家协助解决问题并恢复充电。', "02": u'恭喜您!电池已经充满电!', "0B": u'设备或端口出现问题,为了安全起见,被迫停止工作。建议您根据已经充电的时间评估是否需要到现场换到其他端口充电。' } return { 'status': Const.DEV_WORK_STATUS_IDLE, 'cmdCode': cmdCode, 'port': port, 'leftTime': leftTime, 'reason': desc_map[reasonCode], 'reasonCode': reasonCode, 'uartData': data, 'orderPower': orderPower } #: (高版本的) 提交充电结束状态 (16) elif cmdCode == CMD_CODE.DEVICE_SUBMIT_CHARGE_FINISHED_v2_16: port = int(data[8:10], 16) leftTime = int(data[10:14], 16) elec = int(data[14:18], 16) / 100.0 reasonCode = data[18:20] orderPower = device_event.get("orderPower") # 如果设备类型有自定义结束推送通知就走自定义 devType = DeviceType.objects(id=self.device['devType']['id']).first() finishedReasonDict = devType.finishedReasonDict if finishedReasonDict != {}: desc_map = finishedReasonDict else: desc_map = { '00': u'购买的充电时间或电量用完了。', '01': u'可能是插头被拔掉,或者电瓶已经充满。', '02': u'恭喜您!电池已经充满电!', '03': u'警告!您的充电功率超过充电桩的最大使用功率,已经停止充电!!', '04': u'远程断电。', '0B': u'设备或端口出现问题,被迫停止工作。建议您根据已经充电的时间评估是否需要到现场换到其他端口充电。' } desc = desc_map.get(reasonCode, u'电池没有充满!原因未知。') return { 'status': Const.DEV_WORK_STATUS_IDLE, 'cmdCode': cmdCode, 'port': port, 'leftTime': leftTime, 'elec': elec, 'reason': desc, 'reasonCode': reasonCode, 'uartData': data, 'orderPower': orderPower } # todo 0A的告警不要了,先注释掉,后续删除 #: 上传设备故障 # elif cmdCode == CMD_CODE.DEVICE_SUBMIT_DEVICE_FAULT_0A: # port = int(data[8:10], 16) # errCode = data[10:12] # # FaultDict = { # 'A0': ['充电异常,继电器粘连,短路', FAULT_LEVEL.FATAL], # 'A1': ['高温异常', FAULT_LEVEL.CRITICAL], # '21': ['高温状态恢复', FAULT_LEVEL.NORMAL], # 'A2': ['低温异常', FAULT_LEVEL.NORMAL], # '22': ['低温状态恢复', FAULT_LEVEL.NORMAL], # 'A3': ['空载,充电头脱落、充电器拔出', FAULT_LEVEL.NORMAL], # '23': ['负载恢复正常', FAULT_LEVEL.NORMAL], # 'A4': ['设备消防(烟感)异常', FAULT_LEVEL.CRITICAL], # '24': ['设备消防(烟感)恢复正常', FAULT_LEVEL.CRITICAL], # 'A5': ['总功率过载' if port == 255 else '{}号端口过载'.format(port), FAULT_LEVEL.FATAL], # '25': ['总功率过载恢复正常' if port == 255 else '{}号端口过载恢复正常'.format(port), FAULT_LEVEL.NORMAL], # 'A6': ['设备倾斜异常', FAULT_LEVEL.FATAL], # '26': ['设备倾斜恢复正常', FAULT_LEVEL.NORMAL], # 'A7': ['水压高异常', FAULT_LEVEL.NORMAL], # '27': ['水压(从高)恢复正常', FAULT_LEVEL.NORMAL], # 'A8': ['水压低异常', FAULT_LEVEL.NORMAL], # '28': ['水压(从低)恢复正常', FAULT_LEVEL.NORMAL], # 'A9': ['设备过压异常', FAULT_LEVEL.NORMAL], # '29': ['设备过压恢复正常', FAULT_LEVEL.NORMAL], # 'AA': ['设备欠压异常', FAULT_LEVEL.NORMAL], # '2A': ['设备欠压恢复正常', FAULT_LEVEL.NORMAL], # # '00': ['继电器粘黏(端口: {})'.format(port), FAULT_LEVEL.NORMAL], # 久恒老设备 100206 # } # # return { # 'status': Const.DEV_WORK_STATUS_FAULT, # 'statusInfo': FaultDict.get(errCode, ['设备故障', FAULT_LEVEL.NORMAL])[0], # 'cmdCode': cmdCode, # 'port': port, # 'errCode': errCode, # 'faultContent': FaultDict.get(errCode, [errCode, FAULT_LEVEL.NORMAL])[0], # 'level': FaultDict.get(errCode, [errCode, FAULT_LEVEL.NORMAL])[1] # } # 用户刷卡上报的信息 #: 在线卡上传卡号,预扣费 #: 示例: #: CARD_ID CARD_CST CARD_OPE #: 0x00000000 0x00 0x00 #: 参数 #: CARD_ID : IC 卡卡号 #: CARD_SURP: 卡片需要改变的金额信息(以角为单位) #: CARD_OPE: 0x00 是扣费(减少),0x01 是充值(增加)。 #: 回复 #: RES: 0x00,表示扣费成功,0x01 表示余额不足,0x02 表示非法卡。 #: CARD_SURP: 表示卡余额(以角为单位)。 elif cmdCode == CMD_CODE.SWIPE_CARD_10: # 过滤掉长度不够的10命令 if data[2:4] == '04': return None group = Group.get_group(self._device['groupId']) dealer = Dealer.get_dealer(group['ownerId']) agent = Agent.objects(id = dealer['agentId']).first() device = Device.objects(devNo=self._device['devNo']).first() devType = DeviceType.objects(id=device['devType']['id']).first() cardNo = str(int(data[8:16], 16)) # 兼容以前的老代码, 先走以前的老代码, 如果有新代码再走新的. if agent is not None and 'cardNoReverse' in agent.features: cardData = data[14:16] + data[12:14] + data[10:12] + data[8:10] cardNo = str(int(cardData, 16)) if 'cardNoReverse' in devType.features: if devType.features['cardNoReverse'] is True: cardData = data[14:16] + data[12:14] + data[10:12] + data[8:10] cardNo = str(int(cardData, 16)) else: cardNo = str(int(data[8:16], 16)) else: pass #: 卡里的余额,以角为单位 preFee = int(data[16:18], 16) / 10.0 #: 操作符 00(增加) | 01(减少) oper = data[18:20] return {'cardNo': cardNo, 'preFee': preFee, 'cmdCode': cmdCode, 'oper': oper, "sourceData": data} elif cmdCode == CMD_CODE.SWIPE_CARD_50: data = data[8:-2] cardNo = str(int(data, 16)) return { 'cardNo' : cardNo, 'command' : 'query', 'cmdCode': cmdCode } elif cmdCode == CMD_CODE.SWIPE_CARD_52: data = data[8:-2] port = str(int(data[:2], 16)) coins = str(int(data[2:6], 16) * 0.1) needtime = str(int(data[6:10], 16)) cardNo = str(int(data[10:18], 16)) deductType = str(int(data[18:20], 16)) ackId = device_event['ack_id'] return {'port': port, 'coins': coins, 'needTime': needtime, 'cardNo': cardNo, 'deductType': deductType, 'cmdCode': cmdCode, 'command': 'strat','ack_id':ackId} elif cmdCode == CMD_CODE.SWIPE_CARD_56: data = data[8:-2] port = str(int(data[:2], 16)) usedTime = str(int(data[2:6], 16)) usedElec = str(int(data[6:10], 16)) reasonCode = str(int(data[10:12], 16)) refundMoney = str(int(data[12:16], 16)) refundTime = str(int(data[16:20], 16)) refundType = str(int(data[20:22], 16)) desc_map = { '00': u'购买的充电时间或电量用完了。', '01': u'可能是插头被拔掉,或者电瓶已经充满。', '02': u'恭喜您!电池已经充满电!', '03': u'警告!您的充电功率超过充电桩的最大使用功率,已经停止充电!!', '04': u'远程断电。', '0B': u'设备或端口出现问题,被迫停止工作。建议您根据已经充电的时间评估是否需要到现场换到其他端口充电。' } desc = desc_map.get(reasonCode, u'电池没有充满!原因未知。') ackId = device_event['ack_id'] return {'port': port, 'usedTime': usedTime, 'usedElec': usedElec, 'reason': desc, 'reasonCode': reasonCode, 'refundMoney': refundMoney, 'refundTime': refundTime, 'refundType': refundType, 'cmdCode': cmdCode, 'command': 'finished','ack_id':ackId} #: 上报投币打开的信息 elif cmdCode == CMD_CODE.DEVICE_SUBMIT_OFFLINE_COINS_20: # 启动设备 port = int(data[8:10], 16) needTime = int(data[10:14], 16) elec = int(data[14:18], 16) / 100.0 consumeTypeTemp = data[18:20] if consumeTypeTemp == '00': consumeType = 'coin' elif consumeTypeTemp == '01': consumeType = 'card' elif consumeTypeTemp == '03': consumeType = 'server' else: consumeType = 'other' money = int(data[20:22], 16) / 10.0 rv = { 'cmdCode': cmdCode, 'port': port, 'needTime': needTime, 'elec': elec, 'consumeType': consumeType, 'coins': money, 'uartData': data } if 'today_coins' in device_event and 'ts' in device_event: rv.update({ 'today_coins': device_event['today_coins'], 'ts': device_event['ts'] }) return rv # 4个故障的告警 elif cmdCode == CMD_CODE.DEVICE_FAULT_FIRE: return {"cmdCode": cmdCode, "fault": u"火灾报警"} elif cmdCode == CMD_CODE.DEVICE_FAULT_SMOKE: return {"cmdCode": cmdCode, "fault": "烟雾报警"} elif cmdCode == CMD_CODE.DEVICE_FAULT_TEMPERATURE: return {"cmdCode": cmdCode, "fault": "温度超限告警"} elif cmdCode == CMD_CODE.DEVICE_FAULT_POWER: return {"cmdCode": cmdCode, "fault": "功率超线告警"} elif cmdCode == CMD_CODE.DEVICE_FAULT_ALTER: def getBin8Str(binData): bin8Str = str(bin(binData))[2:] while len(bin8Str) < 8: bin8Str = '0' + bin8Str return bin8Str fire = int(data[8:10],16) smoke = int(data[10:12],16) overload = int(data[12:14],16) overheat = int(data[14:16],16) relayAdhesion = data[16:24] relayAdhesionDetail = (getBin8Str(int(relayAdhesion[0:2], 16)) + getBin8Str(int(relayAdhesion[2:4], 16)) + getBin8Str(int(relayAdhesion[4:6], 16)) + getBin8Str(int(relayAdhesion[6:8], 16)))[::-1] portRelayAdhesion = {} for _ in range(0, len(relayAdhesionDetail)): if int(relayAdhesionDetail[_]): portRelayAdhesion.update({str(_ + 1): int(relayAdhesionDetail[_])}) result = {"cmdCode": cmdCode, "fire": bool(fire), "smoke": bool(smoke), "overload": bool(overload), "overheat":bool(overheat), "portRelayAdhesion": portRelayAdhesion} dealer = Dealer.objects(id=self.device['ownerId']).first() if dealer is None: return {} if 'supportAdhesionAlert' not in dealer.features: result.pop('portRelayAdhesion') fault = [] desc = [] if result.get('fire', False): fault.append("火灾报警") result["errorCode"] = "B202" if result.get('smoke', False): fault.append("烟雾报警") result["errorCode"] = "B202" if result.get('overload', False): fault.append("功率超限告警") result["errorCode"] = "B203" if result.get('overheat', False): fault.append("温度超限告警") result["errorCode"] = "B201" if result.get('portRelayAdhesion', {}): fault.append("继电器粘连告警") result["errorCode"] = "B204" for _ in portRelayAdhesion.keys(): desc.append("{}号继电器粘连告警 ".format(_)) if desc: desc = '--'.join(desc) result.update({"desc": desc}) if fault: fault = '--'.join(fault) result.update({"fault": fault}) else: result = {} return result # 2个状态量上报 elif cmdCode == CMD_CODE.DEVICE_FAULT_POWER: maxPower = int(data[8:12],16) return {"cmdCode": cmdCode, "maxPower": maxPower} elif cmdCode == CMD_CODE.DEVICE_FAULT_TEMPERATURE: maxTemperature = int(data[8:10],16) return {"cmdCode": cmdCode, "maxTemperature": maxTemperature} elif cmdCode == CMD_CODE.DEVICE_ELEC: elec = int(data[8:12], 16) return {"cmdCode": cmdCode, "elec": elec} elif cmdCode == CMD_CODE.DEVICE_TEMPERATURE: temperature = int(data[10:12], 16) if data[8:10] == "00": temperature = temperature * -1 return {"cmdCode": cmdCode, "temperature": temperature} elif cmdCode == CMD_CODE.DEVICE_CARD_CHARGE_2D: _result = data[6: 8] _port = str(int(data[8: 10], 16)) _time = int(data[10: 14], 16) _elec = int(data[14: 18], 16) / 100.0 _type = data[18: 20] _coins = int(data[20: 22], 16) _cardNo = str(int(data[22: 30], 16)) _card_cst = int(data[30: 34], 16) / 10.0 _operation = data[34: 36] _card_type = data[36: 38] _session_id = data[38: 46] return { "portStr": _port, "time": _time, "elec": _elec, "chargeType": _type, "coins": _coins, "cardNo": _cardNo, "cardCst": _card_cst, "operation": _operation, "cardType": _card_type, "sessionId": _session_id, "cmdCode": cmdCode, "result": _result, "sourceData": data } elif cmdCode == CMD_CODE.DEVICE_REAL_TIME_REPORT_21: _result = data[6: 8] port_num = int(data[8:10],16) port_info_urat_info = data[10:170] port_status_desc = { "01": "端口空闲", "02": "端口正在使用", "03": "端口禁用", "04": "端口故障", } port_info = {} for index in xrange(0, 160, 16): item = port_info_urat_info[index: index + 16] one_port = {} one_port["port"] = int(item[:2], 16) one_port["portStatus"] = item[2:4] one_port["portStatusDesc"] = port_status_desc.get(item[2:4]) one_port["leftTime"] = int(item[4:8], 16) one_port["power"] = int(item[8:12], 16) one_port["elec"] = int(item[12:16], 16) * 0.01 port_info[str(one_port["port"])] = one_port return {"cmdCode": cmdCode, "result": _result, "sourceData": data, "portNum": port_num, "portInfo": port_info} else: logger.info("receive data <{}>, cmd is invalid".format(data)) def get_dev_consume_count(self): devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], "funCode": '07', 'data': '00'}) 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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) data = devInfo['data'][6::] if data[0:2] == '01': # 表示成功 pass else: raise ServiceException({'result': 2, 'description': u'获取统计数据失败,请重试看能否解决'}) cardFee = str(RMB(int(data[2:6], 16) / 10.0)) # 以角为单位 coinFee = int(data[6:10], 16) # 以元为单位 return {'cardFee': cardFee, 'coinFee': coinFee} def get_port_info(self, line): data = fill_2_hexByte(hex(int(line)), 2) devInfo = MessageSender.send(self.device, self.make_random_cmdcode(), {"funCode": '15', 'data': data}) 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'])}) data = devInfo['data'][6::] if data[0:2] == '01': # 表示成功 pass else: raise ServiceException({'result': 2, 'description': u'获取端口信息失败,请重试看能否解决'}) if data[2:4] == '00': status = Const.DEV_WORK_STATUS_IDLE else: left_time = int(data[4:8], 16) if left_time <= 0 or left_time == 65535: status = Const.DEV_WORK_STATUS_IDLE else: status = Const.DEV_WORK_STATUS_WORKING # 这里存在多线程更新缓存的场景,可能性比较低,但是必须先取出来,然后逐个更新状态,然后再记录缓存 ctrInfo = Device.get_dev_control_cache(self.device.devNo) old_status = ctrInfo.get(str(line), {}).get('status', Const.DEV_WORK_STATUS_IDLE) if old_status != status: if str(line) in ctrInfo: ctrInfo[str(line)]['status'] = status else: ctrInfo[str(line)] = {'status': status} Device.update_dev_control_cache(self.device.devNo, ctrInfo) leftTime = int(data[4:8], 16) if data[8:12] == 'FFFF': power = 0 else: power = int(data[8:12], 16) if data[12:16] == 'FFFF': elec = 0 else: elec = int(data[12:16], 16) * 0.01 if data[16:20] == 'FFFF': surp = 0 else: surp = int(data[16:20], 16) rv = {'port': line, 'leftTime': leftTime, 'power': power, 'surp': surp, 'leftElec': elec, 'status': status} if self.device.my_obj.bill_as_service_feature.on: rv.pop('leftTime', None) return rv 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']) statusDict = {} if 'allPorts' not in ctrInfo: self.get_port_status_from_dev() ctrInfo = Device.get_dev_control_cache(self._device['devNo']) allPorts = ctrInfo.get('allPorts', 10) # allPorts 有的时候会为0, 这个时候强制设置为10 if allPorts == 0: allPorts = 10 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 get_port_status_from_dev(self): devInfo = MessageSender.send(self.device, self.make_random_cmdcode(), {'IMEI': self._device['devNo'], "funCode": '0F', 'data': '00'}, timeout=MQTT_TIMEOUT.CHECK_DEVICE_STATUS) 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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) data = devInfo['data'][6::] if data[0:2] == '01': # 表示成功 pass else: raise ServiceException({'result': 2, 'description': u'获取统计数据失败,请重试看能否解决'}) result = {} portNum = int(data[2:4], 16) portData = data[4::] ii = 0 while ii < portNum: port = int(portData[ii * 4:ii * 4 + 2], 16) statusTemp = portData[ii * 4 + 2:ii * 4 + 4] if statusTemp == '01': status = {'status': Const.DEV_WORK_STATUS_IDLE} elif statusTemp == '02': status = {'status': Const.DEV_WORK_STATUS_WORKING} elif statusTemp == '03': status = {'status': Const.DEV_WORK_STATUS_FORBIDDEN} elif statusTemp == '04': status = {'status': Const.DEV_WORK_STATUS_FAULT} else: status = {'status': Const.DEV_WORK_STATUS_IDLE} ii += 1 result[str(port)] = status 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 lock_unlock_port(self, port, lock = True): lockStr = '00' if lock else '01' portStr = fill_2_hexByte(hex(int(port)), 2) devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], "funCode": '0C', 'data': portStr + lockStr}) 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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) data = devInfo['data'][6::] if data[0:2] == '01': # 表示成功 pass else: 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 active_deactive_port(self, port, active): if not active: 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, 'endTime': datetime.datetime.now().strftime(Const.DATETIME_FMT)}) newValue = {str(port): portCtrInfo} Device.update_dev_control_cache(self._device['devNo'], newValue) else: raise ServiceException({'result': 2, 'description': u'此设备不支持直接打开端口'}) def stop_charging_port(self, port): portStr = fill_2_hexByte(hex(int(port)), 2) devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], "funCode": '0D', 'data': portStr + '00'}, timeout=7) 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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) data = devInfo['data'][6::] if data[0:2] == '01': # 表示成功 pass else: raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'}) port = int(data[2:4], 16) leftTime = int(data[4:8], 16) result = {'port': port, 'leftTime': leftTime} return result def get_dev_setting_1E(self): devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], "funCode": '1E', 'data': '00'}) 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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) data = devInfo['data'][6::] if data[0:2] == '01': # 表示成功 pass else: raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'}) if len(data) > 62: confData = data[2:-2] coinMin = int(confData[0:4], 16) cardMin = int(confData[4:8], 16) coinElec = int(confData[8:10], 16) cardElec = int(confData[10:12], 16) cst = int(confData[12:14], 16) powerMax1 = int(confData[14:18], 16) powerMax2 = int(confData[18:22], 16) powerMax3 = int(confData[22:26], 16) powerMax4 = int(confData[26:30], 16) powerMax5 = int(confData[30:34], 16) powerMax6 = int(confData[34:38], 16) powerMax7 = int(confData[38:42], 16) powerMax8 = int(confData[42:46], 16) power2Ti = int(confData[46:48], 16) power3Ti = int(confData[48:50], 16) power4Ti = int(confData[50:52], 16) power5Ti = int(confData[52:54], 16) power6Ti = int(confData[54:56], 16) power7Ti = int(confData[56:58], 16) power8Ti = int(confData[58:60], 16) spRecMon = int(confData[60:62], 16) spFullEmpty = int(confData[62:64], 16) fullPowerMin = int(confData[64:66], 16) fullChargeTime = int(confData[66:68], 16) elecTimeFirst = int(confData[68:70], 16) firstCoinTime = 0 secondCoinTime = 0 thirdCoinTime = 0 resultDict = { 'coinMin': coinMin, 'cardMin': cardMin, 'coinElec': coinElec, 'cardElec': cardElec, 'cst': cst, 'powerMax1': powerMax1, 'powerMax2': powerMax2, 'powerMax3': powerMax3, 'powerMax4': powerMax4, 'powerMax5': powerMax5, 'powerMax6': powerMax6, 'powerMax7': powerMax7, 'powerMax8': powerMax8, 'power2Ti': power2Ti, 'power3Ti': power3Ti, 'power4Ti': power4Ti, 'power5Ti': power5Ti, 'power6Ti': power6Ti, 'power7Ti': power7Ti, 'power8Ti': power8Ti, 'spRecMon': spRecMon, 'spFullEmpty': spFullEmpty, 'fullPowerMin': fullPowerMin, 'fullChargeTime': fullChargeTime, 'elecTimeFirst': elecTimeFirst, 'firstCoinTime': firstCoinTime, 'secondCoinTime': secondCoinTime, 'thirdCoinTime': thirdCoinTime } resultDict.update({'support8PowerTi': True}) else: confData = data[2:-2] coinMin = int(confData[0:4], 16) cardMin = int(confData[4:8], 16) coinElec = int(confData[8:10], 16) cardElec = int(confData[10:12], 16) cst = int(confData[12:14], 16) powerMax1 = int(confData[14:18], 16) powerMax2 = int(confData[18:22], 16) powerMax3 = int(confData[22:26], 16) powerMax4 = int(confData[26:30], 16) power2Ti = int(confData[30:32], 16) power3Ti = int(confData[32:34], 16) power4Ti = int(confData[34:36], 16) spRecMon = int(confData[36:38], 16) spFullEmpty = int(confData[38:40], 16) fullPowerMin = int(confData[40:42], 16) fullChargeTime = int(confData[42:44], 16) elecTimeFirst = int(confData[44:46], 16) try: firstCoinTime = int(confData[46:50], 16) secondCoinTime = int(confData[50:54], 16) thirdCoinTime = int(confData[54:58], 16) except Exception as e: firstCoinTime = 0 secondCoinTime = 0 thirdCoinTime = 0 resultDict = { 'coinMin': coinMin, 'cardMin': cardMin, 'coinElec': coinElec, 'cardElec': cardElec, 'cst': cst, 'powerMax1': powerMax1, 'powerMax2': powerMax2, 'powerMax3': powerMax3, 'powerMax4': powerMax4, 'power2Ti': power2Ti, 'power3Ti': power3Ti, 'power4Ti': power4Ti, 'spRecMon': spRecMon, 'spFullEmpty': spFullEmpty, 'fullPowerMin': fullPowerMin, 'fullChargeTime': fullChargeTime, 'elecTimeFirst': elecTimeFirst, 'firstCoinTime': firstCoinTime, 'secondCoinTime': secondCoinTime, 'thirdCoinTime': thirdCoinTime } try: self.device.update_other_conf( cst = cst, coinMin = coinMin, coinElec = coinElec, cardMin = cardMin, cardElec = cardElec) except Exception, e: pass return resultDict def get_dev_setting(self): """ 获取设备配置参数 :return: """ resultDict = self.get_dev_setting_1E() consumeInfo = self.get_dev_consume_count() resultDict.update(consumeInfo) deviceInfo = self.get_real_time_device_info() resultDict.update(deviceInfo) return resultDict def set_shenlin_8_gear_settings(self, setConf): data = '' data += fill_2_hexByte(hex(int(setConf['coinMin'])), 4) data += fill_2_hexByte(hex(int(setConf['cardMin'])), 4) data += fill_2_hexByte(hex(int(setConf['coinElec'])), 2) data += fill_2_hexByte(hex(int(setConf['cardElec'])), 2) data += fill_2_hexByte(hex(int(setConf['cst'])), 2) data += fill_2_hexByte(hex(int(setConf['powerMax1'])), 4) data += fill_2_hexByte(hex(int(setConf['powerMax2'])), 4) data += fill_2_hexByte(hex(int(setConf['powerMax3'])), 4) data += fill_2_hexByte(hex(int(setConf['powerMax4'])), 4) data += fill_2_hexByte(hex(int(setConf['powerMax5'])), 4) data += fill_2_hexByte(hex(int(setConf['powerMax6'])), 4) data += fill_2_hexByte(hex(int(setConf['powerMax7'])), 4) data += fill_2_hexByte(hex(int(setConf['powerMax8'])), 4) data += fill_2_hexByte(hex(int(setConf['power2Ti'])), 2) data += fill_2_hexByte(hex(int(setConf['power3Ti'])), 2) data += fill_2_hexByte(hex(int(setConf['power4Ti'])), 2) data += fill_2_hexByte(hex(int(setConf['power5Ti'])), 2) data += fill_2_hexByte(hex(int(setConf['power6Ti'])), 2) data += fill_2_hexByte(hex(int(setConf['power7Ti'])), 2) data += fill_2_hexByte(hex(int(setConf['power8Ti'])), 2) data += fill_2_hexByte(hex(int(setConf['spRecMon'])), 2) data += fill_2_hexByte(hex(int(setConf['spFullEmpty'])), 2) data += fill_2_hexByte(hex(int(setConf['fullPowerMin'])), 2) data += fill_2_hexByte(hex(int(setConf['fullChargeTime'])), 2) data += fill_2_hexByte(hex(int(setConf['elecTimeFirst'])), 2) devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], "funCode": '18', 'data': data}) 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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) data = devInfo['data'][6::] if data[0:2] == '01': # 表示成功 self.device.update_other_conf( cst=int(setConf['cst']), coinMin=int(setConf['coinMin']), coinElec=int(setConf['coinElec']), cardMin=int(setConf['cardMin']), cardElec=int(setConf['cardElec'])) else: raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'}) def set_dev_setting(self, setConf): """ 设置设备配置参数 :param setConf: :return: """ data = '' data += fill_2_hexByte(hex(int(setConf['coinMin'])), 4) data += fill_2_hexByte(hex(int(setConf['cardMin'])), 4) data += fill_2_hexByte(hex(int(setConf['coinElec'])), 2) data += fill_2_hexByte(hex(int(setConf['cardElec'])), 2) data += fill_2_hexByte(hex(int(setConf['cst'])), 2) data += fill_2_hexByte(hex(int(setConf['powerMax1'])), 4) data += fill_2_hexByte(hex(int(setConf['powerMax2'])), 4) data += fill_2_hexByte(hex(int(setConf['powerMax3'])), 4) data += fill_2_hexByte(hex(int(setConf['powerMax4'])), 4) data += fill_2_hexByte(hex(int(setConf['power2Ti'])), 2) data += fill_2_hexByte(hex(int(setConf['power3Ti'])), 2) data += fill_2_hexByte(hex(int(setConf['power4Ti'])), 2) data += fill_2_hexByte(hex(int(setConf['spRecMon'])), 2) data += fill_2_hexByte(hex(int(setConf['spFullEmpty'])), 2) data += fill_2_hexByte(hex(int(setConf['fullPowerMin'])), 2) data += fill_2_hexByte(hex(int(setConf['fullChargeTime'])), 2) data += fill_2_hexByte(hex(int(setConf['elecTimeFirst'])), 2) if self.device['devType']['code'] == Const.DEVICE_TYPE_CODE_CHARGING_JH_TOUBI: data += fill_2_hexByte(hex(int(setConf['firstCoinTime'])), 4) data += fill_2_hexByte(hex(int(setConf['secondCoinTime'])), 4) data += fill_2_hexByte(hex(int(setConf['thirdCoinTime'])), 4) devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], "funCode": '18', 'data': data}) 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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) data = devInfo['data'][6::] if data[0:2] == '01': # 表示成功 self.device.update_other_conf( cst=int(setConf['cst']), coinMin=int(setConf['coinMin']), coinElec=int(setConf['coinElec']), cardMin=int(setConf['cardMin']), cardElec=int(setConf['cardElec'])) else: raise ServiceException({'result': 2, 'description': u'操作端口失败,请重试看能否解决'}) def response_use_card(self, res, balance, uartId = None): logger.info("ready to send response to dev <{}> funcode 10".format(self.device.devNo)) if not uartId: data = '55061001' data = data + res + fill_2_hexByte(hex(int(balance * 10)), 4) devInfo = MessageSender.send(self.device, DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE, {'IMEI': self._device['devNo'], "funCode": '10', 'data': data}) if devInfo.has_key('rst') and devInfo['rst'] != 0: if devInfo['rst'] == -1: raise ServiceException( {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'}) elif devInfo['rst'] == 1: # 等于1的时候,说明服务器和远程模块通讯OK,响应已经收到,不需要异常处理 pass else: data = '55061001' data = data + res + fill_2_hexByte(hex(int(balance * 10)), 4) MessageSender.send( self.device, DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE, { "IMEI": self.device.devNo, "funCode": '10', "data": data, "uartId": int(uartId) } ) def set_device_function_param(self, request, lastSetConf): coinMin = request.POST.get('coinMin', None) cardMin = request.POST.get('cardMin', None) coinElec = request.POST.get('coinElec', None) cardElec = request.POST.get('cardElec', None) cst = request.POST.get('cst', None) powerMax1 = request.POST.get('powerMax1', None) powerMax2 = request.POST.get('powerMax2', None) powerMax3 = request.POST.get('powerMax3', None) powerMax4 = request.POST.get('powerMax4', None) power2Ti = request.POST.get('power2Ti', None) power3Ti = request.POST.get('power3Ti', None) power4Ti = request.POST.get('power4Ti', None) spRecMon = request.POST.get('spRecMon', None) spFullEmpty = request.POST.get('spFullEmpty', None) fullPowerMin = request.POST.get('fullPowerMin', None) fullChargeTime = request.POST.get('fullChargeTime', None) elecTimeFirst = request.POST.get('elecTimeFirst', None) # 这几个参数是墨小智V3特有的 stWTime = request.POST.get('stWTime', None) temThre = request.POST.get('temThre', None) freeUse = request.POST.get('freeUse', None) relayMasterSwitch = request.POST.get('relayMasterSwitch', None) # 这几个参数是久恒保险丝版本特有的 firstCoinTime = request.POST.get('firstCoinTime', None) secondCoinTime = request.POST.get('secondCoinTime', None) thirdCoinTime = request.POST.get('thirdCoinTime', None) # 这几个参数是申林电子8档久恒板特有的 powerMax5 = request.POST.get('powerMax5', None) powerMax6 = request.POST.get('powerMax6', None) powerMax7 = request.POST.get('powerMax7', None) powerMax8 = request.POST.get('powerMax8', None) power5Ti = request.POST.get('power5Ti', None) power6Ti = request.POST.get('power6Ti', None) power7Ti = request.POST.get('power7Ti', None) power8Ti = request.POST.get('power8Ti', None) if coinMin: lastSetConf.update({'coinMin': int(coinMin)}) if cardMin: lastSetConf.update({'cardMin': int(cardMin)}) if coinElec: lastSetConf.update({'coinElec': int(coinElec)}) if cardElec: lastSetConf.update({'cardElec': int(cardElec)}) if cst: lastSetConf.update({'cst': int(cst)}) if powerMax1: lastSetConf.update({'powerMax1': int(powerMax1)}) if powerMax2: lastSetConf.update({'powerMax2': int(powerMax2)}) if powerMax3: lastSetConf.update({'powerMax3': int(powerMax3)}) if powerMax4: lastSetConf.update({'powerMax4': int(powerMax4)}) if power2Ti: lastSetConf.update({'power2Ti': int(power2Ti)}) if power3Ti: lastSetConf.update({'power3Ti': int(power3Ti)}) if power4Ti: lastSetConf.update({'power4Ti': int(power4Ti)}) if spRecMon: lastSetConf.update({'spRecMon': int(spRecMon)}) if spFullEmpty: lastSetConf.update({'spFullEmpty': int(spFullEmpty)}) if fullPowerMin: lastSetConf.update({'fullPowerMin': int(fullPowerMin)}) if fullChargeTime: lastSetConf.update({'fullChargeTime': int(fullChargeTime)}) if elecTimeFirst: lastSetConf.update({'elecTimeFirst': int(elecTimeFirst)}) # 这几个参数是墨小智V3特有的 if stWTime: lastSetConf.update({'stWTime': int(stWTime)}) if temThre: lastSetConf.update({'temThre': int(temThre)}) if freeUse: lastSetConf.update({'freeUse': int(freeUse)}) # 这几个参数是久恒保险丝版本特有的 if firstCoinTime: lastSetConf.update({'firstCoinTime': int(firstCoinTime)}) if secondCoinTime: lastSetConf.update({'secondCoinTime': int(secondCoinTime)}) if thirdCoinTime: lastSetConf.update({'thirdCoinTime': int(thirdCoinTime)}) # 这几个参数是申林电子8档久恒板特有的 if powerMax5: lastSetConf.update({'powerMax5': int(powerMax5)}) if powerMax6: lastSetConf.update({'powerMax6': int(powerMax6)}) if powerMax7: lastSetConf.update({'powerMax7': int(powerMax7)}) if powerMax8: lastSetConf.update({'powerMax8': int(powerMax8)}) if power5Ti: lastSetConf.update({'power5Ti': int(power5Ti)}) if power6Ti: lastSetConf.update({'power6Ti': int(power6Ti)}) if power7Ti: lastSetConf.update({'power7Ti': int(power7Ti)}) if power8Ti: lastSetConf.update({'power8Ti': int(power8Ti)}) if all([powerMax5, powerMax6, powerMax7, powerMax8, power5Ti, power6Ti, power7Ti, power8Ti]) is True: self.set_shenlin_8_gear_settings(lastSetConf) else: self.set_dev_setting(lastSetConf) # 新增总继电器开关 if relayMasterSwitch: try: self.relay_master_switch(relayMasterSwitch) except Exception as e: pass def handle_clear_start_error(self, port): dealer = Dealer.objects.get(id = self._device["ownerId"]) if not dealer or not dealer.managerialOpenId: return group = Group.get_group(self._device["groupId"]) self.lock_unlock_port(port, lock = False) notifyData = { "title": "设备故障报警解除", "device": u"{gNum}组-{lc}-{port}端口".format(gNum = self._device["groupNumber"], lc = self._device["logicalCode"], port = port), "location": u"{address}-{groupName}".format(address = group["address"], groupName = group["groupName"]), "fault": u"当前端口已被正常启动,端口故障解除", "notifyTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") } task_caller( func_name = 'report_to_dealer_via_wechat', openId = dealer.managerialOpenId, dealerId = str(dealer.id), templateName = "device_fault", **notifyData ) self.lock_unlock_port(port, lock = False) def handle_out_start_error(self, port): dealer = Dealer.objects.get(id = self._device["ownerId"]) if not dealer or not dealer.managerialOpenId: return group = Group.get_group(self._device["groupId"]) times = Device.get_error_start_times(self._device["devNo"], port) self.lock_unlock_port(port, lock = True) notifyData = { "title": u"注意,您的设备可能发生故障!", "device": u"{gNum}组-{lc}-{port}端口".format(gNum = self._device["groupNumber"], lc = self._device["logicalCode"], port = port), "location": u"{address}-{groupName}".format(address = group["address"], groupName = group["groupName"]), "fault": u"当前设备端口连续启动 {times} 失败,目前该端口已经被自动禁用,请注意该端口是否发生故障".format(times = times), "notifyTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") } task_caller( func_name = 'report_to_dealer_via_wechat', openId = dealer.managerialOpenId, dealerId = str(dealer.id), templateName = "device_fault", **notifyData ) # 麦总的告警3次触发,同时锁定设备,为避免解锁设备之后 再次触发 这个地方需要将其缓存清空 Device.delete_error_start_times(self._device["devNo"], port) @SmartBox.check_device_features(device_features=["fun_code_30_31"]) def relay_master_switch(self,relayMasterSwitch=1): if relayMasterSwitch == 1: data = "01" else: data = "00" devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], 'funCode': '31', 'data': data},timeout=7) 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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) self.device.update_other_conf(relayMasterSwitch=relayMasterSwitch) @SmartBox.check_device_features(device_features=["fun_code_30_31"], no_features_to_return={"haveStatus": False}) def get_real_time_device_info(self): try: devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], 'funCode': '30', 'data': '00'},timeout=7) 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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) except Exception: return {"haveStatus": False} data = devInfo.get("data") maxPower = int(data[8:12], 16) elec = int(data[12:16], 16) maxTemperature = int(data[16:18], 16) base = -1 if int(data[18:20], 16) == 1 else 1 temperature = int(data[20:22], 16) * base fire = int(data[22:24], 16) smoke = int(data[24:26], 16) overheat = int(data[26:28], 16) overload = int(data[28:30], 16) relayMasterSwitch = int(data[30:32], 16) return {"haveStatus": True, "maxPower": maxPower, "elec": elec, "maxTemperature": maxTemperature, "temperature": temperature, "fire": fire, "smoke": smoke, "overheat": overheat, "overload": overload, "relayMasterSwitch": relayMasterSwitch, } def _response_to_2D(self, sessionId): """ 回复 充电桩主板的2D指令 否则会重复上报 :param sessionId: :return: """ MessageSender.send( device=self.device, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, payload={ "funCode": "2D", "data": sessionId, "IMEI": self.device.devNo } ) def _check_package(self, package): """ 获取设备启动的发送数据 根据设备的当前模式以及套餐获取 :param package: :return: """ unit = package.get("unit", u"分钟") _time = float(package.get("time", 0)) # 按时间计费 if unit == u"小时": billingType = "time" _time = _time * 60 elif unit == u"天": billingType = "time" _time = _time * 24 * 60 elif unit == u"秒": billingType = "time" _time = _time / 60 elif unit == u"分钟": billingType = "time" _time = _time else: billingType = "elec" _time = _time return _time, unit, billingType def response_card(self, res, balance, fee, oper, cardNoHex, isVir=False, virtual_card_id=None): if isVir: res = int(res) count = int(balance) * 10 fee = int(fee) * 10 oper = int(oper) MessageSender.send( device=self.device, cmd=220, payload={ "res": res, "balance": count, "card_cst": fee, "card_ope": oper, "card_id": cardNoHex, "funCode": "10", "isVir": isVir, "virtual_card_id": virtual_card_id } ) else: res = int(res) balance = int(balance) * 10 fee = int(fee) * 10 oper = int(oper) MessageSender.send( device=self.device, cmd=220, payload={ "res": res, "balance": balance, "card_cst": fee, "card_ope": oper, "card_id": cardNoHex, "funCode": "10", "isVir": isVir } ) def apiGetPortStatusFromJh(self, record): return self.get_port_status_from_dev() def apiGetPortStatusFromJn(self, record): port = record['port'] statusData = self.get_port_status_from_dev() portData = self.get_port_info(port) device = Device.objects(devNo=self._device.devNo).first() group = Group.objects(id=device.groupId).first() cs = ConsumeRecord.objects(devNo=self._device.devNo).order_by('-dateTimeAdded')[0:10] elecValue = 0 for _ in cs: if _.attachParas.get('chargeIndex', '0') == port: usedHours = round(float(round(((datetime.datetime.now() - _.dateTimeAdded).total_seconds() / 60.0))) / 60, 2) elecValue = round((usedHours * portData.get('power', 0)) / 1000, 2) break else: elecValue = 0 status = statusData.get(portData.get('port', -1), '04') if status != '01': elecValue = 0 voltage = random.randint(220, 240) elecCurrent = 0 result = { 'port': portData.get('port', -1), # int 'status': statusData.get(portData.get('port', -1), '04'), # str '00' -> idle, '01' -> working, '03' -> 'forbidden', '04' -> 'fault' 'voltage': voltage, # int 'deviceCode': self._device.logicalCode, # str 'elecCurrent': elecCurrent, # float 'elecValue': elecValue, # float 'leftTime': portData.get('leftTime', 0) * 60, # int 秒 'groupName': group.groupName # str } return result def apiStartDeviceForJh(self, record): port = record['port'] minutes = record['time'] elec = record['elec'] hexPort = fill_2_hexByte(hex(int(port)), 2) hexCoins = '000A' hexTime = fill_2_hexByte(hex(int(minutes))) hexElec = fill_2_hexByte(hex(int(float(elec) * 100)), 4) devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, { 'IMEI': self._device['devNo'], "funCode": '14', 'data': hexPort + hexCoins + hexTime + hexElec }, timeout=10) if devInfo.has_key('rst') and devInfo['rst'] != 0: if devInfo['rst'] == -1: return {'result': u'启动失败', 'resultStatus': 2, 'description': u'当前设备信号弱无响应。'} elif devInfo['rst'] == 1: return {'result': u'启动失败', 'resultStatus': 2, 'description': u'设备串口不通。'} data = devInfo['data'][6::] if data[0:2] == '01': # 表示成功 pass else: return {'result': u'启动失败', 'resultStatus': 2, 'description': u'设备返回失败。'} result = data[4:6] if result == '01': # 成功 pass elif result == '0B': return {'result': u'启动失败', 'resultStatus': 2, 'description': u'设备故障。'} elif result == '0C': return {'result': u'启动失败', 'resultStatus': 2, 'description': u'端口正在使用中。'} start_timestamp = int(time.time()) portDict = { 'startTime': timestamp_to_dt(start_timestamp).strftime('%Y-%m-%d %H:%M:%S'), 'status': Const.DEV_WORK_STATUS_WORKING, 'coins': 1, 'price': 1, 'needElec': elec, 'needTime': minutes, 'isApi': True } Device.update_dev_control_cache(self._device['devNo'], {str(port): portDict}) return {'result': u'启动成功', 'resultStatus': 1, 'description': u'设备开始充电。'} def apiStopChargingPortForJh(self, record): port = record['port'] self.stop_charging_port(port) return {'result': u'停止充电成功', 'resultStatus': 1, 'description': u'{}号端口停止充电。'.format(str(port))} def apiGetPortInfoFromJh(self, record): result = self.get_port_info(record['port']) result.pop('surp') return result def get_ports_info(self, **kw): result = self.get_port_status_from_dev() port_list = [] ctrInfo = Device.get_dev_control_cache(self.device.devNo) statsMap = {Const.DEV_WORK_STATUS_IDLE: 'idle', Const.DEV_WORK_STATUS_WORKING: 'busy', Const.DEV_WORK_STATUS_FAULT: 'fault', Const.DEV_WORK_STATUS_FORBIDDEN: 'ban', Const.DEV_WORK_STATUS_CONNECTED: 'connected', Const.DEV_WORK_STATUS_FINISHED: 'finished'} for port, _info in result.items(): _info['index'] = port if _info.get('status') == Const.DEV_WORK_STATUS_WORKING: _info.update(self.get_port_using_detail(port, ctrInfo, isLazy=True)) for k, v in _info.items(): if k.lower().endswith('money'): _info.pop(k) _info['status'] = statsMap.get(_info['status'], 'busy') port_list.append(_info) return port_list def _set_service_charge_pare_for_dev(self, elecCharge, serviceCharge, on): _elecCharge = float(str(elecCharge)) _serviceCharge = float(str(serviceCharge)) billAsServiceSwitch = '01' if on else '00' hexElecCharge_4 = fill_2_hexByte(hex(int(_elecCharge * 100)), 4) hexServiceCharge_4 = fill_2_hexByte(hex(int(_serviceCharge * 100)), 4) data = billAsServiceSwitch + hexElecCharge_4 + hexServiceCharge_4 + "0000000000" devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, { 'IMEI': self._device['devNo'], "funCode": '22', 'data': data }, timeout=7) 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'设备通讯失败'}) self.device.update_device_obj(**{ 'devType.features.billAsService.elecCharge': _elecCharge, 'devType.features.billAsService.serviceCharge': _serviceCharge, 'devType.features.billAsService.on': True if billAsServiceSwitch == '01' else False }) def __get_service_charge_pare_for_dev(self): serviceChargeInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {"funCode": '23', 'data': '00'}, timeout=7) if serviceChargeInfo['rst'] != 0: if serviceChargeInfo['rst'] == -1: raise ServiceException( {'result': 2, 'description': u'充电桩正在玩命找网络,请您稍候再试'}) elif serviceChargeInfo['rst'] == 1: raise ServiceException( {'result': 2, 'description': u'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) else: raise ServiceException( {'result': 2, 'description': u'设备通讯失败'}) data = serviceChargeInfo['data'][8:] on = True if data[:2] == '01' else False elecCharge = float(int(data[2:6], 16)) / 100 serviceCharge = float(int(data[6:10], 16)) / 100 Device.get_collection().update_one({'devNo': self.device.devNo}, { '$set': { 'devType.features.billAsService.elecCharge': elecCharge, 'devType.features.billAsService.serviceCharge': serviceCharge, 'devType.features.billAsService.on': on }}) Device.invalid_device_cache(self.device.devNo) return { "on": on, "elecCharge": elecCharge, "serviceCharge": serviceCharge } def response_balance_inquiry_50(self,balance,leftDayQuota,cardNo,status,power='0000'): hexBalance = fill_2_hexByte(hex(int(float(balance) * 10))) hexLeftDayQuota = fill_2_hexByte(hex(int(leftDayQuota))) hexCardNo = fill_2_hexByte(hex(int(cardNo))) hexStatus = fill_2_hexByte(hex(int(status)),2) hexPower = fill_2_hexByte(hex(int(float(power)))) data = hexBalance + hexLeftDayQuota + hexCardNo + hexStatus + hexPower MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, {'IMEI': self._device['devNo'], 'funCode': '50', 'data': data}, timeout=7) def response_use_card_52(self,cardNo): data = fill_2_hexByte(hex(int(cardNo)),8) MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, {'IMEI': self._device['devNo'], 'funCode': '52', 'data': data}, timeout=7) def respone_use_card_finished_56(self,cardNo): data = fill_2_hexByte(hex(int(cardNo)),8) MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, {'IMEI': self._device['devNo'], 'funCode': '56', 'data': data}, timeout=7) def response_ak_5X(self,ack_id): data = ack_id MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, {'IMEI': self._device['devNo'], 'funCode': 'AK', 'data': data}, timeout=7) def get_server_setting(self): refundProtectionTime = int(self.device.get_other_conf_item('refundProtectionTime', 5)) return { "refundProtectionTime": refundProtectionTime, "cardRefundProtectionTime": int( self.device.get_other_conf_item('cardRefundProtectionTime', refundProtectionTime)), "minUsedTime": int(self.device.get_other_conf_item('minUsedTime', 0)), "billingType": self.device.get_other_conf_item('billingType', 'time'), "elecFee": float(self.device.get_other_conf_item('elecFee', 0.9)) } def set_server_setting(self, payload): minUsedTime = int(payload['minUsedTime']) refundProtectionTime = int(payload['refundProtectionTime']) cardRefundProtectionTime = int(payload['cardRefundProtectionTime']) if minUsedTime > 0: if refundProtectionTime >= minUsedTime: raise InvalidParameter(u'最小使用时长不能小于扫码保护退费时间') if cardRefundProtectionTime >= minUsedTime: raise InvalidParameter(u'最小使用时长不能小于刷卡保护退费时间') self.device.update_other_conf(refundProtectionTime=refundProtectionTime, cardRefundProtectionTime=cardRefundProtectionTime, minUsedTime=minUsedTime, billingType=payload['billingType'], elecFee=float(payload['elecFee'])) def set_service_fee_info(self, payload): self._set_service_charge_pare_for_dev( payload['billAsService'].get('elecCharge'), payload['billAsService'].get('serviceCharge'), payload['billAsService'].get('on')) super(ChargingJNDZBox, self).set_service_fee_info(payload) def get_service_fee_info(self): rv = super(ChargingJNDZBox, self).get_service_fee_info() rv['billAsService'].update(self.__get_service_charge_pare_for_dev()) return rv class IdCardStartAckEvent(AckEvent): pass