# -*- coding: utf-8 -*- import datetime from django.core.cache import caches from apps.web.constant import Const, DeviceCmdCode, MQTT_TIMEOUT from apps.web.core.adapter.base import SmartBox, fill_2_hexByte, logger from apps.web.core.exceptions import ServiceException from apps.web.core.networking import MessageSender from apps.web.device.models import Device class ChargingLangXinBox(SmartBox): def __init__(self, device): super(ChargingLangXinBox, self).__init__(device) def translate_funcode(self, funCode): funCodeDict = { '01': u'获取端口数量', '02': u'移动支付', '0C': u'获取设备设置', '08': u'获取设备设置', '06': u'获取设备端口详情', '07': u'获取刷卡投币统计数据', '09': u'设置刷卡投币使能', '0A': u'端口使能', '0B': u'端口关闭', '16': u'设置设备参数', '20': u'设备重启', } return funCodeDict.get(funCode, '') def translate_event_cmdcode(self, cmdCode): cmdDict = { '03': u'投币上报', '04': u'刷卡上报', '05': u'充电结束', '10': u'刷卡上报', '0D': u'故障', } return cmdDict.get(cmdCode, '') # 获取端口状态(缓存) 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 = {} staticPorts = self.get_port_number_from_type_code() allPorts = ctrInfo.get('allPorts', staticPorts) # allPorts 有的时候会为0, 这个时候强制设置为设备类型CODE对应的字段 if allPorts == 0: allPorts = staticPorts 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": '01', '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::] result = {} portNum = int(data[0:2], 16) portData = data[2::] ii = 0 while ii < portNum: port = ii + 1 statusTemp = portData[ii * 2:ii * 2 + 2] 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 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 is None: conf = self.get_dev_setting() caches['devmgr'].set('settingConf_%s' % (self._device['devNo']), conf, 24 * 3600) port = hex(int(attachParas['chargeIndex'])) hexPort = fill_2_hexByte(port, 2) needTime = int(package['time']) needCoins = int(package['coins']) unit = package.get('unit', u'分钟') if unit == u'小时': needTime = int(package['time']) * 60 elif unit == u'天': needTime = int(package['time']) * 1440 hexTime = fill_2_hexByte(hex(needTime)) devInfo = MessageSender.send(device = self.device, cmd = DeviceCmdCode.OPERATE_DEV_SYNC, payload = { 'IMEI': self._device['devNo'], "funCode": '02', 'data': hexPort + 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::] usePort = int(data[0:2], 16) result = data[2:4] if result == '00': # 成功 pass elif result == '02': 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 == '03': 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'该端口正在使用中'}) portCache = Device.get_dev_control_cache(self._device['devNo']).get(str(usePort), {}) if portCache.get('status', Const.DEV_WORK_STATUS_IDLE) == Const.DEV_WORK_STATUS_WORKING: Device.update_dev_control_cache( self._device['devNo'], { str(usePort): { 'coins': portCache['coins'] + needCoins, 'status': Const.DEV_WORK_STATUS_WORKING, 'needTime': portCache['needTime'] + needTime, 'isStart': True, 'refunded': False, 'openId': openId, 'startTime': portCache['startTime'] } }) else: Device.update_dev_control_cache( self._device['devNo'], { str(usePort): { 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'status': Const.DEV_WORK_STATUS_WORKING, 'needTime': needTime, 'isStart': True, 'openId': openId, 'refunded': False, 'coins': needCoins, 'vCardId': self._vcard_id } }) return devInfo # 获取设备配置参数 def get_dev_setting(self): devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], "funCode": '0C', '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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) confData = devInfo['data'][6:-2] icMoney = int(confData[0:2], 16) / 10 icTime = int(confData[2:4], 16) * 10 tbTime = int(confData[4:6], 16) * 10 stdPower = int(confData[6:8], 16) * 10 maxPower = int(confData[8:10], 16) * 10 dataDict1 = self.get_param_setting() dataDict2 = {'icMoney': icMoney, 'icTime': icTime, 'tbTime': tbTime, 'stdPower': stdPower, 'maxPower': maxPower} dataDict3 = self.get_dev_consume_count() return dict(dataDict1.items() + dataDict2.items() + dataDict3.items()) # 设置设备参数 def set_dev_setting(self, setConf): data = '' data += fill_2_hexByte(hex(int(setConf['icMoney']) * 10), 2) # 这个改不了, 默认的 data += fill_2_hexByte(hex(int(setConf['icTime']) / 10), 2) data += fill_2_hexByte(hex(int(setConf['tbTime']) / 10), 2) data += fill_2_hexByte(hex(int(setConf['stdPower']) / 10), 2) data += fill_2_hexByte(hex(int(setConf['maxPower']) / 10), 2) devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], "funCode": '08', '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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) # 分析事件参数 def analyze_event_data(self, data): cmdCode = data[4:6] if cmdCode == '03': # 投币上报 coin = int(data[6:8], 16) return {'status': Const.DEV_WORK_STATUS_IDLE, 'cmdCode': cmdCode, 'coin': coin} if cmdCode == '04': # 刷卡上报 money = int(data[6:8], 16) return {'status': Const.DEV_WORK_STATUS_IDLE, 'cmdCode': cmdCode, 'money': money} if cmdCode == '05': # 提交充电结束状态 port = int(data[6:8], 16) leftTime = int(data[8:12], 16) reason = data[12:14] desc = '' if reason == '00': desc = u'电池没有充满!购买的充电时间用完了。每次充满电对您的电池保养有好处哦,建议您在个人中心,找到您正在使用的设备以及端口,付费后续充。' elif reason == '01': desc = u'可能是插头被拔掉,或者电瓶已经充满。系统判断为异常断电,由于电瓶车充电器种类繁多,可能存在误差。如有问题,请您及时联系商家协助解决问题并恢复充电。' elif reason == '02': desc = u'恭喜您!电池已经充满电!' elif reason == '03': desc = u'电池没有充满!设备或端口出现问题,为了安全起见,被迫停止工作。建议您根据已经充电的时间评估是否需要到现场换到其他端口充电。' elif reason == '04': desc = u'过载切断输出。' elif reason == '05': desc = u'警告!您的电池功率超过本机最大限制,已经停止充电,为了公共安全,不建议您在该充电桩充电!提醒您,为了安全,大功率的电池不要放入楼道、室内等位置充电哦。' return {'reasonCode':reason,'status':Const.DEV_WORK_STATUS_IDLE, 'cmdCode':cmdCode, 'port':port, 'leftTime':leftTime, 'reason':desc} elif cmdCode == '0D': port = int(data[6:8], 16) errCode = int(data[8:10], 16) if errCode == '01': return {'status': Const.DEV_WORK_STATUS_FAULT, 'statusInfo': u'端口输出故障', 'cmdCode': cmdCode, 'port': port, 'FaultCode': errCode} elif errCode == '02': return {'status': Const.DEV_WORK_STATUS_FAULT, 'statusInfo': u'机器整体充电功率过大', 'cmdCode': cmdCode, 'port': port, 'FaultCode': errCode} elif errCode == '03': return {'status': Const.DEV_WORK_STATUS_FAULT, 'statusInfo': u'电源故障', 'cmdCode': cmdCode, 'port': port, 'FaultCode': errCode} # 查询当前端口充电状态 def get_port_info(self, line): data = fill_2_hexByte(hex(int(line)), 2) devInfo = MessageSender.send(self.device, self.make_random_cmdcode(), {'IMEI': self._device['devNo'], "funCode": '06', '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::] leftTime = int(data[2:6], 16) if data[6:10] == 'FFFF': power = 0 else: power = int(data[6:10], 16) return {'port': line, 'leftTime': leftTime, 'power': power} # 查询消费总额数据 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::] cardFee = int(data[0:4], 16) coinFee = int(data[4:8], 16) return {'cardFee': cardFee, 'coinFee': coinFee} # 设置IC卡、投币器是否可用 def set_coin_card_enable(self, infoDict): data = '' if infoDict.has_key('putCoins'): data += '01' else: infoDict['putCoins'] = False data += '00' if infoDict.has_key('icCard'): data += '01' else: infoDict['icCard'] = False 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 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": '0A', '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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) 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}}) # 配合stop_charging_port使用 def active_deactive_port(self, port, active): if active: raise ServiceException({'result': 2, 'description': u'该设备不支持直接打开端口'}) 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, 'needTime': 0, 'leftTime': 0, 'endTime': datetime.datetime.now().strftime(Const.DATETIME_FMT)}) newValue = {str(port): portCtrInfo} Device.update_dev_control_cache(self._device['devNo'], newValue) # 远程停止某个端口的使用 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": '0B', 'data': portStr}) 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::] port = int(data[0:2], 16) leftTime = int(data[2:6], 16) return {'port': port, 'leftTime': leftTime} # 个人中心远程停止端口 def stop(self, port = None): infoDict = self.stop_charging_port(port) infoDict['remainder_time'] = infoDict['leftTime'] return infoDict # 设置设备参数 def set_param_setting(self, infoDict): data = '' if infoDict.has_key('autoStop'): if infoDict['autoStop']: data += '01' elif not infoDict['autoStop']: data += '00' else: infoDict['autoStop'] = False data += '00' section = '' if infoDict.has_key('funcParam'): if infoDict['funcParam'] == 0: section += '00' elif infoDict['funcParam'] == 1: section += '01' elif infoDict['funcParam'] == 2: section += '02' elif infoDict['funcParam'] == 3: section += '03' else: section += '00' devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC, {'IMEI': self._device['devNo'], "funCode": '16', 'data': data + '00' + section}) 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({'autoStop': infoDict['autoStop'], 'cardRefund': infoDict['cardRefund'], 'funcParam': infoDict['funcParam']}) 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 get_param_setting(self): try: dev = Device.objects.get(devNo=self._device['devNo']) except Exception, e: logger.error('get dev error = %s' % e) return {'autoStop': False, 'cardRefund': False, 'funcParam': 0} return { 'autoStop': dev['otherConf'].get('autoStop', False), 'cardRefund': dev['otherConf'].get('cardRefund', False), 'funcParam': dev['otherConf'].get('funcParam', 0) } # 设备重启 def set_device_restart(self): devInfo = MessageSender.send(self.device, 220, {'IMEI': self._device['devNo'], "funCode": '20', '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'充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能'}) 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('autoStop'): # autoStop = True if request.POST.get('autoStop') == 'true' else False autoStop = request.POST.get("autoStop", False) lastSetConf.update({'autoStop': autoStop}) self.set_param_setting(lastSetConf) if request.POST.has_key('cardRefund'): # cardRefund = True if request.POST.get('cardRefund') == 'true' else False cardRefund = request.POST.get("cardRefund", False) lastSetConf.update({'cardRefund': cardRefund}) self.set_param_setting(lastSetConf) if request.POST.has_key('reboot'): if request.POST.get('reboot'): self.set_device_restart() def set_device_function_param(self, request, lastSetConf): icMoney = request.POST.get('icMoney', None) icTime = request.POST.get('icTime', None) tbTime = request.POST.get('tbTime', None) stdPower = request.POST.get('stdPower', None) maxPower = request.POST.get('maxPower', None) autoStop = request.POST.get('autoStop', None) funcParam = request.POST.get('funcParam', 0) if icMoney: lastSetConf.update({'icMoney': int(icMoney)}) if icTime: lastSetConf.update({'icTime': int(icTime)}) if tbTime: lastSetConf.update({'tbTime': int(tbTime)}) if stdPower: lastSetConf.update({'stdPower': int(stdPower)}) if maxPower: lastSetConf.update({'maxPower': int(maxPower)}) if autoStop is not None: lastSetConf.update({'autoStop': autoStop}) if funcParam: lastSetConf.update({'funcParam': int(funcParam)}) self.set_dev_setting(lastSetConf) self.set_param_setting(lastSetConf) def get_port_number_from_type_code(self): try: devTypeCode = self.device['devType']['code'] if devTypeCode == Const.DEVICE_TYPE_CODE_CHARGING_LANGXIN: return 10 elif devTypeCode == Const.DEVICE_TYPE_CODE_CHARGING_LANGXIN4: return 4 elif devTypeCode == Const.DEVICE_TYPE_CODE_CHANGING_LANGXIN20: return 20 else: return 10 except Exception as e: return 10 @property def isHaveStopEvent(self): return True