# -*- coding: utf-8 -*- #!/usr/bin/env python import datetime import logging import time from mongoengine import DoesNotExist from apilib.monetary import RMB, VirtualCoin from apps.web.constant import Const from apps.web.dealer.models import Dealer from apps.web.device.models import WeifuleDeviceOrder, Group, Device from apps.web.device.timescale import FluentedEngine from apps.web.eventer import EventBuilder from apps.web.eventer.base import WorkEvent, FaultEvent from apps.web.user.models import ServiceProgress, UserVirtualCard, VCardConsumeRecord, \ ConsumeRecord, Card, CardConsumeRecord, CardRechargeOrder, MyUser, RechargeRecord from django.conf import settings from apps.web.core.adapter.base import reverse_hex,make_cartcp_order_no,asc_to_string from apps.web.device.models import Part from apps.web.south_intf.swap_carcharger import SwapContract from apps.web.constant import DeviceOnlineStatus import time logger = logging.getLogger(__name__) class builder(EventBuilder): def __getEvent__(self, device_event): return NengpaiBox(self.deviceAdapter, device_event) class NengpaiFaultEvent(FaultEvent): def do(self, **args): return # if self.event_data['fun_code'] == fault_event_mcu_36: # self.event_data.update({ # 'faultName':u'单片机告警', # 'faultCode':FAULT_CODE.MCU_REBOOT, # 'level':FAULT_LEVEL.NORMAL, # 'desc':u'充电主板上的单片机重启。' # }) # elif self.event_data['fun_code'] == fault_event_relay_39: # self.event_data.update({ # 'faultName':u'继电器告警', # 'port':int(self.event_data['port']), # 'faultCode':FAULT_CODE.RELAY_FAULT, # 'level':FAULT_LEVEL.CRITICAL, # 'desc':u'继电器发送粘连。' # }) # elif self.event_data['fun_code'] == fault_event_counter_40: # self.event_data.update({ # 'faultName':u'计量芯片告警', # 'port':int(self.event_data['port']), # 'faultCode':FAULT_CODE.COUNTER_FAULT, # 'level':FAULT_LEVEL.CRITICAL, # 'desc':u'计量芯片获取功率失败。' # }) # elif self.event_data['fun_code'] == fault_event_overload_38: # self.event_data.update({ # 'faultName':u'整机功率过载告警', # 'faultCode':FAULT_CODE.DEV_OVERLOAD, # 'level':FAULT_LEVEL.FATAL, # 'desc':u'整机功率超过7500瓦。' # }) # else: # pass # super(NengpaiFaultEvent, self).do() class NengpaiBox(WorkEvent): # 用于计数上报的电压、电流、功率 countMap = {} # 时段数组 shiduanList = [] # 分时数组 fenshiList = [] # 服务费 serverMoneyList = [] # 电费 eleMoneyList = [] current_dict = { 'startTime': '', # 区间开始时间 'endTime': '', # 区间结束时间 'ele': '', # 当前区间充电总电数 'money': '', # 当前区间已充金额 'perServeMoney': '', # 当前区间服务费(元/度) 'elecFee': '', # 当前区间电费(元/度) 'eleMoney': '', # 当前区间总电费 'serveMoney': '', # 当前区间服务费 'chargedMoney': '', # 已充金额 'totalEle': '', # 充电度数 } # 微付乐的板子:根据设备上报上来的订单,创建正在服务的信息 def create_progress_for_weifule_order(self, dev, consumeRcd, order): try: progress = ServiceProgress.objects.filter(weifuleOrderNo=order['id']).first() if progress: return port = int(order['port']) attachParas = consumeRcd.attachParas needKind, needValue, unit = self.analyse_need_from_package(order) consumeOrder = { 'orderNo': consumeRcd.orderNo, 'coin': consumeRcd.coin.mongo_amount, 'money': consumeRcd.money.mongo_amount, 'unit': unit, 'consumeType':'mobile_vcard' if u'虚拟卡' in consumeRcd.remarks else 'mobile' } if needKind: consumeOrder.update({needKind:needValue}) new_service_progress = ServiceProgress( open_id=consumeRcd.openId, device_imei=consumeRcd.devNo, devTypeCode=dev['devType']['code'], port=port, attachParas=attachParas if attachParas else {}, start_time=int(order['create_time']), finished_time=24 * 60 * 60, consumeOrder=consumeOrder ) new_service_progress.save() except Exception as e: logger.exception(e) def consume_money_for_ID_card(self, agentId, cardNo, balance, money): card = Card.objects.get(agentId=agentId, cardNo=cardNo) if card.balance < money: card.balance = RMB(0) else: card.balance = (card.balance - money) # card.showBalance = card.balance #只有微付乐是创建订单成功后才真正扣费 try: card.save() except Exception as e: logger.exception(e) return card def update_balance_for_IC_card(self, card, balance): card.balance = balance try: card.save() except Exception, e: pass def is_order_accepted(self, orderNo, cmdCode): return True if WeifuleDeviceOrder.objects.filter(orderNo=orderNo, cmdCode=cmdCode).count() > 0 else False def record_order_event(self, order, cmdCode): newObj = WeifuleDeviceOrder(orderNo=order['id'], cmdCode=cmdCode, order=order) try: newObj.save() except Exception, e: logger.exception('save order event error=%s' % e) pass def record_consume_for_card(self, card,port,orderNo): group = Group.get_group(self.device['groupId']) address = group['address'] group_number = self.device['groupNumber'] now = datetime.datetime.now() new_record = { 'orderNo': orderNo, 'time': now.strftime("%Y-%m-%d %H:%M:%S"), 'dateTimeAdded': now, 'openId': card.openId, 'ownerId': self.device['ownerId'], 'coin': VirtualCoin(0.0).mongo_amount, 'money': RMB(0.00).mongo_amount, 'devNo': self.device['devNo'], 'logicalCode': self.device['logicalCode'], 'groupId': self.device['groupId'], 'address': address, 'groupNumber': group_number, 'groupName': group['groupName'], 'devTypeCode': self.device.devTypeCode, 'devTypeName': self.device.devTypeName, 'isNormal': False, 'status': ConsumeRecord.Status.RUNNING, 'remarks': u'刷卡消费', 'errorDesc': '', 'sequanceNo': '', 'desc': '', 'attachParas': {}, 'servicedInfo': {} } ConsumeRecord.get_collection().insert_one(new_record) # 刷卡消费也记录一条数据 new_card_record = { 'orderNo': orderNo, 'openId': card.openId, 'cardId': str(card.id), 'money': RMB(0.00).mongo_amount, 'balance': card.balance.mongo_amount, 'devNo': self.device['devNo'], 'devType': self.device['devType']['name'], 'logicalCode': self.device['logicalCode'], 'groupId': self.device['groupId'], 'address': address, 'groupNumber': group_number, 'groupName': group['groupName'], 'result': 'success', 'remarks': u'刷卡消费', 'sequanceNo': '', 'dateTimeAdded': datetime.datetime.now(), 'desc': '', 'servicedInfo': {}, 'linkedConsumeRcdOrderNo':str(new_record['orderNo']) } CardConsumeRecord.get_collection().insert_one(new_card_record) return new_record['orderNo'], new_card_record['orderNo'] # 解析报文内容的函数 def get_msg_info(self): data = self.event_data['data'] cmdCode = self.event_data['cmd'] result = {} if cmdCode == '01': result['type'] = 'jl' if data[26:28] == '00' else 'zl' result['portNum'] = int(data[28:30]) result['conVersion'] = 'V%s' % int(data[30:32],16) result['driverVersion'] = asc_to_string(data[32:48]) result['iccid'] = str(data[50:70]) return result # 获取设备是交流还是直流 def get_charger_type(self): if u'交流' in self.device.majorDeviceType: return 'ac' if u'直流' in self.device.majorDeviceType: return 'dc' return 'ac' # 登记设备 def register_dev(self,data): server,port,proto = self.device.network_address devObj = Device.objects.get(devNo = self.device['devNo']) # 获取sim卡,卡号 msgInfo = self.get_msg_info() devObj.iccid = msgInfo['iccid'] devObj.softVer = msgInfo['driverVersion'] devObj.driverCode = Const.DEVICE_TYPE_CODE_CAR_NENGPAI # 驱动必须赋值 # 第一次上线的时候,需要把server以及feeMode的数据初始化 if server != settings.CAR_TCPIP_SERVER or port != settings.CAR_TCPIP_SERVER_PORT or 'feeMode' not in devObj.otherConf: devObj.server= settings.CAR_TCPIP_SERVER + ":" + str(settings.CAR_TCPIP_SERVER_PORT) if 'feeMode' not in devObj.otherConf: logger.info('init feemode to device,devNo=%s' % self.device['devNo']) devObj.otherConf['feeMode'] = {'modeNo':'0000','jianFee':1.20000, 'fengFee':1.20000, 'pingFee':1.20000, 'guFee':1.20000, 'jianServe':0, 'fengServe':0, 'pingServe':0, 'guServe':0, 'jishunScale':0, 'shiduan':'000000000000000000000000000000000000000000000000' } devObj.save() Device.invalid_device_cache(self.device['devNo']) Device.update_dev_control_cache(self.device['devNo'],{'allPorts': msgInfo['portNum']}) Device.update_online_cache(self.device['devNo'], True,32)# 设备没有返回信号量,所以就用32 def update_port_by_heartbeat(self): data = self.event_data['data'] port = int(data[-8:-6]) part = Part.insert_part_if_not_exist(self.device['logicalCode'],self.device['ownerId'],port, '%s号充电枪' % port,self.get_charger_type())# 交流电 if data[-6:-4] == '01': lastStatus = Device.get_dev_control_cache(self.device['devNo']).get(str(port),{}).get('status',None) Device.update_dev_control_cache(self.device['devNo'], { str(port):{'status':Const.DEV_WORK_STATUS_FAULT} } ) Part.update_part_work_status(self.device['logicalCode'], port, Part.Status.FAULT) Part.update_part_network_status(self.device['logicalCode'], port, Part.OnlineStatus.OFFLINE) if lastStatus != Const.DEV_WORK_STATUS_FAULT: SwapContract.notify_2_all_northers_port_status(self.device,port,Const.DEV_WORK_STATUS_FAULT) else: portStatus = Device.get_dev_control_cache(self.device['devNo']).get(str(port),{}).get('status',None) if portStatus is None or portStatus != Const.DEV_WORK_STATUS_WORKING: Device.update_dev_control_cache(self.device['devNo'], { str(port):{'status':Const.DEV_WORK_STATUS_IDLE} } ) Part.update_part_network_status(self.device['logicalCode'], port, Part.OnlineStatus.ONLINE) # 刷新端口详细信息 def update_order_info(self): data = self.event_data['data'] orderNo = data[12:44] port = int(data[58:60]) valueDict = self.deviceAdapter.get_values_from_data(data) portStatus = valueDict['status'] lastStatus = Device.get_dev_control_cache(self.device['devNo']).get(str(port),{}).get('status',None) Device.update_dev_control_cache(self.device['devNo'],{str(port):valueDict}) ConsumeRecord.get_collection().update_one({'orderNo': orderNo}, {'$set': {'isNormal': True, 'status': ConsumeRecord.Status.RUNNING}}) CardConsumeRecord.get_collection().update_one({'orderNo':orderNo},{'$set':{'result':'success'}}) # 更新part的状态 Part.insert_part_if_not_exist(self.device['logicalCode'],self.device['ownerId'],port, '%s号充电枪' % port,self.get_charger_type())# 交流电 Part.update_part_work_status(self.device['logicalCode'], port, portStatus) Part.update_part_network_status(self.device['logicalCode'], port,Part.OnlineStatus.ONLINE) # 比较端口状态,如果发生变化,需要通知北向 group = Group.get_group(self.device['groupId']) if lastStatus != portStatus and group.get('swapFlag',False): SwapContract.notify_2_all_northers_port_status(self.device,port,portStatus) # 将功率,电流,电压推入时间数据库 devNo = self.device['devNo'] key = '%s-%s' % (devNo, port) # 初始化计数器 if key not in NengpaiBox.countMap: NengpaiBox.countMap[key] = 0 else: NengpaiBox.countMap[key] += 1 print('!!!!!!!!!!!!!!!!!!!NengpaiBox.countMap', NengpaiBox.countMap[key]) # 充电时,事件上报间隔为15s,每60s记录一次,ts为当前时间 if NengpaiBox.countMap[key] % 4 == 0: voltage = int(reverse_hex(data[66:70]), 16) / 10.0 current = int(reverse_hex(data[70:74]), 16) / 10.0 power = voltage * current print('!---------------!insert one record into timedatabase') # 插入时间数据库 FluentedEngine().in_power_udp(devNo=devNo, port=port, ts=int(time.time()), power=power, voltage=voltage, current=current) # 进行分时计算 devObj = Device.objects.get(devNo=self.device['devNo']) feeMode = devObj.otherConf.get('feeMode', {}) shiduan = feeMode.get('shiduan', '000000000000000000000000000000000000000000000000') # 尖峰平谷的服务费从数据库中取得(元/度) jianServe = feeMode['jianServe'] fengServe = feeMode['fengServe'] pingServe = feeMode['pingServe'] guServe = feeMode['guServe'] # 尖峰平谷的电费从数据库中取得(元/度) jianFee = feeMode['jianFee'] fengFee = feeMode['fengFee'] pingFee = feeMode['pingFee'] guFee = feeMode['guFee'] # 判断缓存中是否有shuduanList,serverMoneyList,eleMoneyList if not Device.get_dev_control_cache(self.device['devNo']).get('shiduanList',None): for ii in range(48): startHour = 0 + ii / 2 startMin = '00' if ii % 2 == 0 else '30' startTime = '%02d%s00' % (startHour, startMin) if (ii == 0) or (ii > 0 and shiduan[ii] != shiduan[ii - 1]): self.shiduanList.append(startTime) # 添加服务费,电费列表 if shiduan[ii] == '0': self.serverMoneyList.append(jianServe) self.eleMoneyList.append(jianFee) elif shiduan[ii] == '1': self.serverMoneyList.append(fengServe) self.eleMoneyList.append(fengFee) elif shiduan[ii] == '2': self.serverMoneyList.append(pingServe) self.eleMoneyList.append(pingFee) elif shiduan[ii] == '3': self.serverMoneyList.append(guServe) self.eleMoneyList.append(guFee) self.shiduanList.append('235959') Device.update_dev_control_cache(self.device['devNo'], {'shiduanList':self.shiduanList}) Device.update_dev_control_cache(self.device['devNo'], {'serverMoneyList':self.serverMoneyList}) Device.update_dev_control_cache(self.device['devNo'], {'eleMoneyList':self.eleMoneyList}) else: self.shiduanList = Device.get_dev_control_cache(self.device['devNo']).get(str(port),{}).get('shiduanList',None) self.serverMoneyList = Device.get_dev_control_cache(self.device['devNo']).get(str(port),{}).get('serverMoneyList',None) self.eleMoneyList = Device.get_dev_control_cache(self.device['devNo']).get(str(port),{}).get('eleMoneyList',None) # 将current_dict,fenshiList放入缓存中 if not Device.get_dev_control_cache(self.device['devNo']).get('current_dict', None) \ and not Device.get_dev_control_cache(self.device['devNo']).get('fenshiList', None): Device.update_dev_control_cache(self.device['devNo'], {'current_dict': self.current_dict}) Device.update_dev_control_cache(self.device['devNo'], {'fenshiList': self.fenshiList}) else: self.current_dict = Device.get_dev_control_cache(self.device['devNo']).get('current_dict', None) self.fenshiList = Device.get_dev_control_cache(self.device['devNo']).get('fenshiList', None) # 获取当前时间戳并解析 timestamp = int(time.time()) current_time = time.strftime("%H%M%S", time.localtime(timestamp)) #已充金额,充电度数 current_money = int(reverse_hex(data[120:128]), 16) / 10000.0 current_ele = int(reverse_hex(data[104:112]), 16) / 10000.0 # 记录分时 if self.shiduanList: for i in range(0, len(self.shiduanList) - 1): # 查看当前时刻所在时间段 if int(self.shiduanList[i]) <= int(current_time) <= int(self.shiduanList[i + 1]): # 存startTime,查看当前时间段的startTime是否为空 if not self.current_dict['startTime']: # 第一次startTime为当前时间 if len(self.fenshiList) == 0: self.current_dict['startTime'] = current_time self.current_dict['chargedMoney'] = current_money self.current_dict['totalEle'] = current_ele self.current_dict['elecFee'] = self.eleMoneyList[i] self.current_dict['perServeMoney'] = self.serverMoneyList[i] Device.update_dev_control_cache(self.device['devNo'], {'current_dict': self.current_dict}) # 非第一次startTime为shiduanList最后一段的endTime,同时更新当前区间电费和服务费 else: self.current_dict['startTime'] = self.fenshiList[-1]['endTime'] self.current_dict['chargedMoney'] = self.fenshiList[-1]['chargedMoney'] self.current_dict['totalEle'] = self.fenshiList[-1]['totalEle'] self.current_dict['elecFee'] = self.eleMoneyList[i] self.current_dict['perServeMoney'] = self.serverMoneyList[i] Device.update_dev_control_cache(self.device['devNo'], {'current_dict': self.current_dict}) break # 存endTime,先处理特殊情况,判断是否快到下一个时间段,或者是否是最后一个时间段,是的话将数据更新到fenshiList中(eg:140000-135945=4055): if int(current_time) >= int(self.shiduanList[i + 1]) - 4055 or int(current_time) >= 235944: self.fenshiList[-1]['endTime'] = current_time self.fenshiList[-1]['money'] = current_money - self.current_dict['chargedMoney'] self.fenshiList[-1]['ele'] = current_ele - self.current_dict['totalEle'] self.fenshiList[-1]['chargedMoney'] = current_money self.fenshiList[-1]['totalEle'] = current_ele self.fenshiList[-1]['serveMoney'] = self.fenshiList[-1]['perServeMoney'] * self.fenshiList[-1][ 'ele'] # 当前区间电费可以使用计算,也可以使用当前区间已充金额减去当前区间服务费 self.fenshiList[-1]['eleMoney'] = self.fenshiList[-1]['money'] - self.fenshiList[-1]['serveMoney'] self.current_dict = { 'startTime': '', 'endTime': '', 'ele': '', 'money': '', 'perServeMoney': '', 'elecFee': '', 'eleMoney': '', 'serveMoney': '', 'chargedMoney': '', 'totalEle': '', } Device.update_dev_control_cache(self.device['devNo'], {'current_dict': self.current_dict}) Device.update_dev_control_cache(self.device['devNo'], {'fenshiList': self.fenshiList}) break # 存endTime,如果startTime不为空,则记录其他数据,并填入到list中 # fenshiList有当前时间段的数据,则更新数据 elif len(self.fenshiList) != 0 and int(self.shiduanList[i]) <= int( self.fenshiList[-1]['endTime']) <= int(self.shiduanList[i + 1]): self.fenshiList[-1]['endTime'] = current_time self.fenshiList[-1]['money'] = current_money - self.fenshiList[-1]['chargedMoney'] self.fenshiList[-1]['ele'] = current_ele - self.fenshiList[-1]['totalEle'] self.fenshiList[-1]['serveMoney'] = float(self.fenshiList[-1]['perServeMoney']) * float( self.fenshiList[-1]['ele']) self.fenshiList[-1]['eleMoney'] = float(self.fenshiList[-1]['money']) - float( self.fenshiList[-1]['serveMoney']) Device.update_dev_control_cache(self.device['devNo'], {'fenshiList': self.fenshiList}) # fenshiList没有当前的数据 else: self.current_dict['endTime'] = current_time self.current_dict['money'] = current_money - float(self.current_dict['chargedMoney']) self.current_dict['ele'] = current_ele - float(self.current_dict['totalEle']) self.current_dict['serveMoney'] = float(self.current_dict['perServeMoney']) * float( self.current_dict['ele']) self.current_dict['eleMoney'] = float(self.current_dict['money']) - float( self.current_dict['serveMoney']) self.fenshiList.append(self.current_dict) Device.update_dev_control_cache(self.device['devNo'], {'fenshiList': self.fenshiList}) break # 针对互联互通,进行订单状态的推送 consumeRcd = ConsumeRecord.objects(orderNo = orderNo).first() if not consumeRcd or not consumeRcd.rechargeRcdId: return rechargeRcd = RechargeRecord.objects(id = consumeRcd.rechargeRcdId).first() if not rechargeRcd or rechargeRcd.via != 'swap': return part = Part.objects(logicalCode = self.device['logicalCode'],partNo = str(rechargeRcd.extraInfo['portNo'])).first() if not part: return adapterDict = {str(Const.DEV_WORK_STATUS_IDLE):1,str(Const.DEV_WORK_STATUS_FAULT):255,str(Const.DEV_WORK_STATUS_WORKING):3} orderStatus ={ 'StartChargeSeq':str(rechargeRcd.wxOrderNo), 'StartChargeSeqStat':4 if valueDict.get('status') == Const.DEV_WORK_STATUS_IDLE else 2, 'ConnectorID':str(part.id), 'ConnectorStatus':adapterDict.get(valueDict.get('status'),1), 'CurrentA':valueDict.get('current'), 'VoltageA':220.0, 'Soc':0, 'StartTime':str(rechargeRcd.time), 'EndTime':datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'TotalPower':round(valueDict.get('elec'),2), 'TotalMoney':round(valueDict.get('chargedMoney'),2), 'ConnectorId':str(part.id), } SwapContract.notify_2_all_northers_order_status(self.device,orderStatus) def __translate_reason(self, code): descDict = { '40':u'结束充电,远程停止。','41':u'结束充电,SOC 达到 100%。','42':u'结束充电,充电电量满足设定条件。','43':u'结束充电,充电金额满足设定条件', '44':u'结束充电,充电时间满足设定条件','45':u'结束充电,手动停止充电','46':u'其他原因','47':u'其他原因','48':u'其他原因','49':u'其他原因', '4A':u'充电启动失败,充电桩控制系统故障(需要重启或自动恢复)','4B':u'充电启动失败,控制导引断开','4C':u'充电启动失败,断路器跳位', '4D':u'充电启动失败,电表通信中断','4E':u'充电启动失败,余额不足','4F':u'充电启动失败,充电模块故障','50':u'充电启动失败,急停开入', '51':u'充电启动失败,防雷器异常','52':u'充电启动失败,BMS 未就绪','53':u'充电启动失败,温度异常','54':u'充电启动失败,电池反接故障', '55':u'充电启动失败,电子锁异常','56':u'充电启动失败,合闸失败','57':u'充电启动失败,绝缘异常','58':u'其他原因','59':u'充电启动失败,接收 BMS 握手报文 BHM 超时', '5A':u'充电启动失败,接收 BMS 和车辆的辨识报文超时 BRM','5B':u'充电启动失败,接收电池充电参数报文超时 BCP','5C':u'充电启动失败,接收 BMS 完成充电准备报文超时 BRO AA', '5D':u'充电启动失败,接收电池充电总状态报文超时 BCS','5E':u'充电启动失败,接收电池充电要求报文超时 BCL','5F':u'充电启动失败,接收电池状态信息报文超时 BSM', '60':u'充电启动失败,GB2015 电池在 BHM 阶段有电压不允许充电','61':u'充电启动失败,GB2015 辨识阶段在 BRO_AA 时候电池实际电压与 BCP 报文电池电压差距大于 5%', '62':u'充电启动失败,B2015 充电机在预充电阶段从 BRO_AA 变成BRO_00 状态','63':u'充电启动失败,接收主机配置报文超时', '64':u'充电启动失败,充电机未准备就绪,我们没有回 CRO AA,对应老国标','65':u'其他原因','66':u'其他原因','67':u'其他原因','68':u'其他原因','69':u'其他原因', '6A':u'充电异常中止,系统闭锁','6B':u'充电异常中止,导引断开','6C':u'充电异常中止,断路器跳位','6D':u'充电异常中止,电表通信中断','6E':u'充电异常中止,余额不足', '6F':u'充电异常中止,交流保护动作','70':u'充电异常中止,直流保护动作','71':u'充电异常中止,充电模块故障','72':u'充电异常中止,急停开入', '73':u'充电异常中止,防雷器异常','74':u'充电异常中止,温度异常','75':u'充电异常中止,输出异常','76':u'充电异常中止,充电无流','77':u'充电异常中止,电子锁异常', '78':u'其他原因','79':u'充电异常中止,总充电电压异常','7A':u'充电异常中止,总充电电流异常','7B':u'充电异常中止,单体充电电压异常','7C':u'充电异常中止,电池组过温', '7D':u'充电异常中止,最高单体充电电压异常','7E':u'充电异常中止,最高电池组过温','7F':u'充电异常中止,BMV 单体充电电压异常','80':u'充电异常中止,BMT 电池组过温', '81':u'充电异常中止,电池状态异常停止充电','82':u'充电异常中止,车辆发报文禁止充电','83':u'充电异常中止,充电桩断电','84':u'充电异常中止,接收电池充电总状态报文超时', '85':u'充电异常中止,接收电池充电要求报文超时','86':u'充电异常中止,接收电池状态信息报文超时','87':u'充电异常中止,接收 BMS 中止充电报文超时','88':u'充电异常中止,接收 BMS 充电统计报文超时', '89':u'充电异常中止,接收对侧 CCS 报文超时','8A':u'其他原因','8B':u'其他原因','8C':u'其他原因','8D':u'其他原因','8E':u'其他原因','8F':u'其他原因','90':u'其他原因' } return descDict.get(code,u'其他原因') def get_finished_value_dict(self,data): resultDict = {} resultDict['chargeIndex'] = int(data[58:60]) resultDict['startTime'] = '20%02d-%02d-%02d %02d:%02d:%02d' % (int(data[72:74],16),int(data[70:72],16),int(data[68:70],16),int(data[66:68],16),int(data[64:66],16),int('%s%s' % (data[62:64],data[60:62]),16)/1000) resultDict['finishTime'] = '20%02d-%02d-%02d %02d:%02d:%02d' % (int(data[86:88],16),int(data[84:86],16),int(data[82:84],16),int(data[80:82],16),int(data[78:80],16),int('%s%s' % (data[76:78],data[74:76]),16)/1000) resultDict['jianFee'] = int(reverse_hex(data[88:96]),16)/100000.0 resultDict['jianElec'] = int(reverse_hex(data[96:104]),16)/10000.0 resultDict['jianShun'] = int(reverse_hex(data[104:112]),16)/10000.0 resultDict['jianSpend'] = int(reverse_hex(data[112:120]),16)/10000.0 resultDict['fengFee'] = int(reverse_hex(data[120:128]),16)/100000.0 resultDict['fengElec'] = int(reverse_hex(data[128:136]),16)/10000.0 resultDict['fengShun'] = int(reverse_hex(data[136:144]),16)/10000.0 resultDict['fengSpend'] = int(reverse_hex(data[144:152]),16)/10000.0 resultDict['pingFee'] = int(reverse_hex(data[152:160]),16)/100000.0 resultDict['pingElec'] = int(reverse_hex(data[160:168]),16)/10000.0 resultDict['pingShun'] = int(reverse_hex(data[168:176]),16)/10000.0 resultDict['pingSpend'] = int(reverse_hex(data[176:184]),16)/10000.0 resultDict['guFee'] = int(reverse_hex(data[184:192]),16)/100000.0 resultDict['guElec'] = int(reverse_hex(data[192:200]),16)/10000.0 resultDict['guShun'] = int(reverse_hex(data[200:208]),16)/10000.0 resultDict['guSpend'] = int(reverse_hex(data[208:216]),16)/10000.0 resultDict['meterStart'] = int(reverse_hex(data[216:226]),16)/10000.0 resultDict['meterEnd'] = int(reverse_hex(data[226:236]),16)/10000.0 resultDict['allElec'] = int(reverse_hex(data[236:244]),16)/10000.0 resultDict['elec'] = int(reverse_hex(data[244:252]),16)/10000.0# 计损总电量 if resultDict['elec'] == 0:# 用这个总电量吧,如果数据不对 resultDict['elec'] = resultDict['allElec'] resultDict['spendMoney'] = int(reverse_hex(data[252:260]),16)/10000.0 typeDict = {'01':'app_start','02':'card_start','04':'offline_card_start','05':'vin_code_start'} resultDict['orderType'] = typeDict.get(data[294:296],'') resultDict['orderTime'] = '20%02d-%02d-%02d %02d:%02d:%02d' % (int(data[308:310],16),int(data[306:308],16),int(data[304:306],16),int(data[302:304],16),int(data[300:302],16),int('%s%s' % (data[298:300],data[296:298]),16)/1000) resultDict['reason'] = self.__translate_reason(data[310:312]) resultDict['cardNo'] = int(data[312:328],16) return resultDict def get_finished_value_dict_old(self,data): resultDict = {} resultDict['chargeIndex'] = int(data[58:60]) resultDict['startTime'] = '20%02d-%02d-%02d %02d:%02d:%02d' % (int(data[72:74],16),int(data[70:72],16),int(data[68:70],16),int(data[66:68],16),int(data[64:66],16),int('%s%s' % (data[62:64],data[60:62]),16)/1000) resultDict['finishTime'] = '20%02d-%02d-%02d %02d:%02d:%02d' % (int(data[86:88],16),int(data[84:86],16),int(data[82:84],16),int(data[80:82],16),int(data[78:80],16),int('%s%s' % (data[76:78],data[74:76]),16)/1000) resultDict['jianFee'] = int(reverse_hex(data[88:96]),16)/100000.0 resultDict['jianElec'] = int(reverse_hex(data[96:104]),16)/10000.0 resultDict['jianShun'] = int(reverse_hex(data[104:112]),16)/10000.0 resultDict['jianSpend'] = int(reverse_hex(data[112:120]),16)/10000.0 resultDict['fengFee'] = int(reverse_hex(data[120:128]),16)/100000.0 resultDict['fengElec'] = int(reverse_hex(data[128:136]),16)/10000.0 resultDict['fengShun'] = int(reverse_hex(data[136:144]),16)/10000.0 resultDict['fengSpend'] = int(reverse_hex(data[144:152]),16)/10000.0 resultDict['pingFee'] = int(reverse_hex(data[152:160]),16)/100000.0 resultDict['pingElec'] = int(reverse_hex(data[160:168]),16)/10000.0 resultDict['pingShun'] = int(reverse_hex(data[168:176]),16)/10000.0 resultDict['pingSpend'] = int(reverse_hex(data[176:184]),16)/10000.0 resultDict['guFee'] = int(reverse_hex(data[184:192]),16)/100000.0 resultDict['guElec'] = int(reverse_hex(data[192:200]),16)/10000.0 resultDict['guShun'] = int(reverse_hex(data[200:208]),16)/10000.0 resultDict['guSpend'] = int(reverse_hex(data[208:216]),16)/10000.0 resultDict['meterStart'] = int(reverse_hex(data[216:224]),16)/10000.0 #旧版本4个字节,新版本5个字节 resultDict['meterEnd'] = int(reverse_hex(data[224:232]),16)/10000.0 #旧版本4个字节,新版本5个字节 resultDict['allElec'] = int(reverse_hex(data[232:240]),16)/10000.0 resultDict['elec'] = int(reverse_hex(data[240:248]),16)/10000.0# 计损总电量 if resultDict['elec'] == 0:# 用这个总电量吧,如果数据不对 resultDict['elec'] = resultDict['allElec'] resultDict['spendMoney'] = int(reverse_hex(data[248:256]),16)/10000.0 typeDict = {'01':'app_start','02':'card_start','04':'offline_card_start','05':'vin_code_start'} resultDict['orderType'] = typeDict.get(data[290:292],'') resultDict['orderTime'] = '20%02d-%02d-%02d %02d:%02d:%02d' % (int(data[304:306],16),int(data[302:304],16),int(data[300:302],16),int(data[298:300],16),int(data[296:298],16),int('%s%s' % (data[294:296],data[292:294]),16)/1000) resultDict['reason'] = self.__translate_reason(data[306:308]) resultDict['cardNo'] = int(data[308:324],16) return resultDict def deal_with_finished_order(self): data = self.event_data['data'] orderNo = data[12:44] port = int(data[58:60]) devNo = self.device['devNo'] if self.event_data['cmd'] == '3B': # 1.4旧版本的 电表总起始值是4个直接,1.5后,换成了5个字节 consumeDict = self.get_finished_value_dict(data) else: consumeDict = self.get_finished_value_dict_old(data) logger.info('the data dict is %s ' % consumeDict) progress = ServiceProgress.objects.filter(weifuleOrderNo=orderNo).first() if progress is None : return False if progress.status == 'finished':# 防止重复的报文上来,导致重复退费 return True progress.status = 'finished' progress.isFinished = True progress.save() consumeRcd = ConsumeRecord.objects.filter(orderNo=orderNo).first() if not consumeRcd: return False try: # 首先把consumeRcd 更新状态 consumeRcd.status = ConsumeRecord.Status.FINISHED consumeRcd.save() # 更新part的状态 Part.update_part_work_status(self.device['logicalCode'], port, Part.Status.IDLE) group = Group.get_group(self.device['groupId']) coins = RMB(consumeDict['spendMoney']) backCoins = consumeRcd.coin - VirtualCoin(consumeDict['spendMoney']) if consumeRcd.coin > VirtualCoin(consumeDict['spendMoney']) else VirtualCoin(0) refundMoney = consumeRcd.money - RMB(consumeDict['spendMoney']) if consumeRcd.money > RMB(consumeDict['spendMoney']) else RMB(0.00) orderTitleDictList = [ # {u'尖电':u'单价:%s元,充电%s度,花费%s元' % (round(consumeDict['jianFee'],1),round(consumeDict['jianShun'],1),round(consumeDict['jianSpend'],1))}, # {u'峰电':u'单价:%s元,充电%s度,花费%s元' % (round(consumeDict['fengFee'],1),round(consumeDict['fengShun'],1),round(consumeDict['fengSpend'],1))}, # {u'平电':u'单价:%s元,充电%s度,花费%s元' % (round(consumeDict['pingFee'],1),round(consumeDict['pingShun'],1),round(consumeDict['pingSpend'],1))}, # {u'谷电':u'单价:%s元,充电%s度,花费%s元' % (round(consumeDict['guFee'],1),round(consumeDict['guShun'],1),round(consumeDict['guSpend'],1))}, {u'总电量':u'%s度' % round(consumeDict['elec'],1)}, {u'总金额':u'%s元' % round(consumeDict['spendMoney'],1)}, ] if u'虚拟卡' in consumeRcd.remarks: # 退额度 try: vRcd = VCardConsumeRecord.objects.get(orderNo=orderNo) vCard = UserVirtualCard.objects.get(id=vRcd.cardId) except DoesNotExist, e: logger.info('can not find the vCard id = %s' % vRcd.cardId) return # 通知服务结束 notifyOpenId = self.get_managerialOpenId_by_openId(vRcd.openId) if vCard else '' self.notify_user_service_complete( service_name = u'充电', openid = notifyOpenId, port = port, address = group['address'], reason = consumeDict['reason'], finished_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), extra = orderTitleDictList ) # 不需要退款,直接返回,不通知 if self.device.is_auto_refund: vCard.refund_quota(vRcd, 0.0, 0, backCoins.mongo_amount) # 刷卡的方式。不存在退费,直接从卡里面扣费 elif u'刷卡' in consumeRcd.remarks: dealer = Dealer.get_dealer(ownerId=self.device['ownerId']) card = Card.objects.filter(agentId=dealer['agentId'], cardNo=str(consumeDict['cardNo'])).first() if card is None: # 离线卡没有绑定或者在线卡被解绑了 return False # 先把消费记录的数据刷到准确值 consumeRcd.coin = VirtualCoin(consumeDict['spendMoney']) consumeRcd.money = RMB(consumeDict['spendMoney']) consumeRcd.save() cardRcd = CardConsumeRecord.objects(orderNo = orderNo).first() if cardRcd is None: return False cardRcd.money = RMB(consumeDict['spendMoney']) cardRcd.balance = card.balance - RMB(consumeDict['spendMoney']) cardRcd.finishedTime = datetime.datetime.now() cardRcd.save() # 然后扣费 self.consume_money_for_card(card,RMB(consumeDict['spendMoney'])) # 通知 self.notify_user_service_complete( service_name = u'充电', openid=card.managerialOpenId, port = port, address = group['address'], reason = consumeDict['reason'], finished_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), extra = orderTitleDictList ) elif u'扫码' in consumeRcd.remarks: # 扫码的 user = MyUser.objects(openId=consumeRcd.openId, groupId=self.device['groupId']).first() if not user: return False # 通知服务结束 notifyOpenId = user.managerialOpenId if user else '' # 如果需要退款,计算退款数据. if not self.device.is_auto_refund: return True if refundMoney > RMB(0): rechargeRcdId = consumeRcd.rechargeRcdId if rechargeRcdId: rechargeRcd = RechargeRecord.objects.filter(id=rechargeRcdId).first() else: rechargeRcd = None if rechargeRcd: # 退现金特征 + 有充值订单 self.refund_net_pay(user, {'rechargeRcdId': rechargeRcdId, 'openId': user.openId}, refundMoney, VirtualCoin(0), consumeDict, True) orderTitleDictList.append({u'退款':u'%s元' % refundMoney}) else: self.refund_net_pay(user, {'openId': user.openId}, RMB(0), backCoins, consumeDict, False) self.notify_user_service_complete( service_name = u'充电', openid = notifyOpenId, port = port, address = group['address'], reason = consumeDict['reason'], finished_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), extra = orderTitleDictList ) elif u'互联互通' in consumeRcd.remarks and consumeRcd.rechargeRcdId: rechargeRcd = RechargeRecord.objects(id = consumeRcd.rechargeRcdId).first() if rechargeRcd is None or rechargeRcd.via != 'swap': return part = Part.objects(logicalCode = self.device['logicalCode'],partNo = str(rechargeRcd.extraInfo['portNo'])).first() if part is None: return devObj = Device.objects(devNo = self.device['devNo']).first() if devObj is None: return feeMode= devObj.otherConf.get('feeMode') serveMoney = feeMode['jianServe'] * consumeDict['jianElec'] + feeMode['fengServe'] * consumeDict['fengElec'] \ + feeMode['pingServe'] * consumeDict['pingElec'] + feeMode['guServe'] * consumeDict['guElec'] reasonAdapter = {'40':1,'41':2,'42':1,'43':1,'44':1,'45':0,'00':1} result = { 'StartChargeSeq':str(rechargeRcd.wxOrderNo), 'ConnectorID':str(part.id), 'StartTime':str(rechargeRcd.time), 'EndTime':str(consumeDict['finishTime']), 'TotalPower':round(consumeDict['elec'],2), 'TotalElecMoney':round(consumeDict['spendMoney'] - serveMoney,2), 'TotalSeviceMoney':round(serveMoney,2), 'TotalMoney':round(consumeDict['spendMoney'],2), 'StopReason':reasonAdapter.get(data[310:312],0) if int(data[310:312]) <= 45 else int(data[310:312]) - 65 } consumeDict.update({'northerResult':result}) # 把通知北向的结果,也记录到数据库中,便于后续对账核对数据 SwapContract.notify_2_all_northers_order_info(self.device,result) # ServiceProgress.update_progress_and_consume_rcd(self.device['ownerId'], {'weifuleOrderNo':orderNo}, consumeDict) except Exception, e: logger.exception('some exception happed,devNo=%s,e=%s' % (devNo, e)) finally: logger.info('update progress and consume rcd') ServiceProgress.update_progress_and_consume_rcd(self.device['ownerId'], {'weifuleOrderNo':orderNo}, consumeDict) logger.info('clear_port_control_cache') Device.clear_port_control_cache(devNo, port) return True # 根据报文以及卡的信息,获取服务器发给设备的回复 def deal_card_start(self): data = self.event_data['data'] port = int(data[26:28]) sqNo = str(data[4:8]) cardNo = str(int(data[32:48], 16)) Card.record_dev_card_no(self.device['devNo'], cardNo) card = self.find_card_by_card_no(cardNo) # : 首先检查订单,并进行充值 # : 用户首先在手机客户端充值,需要这个时刻刷卡上报事件将充值的订单同步上来 if card: card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id)) if card_recharge_order: result = self.recharge_id_card(card=card, rechargeType='append', order=card_recharge_order) card.reload() else: self.deviceAdapter.reply_card_start(sqNo,'00000000000000000000000000000000',port,cardNo,0,'00','01') return replyResult = '00' # 如果没有卡,直接返回 if card.frozen: replyResult = '02' elif card.balance <= RMB(0): replyResult = '03' if self.device['status'] in [Const.DEV_WORK_STATUS_FAULT,Const.DEV_WORK_STATUS_FORBIDDEN]: replyResult = '05' # 如果没有dealerId,更新一次,记录到数据库,以后这个卡,就只能在这个经销商名下刷卡,不能在其他家刷卡了 if not card.dealerId: card = self.update_card_dealer_and_type(cardNo, 'ID') elif card.dealerId != self.device.ownerId: replyResult = '06' # 查找下是否出现刷多次。不允许出现刷多次 count = ServiceProgress.objects(cardId = str(card.id),isFinished=False,startTime__gte = datetime.datetime.now() - datetime.timedelta(hours=24)).count() if count > 0: replyResult = '04' devObj = Device.objects.get(devNo = self.device['devNo']) # 端口被禁用,也不允许使用 if devObj.otherConf.get(str(port),False): replyResult = '05' if replyResult == '00': success = '01' else: success = '00' orderNo = make_cartcp_order_no(self.device['devNo'],port) self.deviceAdapter.reply_card_start(sqNo,orderNo,port,card.cardNo,card.balance,success,replyResult) # 记录卡消费记录以及消费记录 orderNo, cardOrderNo = self.record_consume_for_card(card,port,orderNo) # 记录当前服务的progress,便于手机界面查询进度 consumeDict = {'orderNo': orderNo, 'cardOrderNo': cardOrderNo,'unit':u'次'} ServiceProgress.register_card_service_for_weifule(self.device, port, card, consumeDict) # 通知用户,已经扣费 self.notify_balance_has_consume_for_card(card, 0 ,u'(使用结束后,根据实际使用情况进行扣费结算)') Device.update_port_control_cache(self.device['devNo'], {'coins':0,'port':port}, 'overwrite') # 重新写端口数据,覆写掉 def do(self, **args): cmdCode = self.event_data['cmd'] if cmdCode == '01': # 登录鉴权 logger.info('this is authentication') self.register_dev(self.event_data) self.deviceAdapter.reply_authentication(self.event_data) try: # 先下发F0,如果报错则下发9C(北科新能源) self.deviceAdapter.send_qrcode() except Exception as e: self.deviceAdapter.send_qrcode2() elif cmdCode == '05': # 计费模型验证请求回复 self.deviceAdapter.reply_feemode(self.event_data) logger.info('send current time to device') self.deviceAdapter.send_current_time() elif cmdCode == '09': # 充电桩发现计费模型不一致的时候,主动请求计费模型 self.deviceAdapter.reply_new_feemode(self.event_data) elif cmdCode == '03': # 心跳应答 if self.device['ownerId']: # 没有注册,或者注销设备后,仍然有可能有数据上来 self.update_port_by_heartbeat() self.deviceAdapter.reply_heartbeat(self.event_data) elif cmdCode == '13': if self.device['ownerId']: # 没有注册,或者注销设备后,仍然有可能有数据上来 self.update_order_info() elif cmdCode == '19': # 充电结束,直接放在账单确认做处理 pass elif cmdCode in ['3B','3F']: # 充电的账单,3F是1.4以前的协议,内容没有变化 if self.device['ownerId']: # 没有注册,或者注销设备后,仍然有可能有数据上来 result = self.deal_with_finished_order() strTemp = '00' if result else '01' else: strTemp = '00' self.deviceAdapter.reply_finished_order(self.event_data,strTemp) elif cmdCode == '31': self.deal_card_start() else: # 处理任务事件 pass