# -*- coding: utf-8 -*- #!/usr/bin/env python import datetime import logging import time from django.core.cache import caches from apilib.utils_datetime import timestamp_to_dt from apps.web.constant import DeviceCmdCode, Const, MQTT_TIMEOUT from apps.web.core.adapter.base import SmartBox, fill_2_hexByte from apps.web.core.exceptions import ServiceException from apps.web.core.networking import MessageSender from apps.web.device.models import Device logger = logging.getLogger(__name__) class ChargingJNDZBox(SmartBox): def __init__(self, device): super(ChargingJNDZBox, self).__init__(device) def translate_funcode(self,funCode): funCodeDict = { '01':u'获取端口数量', '02':u'获取端口数据', '07':u'获取刷卡投币统计数据', '0F':u'获取端口状态', '0C':u'端口锁操作', '0D':u'端口开关', } return funCodeDict.get(funCode,'') def translate_event_cmdcode(self,cmdCode): cmdDict = { '06':u'充电结束', } return cmdDict.get(cmdCode,'') def test(self, coins): hexPort = fill_2_hexByte(1,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": '04', 'data': hexPort + hexCoins + hexTime}) return devInfo def stop(self,port): infoDict = self.stop_charging_port(port) infoDict['remainder_time'] = infoDict['leftTime'] return infoDict 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'请您选择合适的充电线路'}) devConf = caches['devmgr'].get('settingConf_%s' % (self._device['devNo'])) if devConf: refundProtection = devConf.get('refundProtection', 0) else: refundProtection = 0 price = float(package['price']) port = hex(int(attachParas['chargeIndex'])) hexPort = fill_2_hexByte(port,2) coins = int(package['coins']) hexCoins = fill_2_hexByte(hex(coins*10))#注意单位是角 unit = package.get('unit',u'分钟') if unit in [u'分钟',u'小时',u'天']: needTime = int(package['time']) if unit == u'小时': needTime = int(package['time'])*60 elif unit == u'天': needTime = int(package['time'])*1440 hexTime = fill_2_hexByte(hex(needTime)) else: raise ServiceException({'result': 2, 'description': u'套餐必须包含时间信息'}) #在启动设备前,如果可能是续充,需要获取下设备端口状态,便于后面核实。防止结束报文丢包导致数据不准确 #重新把设备上的状态取回来,可以保证needTime数据不出错,正在服务显示也不会有问题 ctrInfo = Device.get_dev_control_cache(self._device['devNo']) lastPortInfo = ctrInfo.get(str(attachParas['chargeIndex']), None) if (lastPortInfo is not None) and lastPortInfo.get('status','') == Const.DEV_WORK_STATUS_WORKING: self.get_port_status_from_dev() devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], "funCode": '04', 'data': hexPort + hexCoins + hexTime}, timeout = MQTT_TIMEOUT.START_DEVICE) 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'获取端口数据失败,请重试看能否解决'}) usePort = int(data[2:4],16) 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'该端口正在使用中'}) 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': float(coins), 'isStart': True, 'price': price, 'openId': openId, 'refunded': False, 'refundProtection': refundProtection, 'vCardId': self._vcard_id } ctrInfo = Device.get_dev_control_cache(self._device['devNo']) lastPortInfo = ctrInfo.get(str(usePort),None) if lastPortInfo is not None and lastPortInfo.get('status','') == Const.DEV_WORK_STATUS_WORKING: if lastPortInfo.has_key('coins'): portDict['coins'] = float(coins) + lastPortInfo['coins'] if lastPortInfo.has_key('price'): portDict['price'] = price + lastPortInfo['price'] portDict.update({'needTime':needTime}) if (lastPortInfo is not None) and lastPortInfo.has_key('needTime') and lastPortInfo.get('status','') == Const.DEV_WORK_STATUS_WORKING: portDict['needTime'] = needTime + lastPortInfo['needTime'] if attachParas.has_key('orderNo'): portDict.update({'orderNo':attachParas['orderNo']}) devInfo['finishedTime'] = start_timestamp + needTime * 60 portDict.update({'finishedTime': devInfo['finishedTime']}) Device.update_dev_control_cache(self._device['devNo'],{str(usePort):portDict}) return devInfo def analyze_event_data(self, data): cmdCode = data[4:6] if cmdCode in ['06']: port = int(data[8:10],16) leftTime = int(data[10:14],16) reason = data[14:16] if reason == '00': desc = u'购买的充电时间用完了。' elif reason == '01': desc = u'可能是插头被拔掉,或者电瓶已经充满。系统判断为异常断电,由于电瓶车充电器种类繁多,可能存在误差。如有问题,请您及时联系商家协助解决问题并恢复充电。' elif reason == '02': desc = u'恭喜您!电池已经充满电!' elif reason == '0B': desc = u'设备或端口出现问题,为了安全起见,被迫停止工作。建议您根据已经充电的时间评估是否需要到现场换到其他端口充电。' return {'status':Const.DEV_WORK_STATUS_IDLE,'cmdCode':cmdCode,'port':port,'leftTime':leftTime,'reason':desc,'reasonCode':reason} 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 = int(data[2:6],16)/10.0#以角为单位 coinFee = int(data[6:10],16)#以元为单位 return {'cardFee':cardFee,'coinFee':coinFee} def get_port_info(self,line): return {} 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 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) 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'}) 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} 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'}) 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) return {'port':port,'leftTime':leftTime} # #获取设备配置参数 def get_dev_setting(self): dev = Device.objects.get(devNo=self._device['devNo']) resultDict={} resultDict = self.get_coin_card_enable() refundProtection = dev.otherConf.get('refundProtection', 0) resultDict.update({'refundProtection':refundProtection}) consumeInfo = self.get_dev_consume_count() resultDict.update(consumeInfo) return resultDict #设置设备配置参数 def set_dev_setting(self,setConf): dev = Device.objects.get(devNo=self._device['devNo']) dev.otherConf.update({'coinActive': setConf['coinActive']}) dev.otherConf.update({'cardActive': setConf['cardActive']}) dev.otherConf.update({'refundProtection': setConf['refundProtection']}) dev.save() def get_coin_card_enable(self): devs = Device.get_collection().find({'devNo': self._device['devNo']}) if devs.count == 0: raise ServiceException( {'result': 2, 'description': u'没有找到设备哦'}) return {'putCoins': devs[0].get('otherConf', {}).get('putCoins', False), 'icCard': devs[0].get('otherConf', {}).get('icCard', False)} def set_coin_card_enable(self, infoDict): data = '' if infoDict['putCoins']: data += '01' else: data += '00' if infoDict['icCard']: data += '01' else: data += '00' devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], "funCode": '09', '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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) try: conf = Device.objects.get(devNo = self._device['devNo']).otherConf conf.update({'putCoins': infoDict['putCoins'], 'icCard': infoDict['icCard']}) Device.get_collection().update({'devNo': self._device['devNo']}, {'$set': {'otherConf': conf}}) except Exception, e: logger.error('update dev=%s coin enable ic enable e=%s' % (self._device['devNo'], e)) def set_device_function(self, request, lastSetConf): if request.POST.has_key('putCoins'): # putCoins = True if request.POST.get('putCoins') == 'true' else False putCoins = request.POST.get("putCoins", False) lastSetConf.update({'putCoins': putCoins}) self.set_coin_card_enable(lastSetConf) if request.POST.has_key('icCard'): # icCard = True if request.POST.get('icCard') == 'true' else False icCard = request.POST.get("icCard", False) lastSetConf.update({'icCard': icCard}) self.set_coin_card_enable(lastSetConf) if request.POST.has_key('refundProtection'): # refundProtection = 1 if request.POST.get('refundProtection') == 'true' else 0 refundProtection = int(request.POST.get("refundProtection", False)) dev = Device.objects.get(devNo = self._device['devNo']) dev.otherConf['refundProtection'] = refundProtection dev.save() @property def isHaveStopEvent(self): return True