1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- import binascii
- import copy
- import datetime
- import hashlib
- import logging
- import random
- import string
- import struct
- import threading
- import time
- import traceback
- from functools import wraps
- import simplejson as json
- import requests
- from bson import ObjectId
- from django.conf import settings
- from typing import Optional, Dict
- from apilib.monetary import RMB
- from apilib.utils import is_number
- from apilib.utils_datetime import timestamp_to_dt, to_datetime
- from apilib.utils_json import JsonResponse, json_dumps
- from apilib.utils_string import get_random_str, make_title_from_dict
- from apps.web.constant import Const, ErrorCode, DeviceCmdCode, FAULT_CODE, MQTT_TIMEOUT, DeviceErrorCodeDesc, \
- DeviceOnlineStatus, CONSUMETYPE
- from apps.web.core.exceptions import ServiceException, ClientServiceSerialException, ClientServiceTimeOutException, \
- ManagerServiceSerialException, ManagerServiceTimeOutException, TestError, TestTimeOutError, TestSerialError, \
- DeviceNetworkTimeoutError, InvalidParameter
- from apps.web.core.networking import MessageSender
- from apps.web.device.models import Device, Part, DeviceDict, Group, GroupDict
- from apps.web.user.constant2 import StartDeviceType
- from apps.web.user.models import ConsumeRecord, MyUser, Card
- from apps.web.api.models import APIStartDeviceRecord
- from apps.web.user.utils2 import notify_user
- from apps.web.utils import concat_user_center_entry_url, concat_front_end_url, testcase_point
- from apps.web.common.models import ExceptionLog
- from django.core.cache import caches
- logger = logging.getLogger(__name__)
- key = '1c7e3b866ef645760040ddde343601de'
- # imei:电子标签
- # time:一小时内的描述(0~3599)
- # pulseCount:脉冲个数(1~16)
- def encode(imei, time, pulseCount):
- short_time = time & 0xFFFFFFF0 # 一小时内的描述,需要按16模取整
- pulseCount = pulseCount - 1 # 为编码到4位,所以减一
- extend = short_time + pulseCount
- key_ext = key + datetime.datetime.now().strftime("%Y%m%d%H")
- code = hashlib.pbkdf2_hmac('sha1', key_ext.encode('utf-8'), struct.pack(">I", extend).lstrip('\x00'),
- pulseCount + 1, 128)
- md5_obj = hashlib.md5(imei.encode('utf-8'))
- md5_obj.update(code)
- md5_digest = md5_obj.digest()
- md5_digest_head = int(binascii.hexlify(md5_digest[0]), 16)
- digest = md5_digest_head >> 1
- digest = (digest << 12) + extend
- return '{:0>6}'.format(digest)
- def hexbyte_2_bin(hexCode):
- binTemp = bin(int(hexCode, 16))
- needNum = 8 - len(binTemp) + 2
- c = '00000000'
- result = c[0:needNum] + binTemp[2::]
- return result
- def decimal_2_hexByte(decimalCode):
- hexTemp = hex(decimalCode)
- c = '00'
- needNum = 2 - len(hexTemp) + 2
- result = c[0:needNum] + hexTemp[2::]
- return result
- def fill_2_hexByte(hexCode, num = 4, reverse = False):
- hexCode = hexCode.replace('L', '')
- tList = ['0' for _ in range(num)]
- c = ''.join(tList)
- needNum = len(c) + 2 - len(hexCode)
- if needNum <= 0:
- if reverse:
- return reverse_hex(hexCode[2::]).upper()
- return hexCode[2::].upper()
- result = c[0:needNum] + hexCode[2::]
- if reverse:
- return reverse_hex(result).upper()
- return result.upper()
- def pack_float(value, reverse = False):
- """
- 将浮点数转换为16进制转换的 float
- :param value: 浮点数
- :param reverse: 小端 (True)或者 大端
- :return:
- """
- sign = ">f" if not reverse else "<f"
- return struct.pack(sign, value).encode("hex")
- def asc_to_string(ascString):
- result = ''
- for ii in range(len(ascString) / 2):
- char = int(ascString[ii * 2:ii * 2 + 2], 16)
- if char == 0:
- break
- result += chr(char)
- return result
- # 将字符串转为asc码形式的字符串
- def string_to_ascstring(strIn, needLenth=-1):
- result = ''
- for a in strIn:
- result += str(hex(ord(a))).replace("0x", "")
- if needLenth > len(result):
- for ii in range(needLenth-len(result)):
- result += '0'
- return result.upper()
- def unpack_float(value, reverse = False):
- """
- 解析16进制浮点数
- :param value: 16进制浮点数
- :param reverse:小端 (True)或者 大端
- :return:
- """
- sign = ">f" if not reverse else "<f"
- return struct.unpack(sign, value.decode("hex"))[0]
- def reverse_hex(hexNum):
- newHexNum = ""
- while hexNum:
- newHexNum += hexNum[-2:]
- hexNum = hexNum[: -2]
- return newHexNum
- def calc_signal_from_rssi(rssi):
- if rssi < 70:
- return 31
- elif rssi < 85:
- return 20
- else:
- return 5
- class ChargingMixin(object):
- def active_deactive_port(self):
- raise NotImplementedError()
- def lock_unlock_port(self, port, lock = True):
- raise NotImplementedError()
- class _RequestMixin(object):
- def get(self, url, data = None, **kwargs):
- return self._request(
- method = 'get',
- url = url,
- data = data,
- **kwargs)
- def post(self, url, data = None, **kwargs):
- return self._request(
- method = 'post',
- url = url,
- data = data,
- **kwargs
- )
- def _decode_result(self, res):
- try:
- import simplejson as json
- result = json.loads(res.content.decode('utf-8', 'ignore'), strict = False)
- except (TypeError, ValueError):
- return res
- return result
- def _request(self, method, url, data = None, headers = None, **kwargs):
- with requests.sessions.Session() as session:
- if headers:
- headers.update({
- 'Content-Type': 'application/json'
- })
- else:
- headers = {
- 'Content-Type': 'application/json'
- }
- if data:
- body = json_dumps(data)
- body = body.encode('utf-8')
- kwargs['data'] = body
- res = session.request(
- url = url,
- method = method,
- headers = headers,
- **kwargs)
- try:
- res.raise_for_status()
- except requests.RequestException as reqe:
- return {
- 'errcode': 'EXCEPTION',
- 'errmsg': str(reqe)
- }
- return self._decode_result(res)
- class AsyncStartDevice(threading.Thread, _RequestMixin):
- def __init__(self, deviceAdapter, record):
- # type: (SmartBox, APIStartDeviceRecord)->None
- super(AsyncStartDevice, self).__init__()
- self.deviceAdapter = deviceAdapter
- self.record = record # type:APIStartDeviceRecord
- def notify_api_client(self, url, **kwargs):
- try:
- res = self.post(url = url, data = kwargs, timeout = 15)
- logger.debug(str(res))
- except Exception as e:
- logger.exception(e)
- def run(self):
- logger.info(repr(self.record))
- notify_payload = {
- 'sign': self.record.apiConf['mySign'],
- 'extOrderNo': self.record.orderNo,
- 'deviceCode': self.record.deviceCode,
- 'channel': self.record.channel,
- 'createTime': self.record.createTime,
- 'finishedTime': datetime.datetime.now(),
- 'errcode': ErrorCode.EXCEPTION,
- 'errmsg': u'系统异常'
- }
- err_code = ErrorCode.EXCEPTION
- err_msg = u'系统异常'
- try:
- device_result = self.deviceAdapter.start(package = self.record.package,
- openId = self.record.userId,
- attachParas = self.record.attachParas)
- err_code = device_result['rst']
- err_msg = device_result.get('desc', '')
- except ServiceException as e:
- logger.error(str(e))
- err_code = e.result.get('result')
- err_msg = e.result.get('description')
- except Exception as e:
- logger.exception(e)
- err_code = ErrorCode.EXCEPTION
- err_msg = e.message
- finally:
- try:
- self.record.errCode = err_code
- self.record.errMsg = err_msg
- self.record.save()
- except Exception as e:
- logger.exception(e)
- notify_payload.update({
- 'errcode': err_code,
- 'errmsg': u'系统异常' if err_code == ErrorCode.EXCEPTION else err_msg
- })
- return self.notify_api_client(url = self.record.notifyUrl, **notify_payload)
- class SmartBox(object):
- def __init__(self, device):
- # type: (DeviceDict)->None
- self._device = device
- self._vcard_id = None # 启动方式,None,表示扫码直接启动,如果是虚拟卡,表示用虚拟卡启动,如果是卷也是的
- super(SmartBox, self).__init__()
- @property
- def device(self):
- return self._device
- def check_dev_status(self, attachParas = None):
- pass
- def get_dev_info(self):
- pass
- def check_alarm(self, alarm):
- return ''
- def set_vcard_id(self, vcard_id):
- self._vcard_id = vcard_id
- def test(self, coins):
- raise NotImplementedError(u'设备未实现 `test`')
- def serial_test(self, payload):
- """
- 串口测试函数
- :param payload: 根据个性化配置不同 传入参数也不同
- :return:
- """
- raise NotImplementedError(u"该设备不支持")
- def start_device(self, package, openId, attachParas):
- raise NotImplementedError('cannot call `start_device` of base class SmartBox')
- def start(self, packageId, openId=None, attachParas={}):
- washConfig = self.device.get("washConfig", dict())
- package = washConfig.get(packageId)
- if not package:
- raise ServiceException({"result": 2, "description": u"未找到套餐,请联系经销商!"}) # new
- if self.device.bill_as_service_feature.support and self.device.bill_as_service_feature.on:
- return self.bill_as_service_start_device(package, openId, attachParas)
- else:
- return self.start_device(package, openId, attachParas)
- def start_temp_use(self, packageId, openId = None, attachParas = {}):
- package = self._device['tempWashConfig'].get(packageId)
- return self.start_device(package, openId, attachParas)
- def start_from_api(self, record):
- # type: (APIStartDeviceRecord)->Optional[Dict]
- if record.notifyUrl:
- return AsyncStartDevice(self, record).start()
- else:
- return self.start_device(package = record.package,
- openId = record.userId,
- attachParas = copy.deepcopy(record.attachParas))
- # 基础的停止接口,返回的是单位是分钟.此接口单独放到业务做,目的是有的设备结算按照分钟,有的设备按照秒。这样统一由业务决定
- def stop(self, port = None):
- devInfo = MessageSender.send(self.device, DeviceCmdCode.STOP_DEVICE, {'IMEI': self._device['devNo']})
- if devInfo.get('rst', 0) != 0:
- return JsonResponse({"result": 0, "description": u'网络连接异常,停止设备失败,请您重新尝试停掉设备', "payload": ''})
- devInfo['remainder_time'] = int(devInfo['remainder_time'] / 60.0)
- return devInfo
- def calc_stop_back_coins(self, totalFee, remainderTime, totalTime):
- refundFee = round(totalFee * (float(remainderTime) / totalTime), 2)
- if refundFee > totalFee:
- refundFee = totalFee
- if refundFee <= 0.0:
- refundFee = 0.00
- return refundFee
- def remote_charge_card(self, price, rechargeRecord = None):
- raise NotImplementedError(u'设备未实现 `remote_charge_card`')
- def recharge_card(self, cardNo, money, orderNo = None):
- # type:(str,RMB,str)->(dict, RMB)
- raise NotImplementedError(u'设备未实现 `recharge_ic_card`')
- def recharge_ic_card_realiable(self, cardNo, money, order_no):
- # type:(str,RMB,str)->dict
- raise NotImplementedError(u'设备未实现 `recharge_ic_card_realiable`')
- def response_card_balance(self, cardNo, balance):
- raise NotImplementedError(u'设备未实现 `response_card_balance`')
- def response_use_card(self, res, leftBalance, uartId = None):
- raise NotImplementedError(u'设备未实现 `response_use_card`')
- def get_dev_consume_count(self):
- pass
- def stop_charging_port(self, port):
- pass
- # 访问设备,获取设备信息
- def getDevInfo(self):
- pass
- # 解析获取设备信息的返回报文
- def analyze_event_data(self, data):
- return data
- def active_deactive_port(self, port, active):
- raise ServiceException({'result': 2, 'description': u'此设备不支持直接打开或者管理端口'})
- def lock_unlock_port(self, port, lock = True):
- raise ServiceException({'result': 2, 'description': u'此设备不支持直接禁用、解禁端口'})
- def is_port_can_use(self, port, canAdd=False):
- """
- 端口是否可用,如果canAdd为True,表示可以追加钱
- :param port:
- :param canAdd:
- :return:
- """
- try:
- portDict = self.get_port_status() # type: Optional[dict, None]
- if portDict is None:
- return True, ''
- port = str(port)
- if port in portDict:
- if portDict[port]['status'] == Const.DEV_WORK_STATUS_IDLE:
- return True, ''
- elif portDict[port]['status'] == Const.DEV_WORK_STATUS_FAULT:
- return False, u'该线路故障,暂时不能使用,请您使用其他线路'
- elif portDict[port]['status'] == Const.DEV_WORK_STATUS_WORKING:
- if canAdd:
- return True, ''
- return False, u'该线路正在工作,暂时不能继续使用,请您使用其他线路,或者等待该线路工作完毕'
- elif portDict[port]['status'] == Const.DEV_WORK_STATUS_FORBIDDEN:
- return False, u'该线路已被禁止使用,请您使用其他线路'
- elif portDict[port]['status'] == Const.DEV_WORK_STATUS_CONNECTED:
- return True, u''
- else:
- return False, u'线路未知状态,暂时不能使用'
- return False, u'未知端口,无法使用'
- except ServiceException, e:
- return False, e.result.get('description')
- except Exception, e:
- logger.error('error = %s' % e)
- return False, u'获取端口状态失败'
- def press_down_key(self, keyName):
- pass
- def get_device_function_by_key(self, keyName):
- return ''
- def get_port_status(self, force = False):
- return None
- def dealer_get_port_status(self):
- """
- 远程上分的时候获取端口状态
- :return:
- """
- return self.get_port_status()
- def get_dev_setting(self):
- return None
- def set_device_function(self, request, lastSetConf):
- pass
- def set_device_function_param(self, request, lastSetConf):
- pass
- def get_server_setting(self):
- return None
- def set_server_setting(self, payload):
- pass
- def count_down(self, request, dev, agent, group, devType, lastOpenId):
- return None
- def set_dev_fault(self, fault):
- pass
- def set_dev_disable(self, disable):
- pass
- def get_port_static_info(self, portDict):
- allPorts, usedPorts = 0, 0
- for v in portDict.values():
- allPorts += 1
- if (v.has_key('isStart') and v['isStart']) or (
- v.has_key('status') and v['status'] != Const.DEV_WORK_STATUS_IDLE):
- usedPorts += 1
- return allPorts, usedPorts, allPorts - usedPorts
- def make_random_cmdcode(self):
- return random.randint(DeviceCmdCode.RANDOM_START_CODE, DeviceCmdCode.RANDOM_END_CODE)
- def get_port_status_from_dev(self):
- raise NotImplementedError(u'设备未实现 `get_port_status_from_dev`')
- def get_part_info(self):
- return {}
- def get_port_info(self, port):
- return {}
- def async_update_portinfo_from_dev(self):
- class Sender(threading.Thread):
- def __init__(self, smartBox):
- super(Sender, self).__init__()
- self._smartBox = smartBox
- def run(self):
- try:
- result = self._smartBox.get_port_status_from_dev()
- # 将端口数据入库刷新到部件表中
- portFromDev = result.keys()
- portInDb = [obj.partNo for obj in
- Part.objects.filter(logicalCode = self._smartBox._device['logicalCode'],
- partName = 'port')]
- needAddPartNo = list(set(portFromDev) - set(portInDb))
- needAddPart = [{'logicalCode': self._smartBox._device['logicalCode'],
- 'ownerId': self._smartBox._device['ownerId'],
- 'partNo': portNo, 'partName': 'port',
- 'dateTimeAdded': datetime.datetime.now(),
- 'dateTimeUpdated': datetime.datetime.now()} for portNo in needAddPartNo]
- if needAddPart:
- Part.get_collection().insert(needAddPart)
- needRemovePartNo = list(set(portInDb) - set(portFromDev))
- Part.objects(logicalCode = self._smartBox._device['logicalCode'], partNo__in = needRemovePartNo,
- partName = 'port').delete()
- # 将其他部件入库到部件表中(目前支持其他部件的只有我们自己的一体板)
- partItems = self._smartBox.get_part_info()
- for partName, partInfo in partItems.items():
- if not partInfo['SN']:
- continue
- Part.upsert_part(logicalCode = self._smartBox._device['devNo'],
- ownerId = self._smartBox._device['ownerId'], partNo = partInfo['SN'],
- partName = partName, partType = '9999')
- except ServiceException:
- return
- except Exception as e:
- logger.exception(e)
- sender = Sender(self)
- sender.start()
- def format_port_using_detail(self, detailDict):
- portData = {}
- startTimeStr = detailDict.get('startTime', None)
- if startTimeStr is not None and "usedTime" not in detailDict:
- startTime = to_datetime(startTimeStr)
- usedTime = int(round((datetime.datetime.now() - startTime).total_seconds() / 60.0))
- portData['usedTime'] = usedTime
- elif detailDict.get("usedTime"):
- usedTime = detailDict.get("usedTime")
- portData['usedTime'] = usedTime
- else:
- usedTime = None
- if detailDict.has_key('needTime'):
- if detailDict['needTime'] == 999:
- portData['needTime'] = u'充满自停'
- else:
- portData['needTime'] = detailDict['needTime']
- if detailDict.has_key('leftTime') and (usedTime > 0):
- if detailDict['leftTime'] == 65535:
- portData['leftTime'] = 65535
- detailDict['usedTime'] = 0
- detailDict['actualNeedTime'] = 0
- else:
- portData['actualNeedTime'] = int(detailDict['leftTime']) + int(usedTime)
- if detailDict.has_key('needTime') and portData['actualNeedTime'] > detailDict['needTime']:
- portData['actualNeedTime'] = portData['needTime']
- portData['leftTime'] = detailDict['leftTime']
- if detailDict.has_key('coins'):
- if self.device.is_auto_refund:
- portData['leftMoney'] = round(
- float(detailDict['coins']) * int(detailDict['leftTime']) / (
- int(detailDict['leftTime']) + int(usedTime)), 2)
- portData['consumeMoney'] = round(
- float(detailDict['coins']) * int(portData['usedTime']) / (
- int(detailDict['leftTime']) + usedTime), 2)
- elif detailDict.has_key('leftTime'):
- portData['leftTime'] = detailDict['leftTime']
- if (not detailDict.has_key('leftTime')) and (usedTime is not None):
- if detailDict.has_key('needTime'):
- if isinstance(detailDict['needTime'], (int, float)):
- portData['leftTime'] = detailDict['needTime'] - usedTime
- if detailDict.has_key('coins') and float(detailDict['coins']) != 0: # 只有支持退费的设备才显示可退费数据
- if self.device.is_auto_refund:
- portData['leftMoney'] = round(
- float(detailDict['coins']) * portData['leftTime'] / detailDict['needTime'], 2)
- portData['consumeMoney'] = round(
- float(detailDict['coins']) * portData['usedTime'] / detailDict['needTime'], 2)
- if detailDict.has_key('openId'):
- user = MyUser.objects(openId=detailDict['openId'], groupId=self.device.groupId).first()
- if user:
- portData['nickName'] = user.nickname
- if detailDict.has_key('cardId'):
- if not detailDict.has_key('consumeType'):
- portData['consumeType'] = 'card'
- card = Card.objects.get(id=ObjectId(detailDict['cardId']))
- if card.cardName:
- portData['cardName'] = card.cardName
- portData['cardNo'] = card.cardNo
- # 注意,如果是IC卡,不支持余额回收,这里也不要显示出来
- if card.cardType == 'IC' and portData.has_key('leftMoney'):
- portData.pop('leftMoney')
- elif 'openId' in detailDict and ('consumeType' not in detailDict):
- if detailDict.get('vCardId'):
- portData['consumeType'] = 'mobile_vcard'
- else:
- portData['consumeType'] = 'mobile'
- elif 'consumeType' in detailDict:
- if detailDict['consumeType'] == 'coin':
- portData['consumeType'] = 'coin' # 硬币的都无法退费
- if portData.has_key('leftMoney'):
- portData.pop('leftMoney')
- elif detailDict['consumeType'] == 'server':
- portData['consumeType'] = 'mobile'
- # 做个特殊处理
- if portData.has_key('needTime'):
- if portData['needTime'] == '999' or portData['needTime'] == '充满自停':
- portData['needTime'] = u'充满自停'
- portData.pop('leftTime', None)
- else:
- portData['needTime'] = u'%s分钟' % portData['needTime']
- # 如果剩余时间为65535,表示未接插头
- if portData.has_key('leftTime') and portData['leftTime'] == 65535:
- portData['leftTime'] = u'(线路空载)'
- portData['usedTime'] = 0
- portData['needTime'] = 0
- detailDict.update(portData)
- for k, v in detailDict.items():
- if v < 0:
- detailDict.pop(k)
- # 因为前台显示的开始时间如果带年,就显示不下,这里做个切割
- if detailDict.has_key('startTime') and detailDict['startTime'].count('-') == 2:
- detailDict['startTime'] = to_datetime(detailDict['startTime']).strftime('%m-%d %H:%M:%S')
- return detailDict
- def get_port_using_detail(self, port, ctrInfo, isLazy=False):
- """
- 获取设备端口的详细信息
- :param port:
- :param ctrInfo:
- :param isLazy: 是否延时加载设备信息
- :return:
- """
- detailDict = ctrInfo.get(str(port), {})
- try:
- if isLazy: # 需要点击再次点击按钮加载
- portInfo = {'isLazy': True}
- else:
- portInfo = self.get_port_info(str(port))
- skipPipelineProcessing = portInfo.get('skipPipelineProcessing', False)
- if skipPipelineProcessing:
- detailDict.update(portInfo)
- return detailDict
- # 有的主机报的信息leftTime错误
- if portInfo.has_key('leftTime') and portInfo['leftTime'] < 0:
- portInfo.pop('leftTime')
- detailDict.update(portInfo)
- except Exception, e:
- logger.exception('get port info from dev=%s err=%s' % (self.device.devNo, e))
- return detailDict
- return self.format_port_using_detail(detailDict)
- def support_count_down(self, openId = None, port = None):
- """
- 是否支持倒计时界面
- :return:
- """
- return False
- def get_duration(self, package):
- if 'time' not in package or not package['time']:
- return 0
- unit = package.get('unit', u'分钟')
- reserved_time = float(package['time'])
- if unit == u'秒':
- reserved_time = int(reserved_time)
- elif unit == u'分钟':
- reserved_time = int(reserved_time * 60)
- elif unit == u'小时':
- reserved_time = int(reserved_time * 60 * 60)
- elif unit == u'天':
- reserved_time = int(reserved_time * 24 * 60 * 60)
- else:
- # 其他都算分钟吧
- reserved_time = int(reserved_time * 60)
- return reserved_time
- def translate_funcode(self, funCode):
- return ''
- def translate_event_cmdcode(self, cmdCode):
- return ''
- def translate_smartbox_cmd(self, cmdParas):
- cmd = str(cmdParas['cmd'])
- descList = []
- temp = Const.CMD_CMD_TRANSLATE_DICT.get(cmd, '')
- if temp:
- descList.append(temp)
- if cmdParas.has_key('funCode'):
- temp = self.translate_funcode(cmdParas['funCode'])
- if temp:
- descList.append(temp)
- return ' '.join(descList)
- def translate_server_cmd(self, cmdParas):
- descList = []
- for k, v in cmdParas.items():
- if k == 'cmd':
- temp = Const.CMD_CMD_TRANSLATE_DICT.get(v, '')
- if temp:
- descList.append(temp)
- elif k in Const.CMD_PARAS_TRANSLATE_DICT:
- temp = Const.CMD_PARAS_TRANSLATE_DICT.get(k, '')
- if temp:
- descList.append('%s:%s' % (temp, v))
- elif k == 'data' and v:
- dataDict = self.analyze_event_data(v)
- if dataDict.has_key('cmdCode'):
- cmdDesc = self.translate_event_cmdcode(dataDict['cmdCode'])
- descList.append(cmdDesc)
- for dataKey, dataValue in dataDict.items():
- if dataKey == 'cmdCode':
- continue
- dataDesc = Const.CMD_DATA_TRANSLATE_DICT.get(dataKey)
- if dataDesc:
- temp = '%s:%s' % (dataDesc, dataValue)
- descList.append(temp)
- return ' '.join(descList)
- # duration 单位:秒
- def send_dev_runtime(self, openId, duration):
- dev = self._device
- now_time = datetime.datetime.now()
- devInfo = MessageSender.send(dev, DeviceCmdCode.PAY_MONEY, {'IMEI': dev['devNo'], 'duration': duration,
- 't': int(time.mktime(now_time.timetuple()))})
- if devInfo.has_key('rst') and devInfo['rst'] != 0:
- current_dev_type_name = dev['devType']['name']
- if current_dev_type_name == u'其他':
- current_dev_type_name = u'自助设备'
- if devInfo['rst'] == -1:
- description = u'当前' + current_dev_type_name + u'正在玩命找网络,您的金币还在,重试不会扣款,建议您试试旁边其他设备,或者试试投硬币,或者稍后再试哦'
- raise ServiceException({'result': 2, 'description': description})
- elif devInfo['rst'] == 1:
- description = u'当前' + current_dev_type_name + u'正在忙,无响应,您的金币还在,重试不会扣款,请试试其他线路,或者请稍后再试哦'
- raise ServiceException({'result': 2, 'description': description})
- else:
- description = u'系统无响应'
- raise ServiceException({'result': 2, 'description': description})
- start_timestamp = int(time.time())
- devInfo['finishedTime'] = start_timestamp + duration
- Device.update_dev_control_cache(dev['devNo'],
- {
- 'openId': openId,
- 'startTime': timestamp_to_dt(start_timestamp).strftime('%Y-%m-%d %H:%M:%S'),
- 'needTime': duration,
- 'status': Const.DEV_WORK_STATUS_WORKING,
- 'finishedTime': devInfo['finishedTime']
- })
- return devInfo
- @property
- def isHaveStopEvent(self):
- return False
- def notify_low_power_to_user(self, user, dealer, port, delay, lowPower = None):
- """
- 用户的低功率检测 设备启动之后 延后一段时间直接对设备的当前的端口功率进行 主板查询 如果查询结果小于一定值 告警用户
- :param user: 用户
- :param dealer: 经销商( 无用参数 需要干掉或者删除)
- :param port: 延迟检测的端口
- :param delay: 延迟秒数
- :param lowPower: 检测的功率 可传参,如果为空 直接从设备侧获取
- :return:
- """
- from taskmanager.mediator import task_caller
- if lowPower is None:
- lowPower = self.device.get("otherConf", dict()).get("lowPowerDetectionPower", 0)
- managerialOpenId = user.managerialOpenId
- task_caller(
- "report_to_user_low_power",
- delay = delay,
- devNo = self.device.devNo,
- line = port,
- power = lowPower,
- managerialOpenId = managerialOpenId,
- dealerId = self.device.ownerId
- )
- def record_serial_port_for_timeout(self):
- pass
- # 检查串口超时,电川的板子出现启动命令串口超时,但是获取端口状态却正常。(现网已经抓到几个案例,此函数用于规避止血,但是并不能完全解决)
- # 只能解决获取端口状态命令OK,但是启动命令不行的这种情况。
- def check_serial_port_for_startcmd(self, port):
- # 首先记录到数据库表,用于查询现网问题,然后进行分析
- try:
- portsInfo = self.get_port_status_from_dev()
- ExceptionLog.log(
- user = self.device.devNo, exception = '',
- extra = {
- 'action': 'check_serial_port_for_startcmd:get_port_status_from_dev',
- 'result': 'success', 'portsInfo': str(portsInfo)})
- except Exception as e:
- ExceptionLog.log(user = self.device.devNo, exception = traceback.format_exc(),
- extra = {
- 'action': 'check_serial_port_for_startcmd:get_port_status_from_dev',
- 'result': 'failure'
- })
- raise e
- portStatus = portsInfo.get(str(port), {}).get('status', Const.DEV_WORK_STATUS_IDLE)
- if portStatus == Const.DEV_WORK_STATUS_WORKING: # 如果端口处于忙的状态,默认命令成功,会扣费
- return
- raise ServiceException(
- {'result': 2, 'description': u'设备忙无响应。本次操作没有扣除您的金额,您可以稍后重试或者试试附近其他设备。'})
- def handle_out_start_error(self, port):
- """
- 处理 启动错误 超过次数上限
- :param port: 端口
- :return:
- """
- pass
- def handle_clear_start_error(self, port):
- """
- 清除启动错误之后 的后续操作
- :param port: 端口
- :return:
- """
- pass
- @property
- def show_pay_unit(self):
- """
- 前台显示付费的时候,目前有不同的客户希望 显示不同的单位 有的显示金币 有的显示元, 这个地方处理下
- :return:
- """
- return u"元"
- def get_many_port_info(self, portList):
- return None
- def check_order_state(self, openId):
- return
- def start_device_realiable(self, order):
- # type:(ConsumeRecord)->dict
- raise NotImplementedError('cannot call `start_device_realiable` of base class SmartBox')
- def calc_elec_fee(self, spend_elec):
- group = Group.objects.get(id = self.device['groupId'])
- return float(group.otherConf.get('elecFee', 0)) * spend_elec
- def isHaveCallback(self):
- return False
- def do_callback(self, *args, **kwargs):
- """
- 针对状态机确认成功付款后的状态回调使用
- """
- raise NotImplementedError(u'设备未实现 `dev_callback`')
- @staticmethod
- def check_device_features(device_features, no_features_to_return = None):
- def warpper(func):
- @wraps(func)
- def inner(self, *args, **kwargs):
- retult = False
- features = self.device.devType.get("features", {})
- if isinstance(device_features, (list, tuple)):
- for item in device_features:
- if features.get(item) and features.get(item) is True:
- retult = True
- break
- elif isinstance(device_features, dict):
- if set(device_features.items()) & set(features.items()):
- retult = True
- elif isinstance(device_features, (str, unicode, int)):
- if features.get(device_features) and features.get(device_features) is True:
- retult = True
- if retult:
- return func(self, *args, **kwargs)
- else:
- return no_features_to_return
- return inner
- return warpper
- @staticmethod
- def life_cycle(before=lambda self, *args, **kwargs: None, after=lambda *args: args):
- def warpper(func):
- @wraps(func)
- def inner(self, *args, **kwargs):
- before(self, *args, **kwargs)
- data = func(self, *args, **kwargs)
- result = after(self, data)
- return result or data
- return inner
- return warpper
- def get_signal(self):
- result = MessageSender.send(device = self._device, cmd = DeviceCmdCode.GET_DEVINFO,
- payload = {'IMEI': self._device['devNo']},
- timeout = MQTT_TIMEOUT.SHORT)
- return result
- def notify_user(self, order, cardStart = False):
- pass
- def deal_order_money(self, order):
- # type: (ConsumeRecord) -> ConsumeRecord
- raise NotImplementedError(u'设备未实现 `deal_order_money`')
- @property
- def support_monthly_package(self):
- return False
- def _notify_user(self, user, templateName, url = None, **kwargs):
- if not user or not user.managerialOpenId:
- logger.warning('user is none or managerialOpenId is not exists.')
- return
- from taskmanager.mediator import task_caller
- task_caller('send_msg_to_user_via_wechat', productId = user.productAgentId, openId = user.managerialOpenId,
- templateName = templateName, url = url, **kwargs)
- def notify_service_start_to_user(self, order, user=None):
- # type: (ConsumeRecord, MyUser)->None
- if not order.user:
- return
- dealer = self.device.owner
- if dealer.showServicePhone:
- remark = u'感谢您的使用!有任何问题请联系客服(联系电话{})!'.format(dealer.service_phone)
- else:
- remark = u'感谢您的使用!有任何问题请联系客服!'
- if order.port != Const.NO_PORT:
- service = unicode(
- '{}({}/{}-端口{})'.format(self.device.majorDeviceType, self.device.group.address[0:32],
- self.device.logicalCode,
- order.port))
- else:
- service = unicode(
- '{}({}/{})'.format(self.device.majorDeviceType, self.device.group.address[0:32],
- self.device.logicalCode))
- kw = {
- 'WechatUserManagerApp': {
- 'title': u'尊敬的用户,设备已经成功启动',
- 'service': service,
- 'time': order.device_start_time,
- 'remark': remark
- },
- 'WechatUserSubscribeManagerApp': {
- 'title': u'尊敬的用户,设备已经成功启动',
- 'service': service,
- 'time': order.device_start_time,
- 'remark': remark
- }
- }
- user = order.user if not user else user # type: MyUser
- if not notify_user:
- logger.warning('user<openId={},groupId={}> is not exists.'.format(order.openId, order.groupId))
- return
- url = self.custom_push_url(order, user)
- self._notify_user(user, 'service_start', url = url, **kw)
- def notify_service_end_to_user(self, order, url=None): # type:(ConsumeRecord, Optional[str, None]) -> None
- service_name = order.package.name or u"充电"
- title_list = [
- {u'': u'[{}]结束,感谢您的使用!'.format(service_name)},
- {u'设备编号': self.device.logicalCode}
- ]
- port = order.port
- if port:
- title_list.extend([{u'设备端口': port}, {u'设备地址': order.address}])
- else:
- title_list.extend([{u'设备地址': order.address}])
- reason = order.service.reason
- if reason:
- title_list.extend([{u'结束原因': reason}])
- dealer = order.owner
- if dealer.showServicePhone:
- remark = u'客服联系电话:{}'.format(dealer.service_phone)
- else:
- remark = u'我们竭诚为您服务,有任何问题请联系客服!'
- finished_time = order.device_end_time
- if not finished_time:
- finished_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
- agent = dealer.my_agent
- device = order.device
- if agent.customizedUserSubGzhAllowable:
- if port:
- service = u"{}服务({}-端口{})".format(device.majorDeviceType, device.logicalCode, order.port)
- if len(service) > 20:
- service = u'{}服务({})'.format(device.majorDeviceType, device.logicalCode)
- if len(service) > 20:
- service = u'{}服务'.format(device.majorDeviceType)
- else:
- service = u'{}服务({})'.format(service_name, device.logicalCode)
- if len(service) > 20:
- service = u'{}服务'.format(device.majorDeviceType)
- kw = {
- 'service': service,
- 'finishTime': finished_time,
- 'remark': remark,
- }
- else:
- kw = {
- 'title': make_title_from_dict(title_list),
- 'service': u'{}服务'.format(service_name),
- 'finishTime': finished_time,
- 'remark': remark
- }
- kw.update({'url': url or settings.SERVER_END_BASE_SSL_URL + '/user/index.html?v=1.0.31#/user/consume'})
- notify_user(order.openId, order.ownerId, 'service_complete', **kw)
- def stop_by_order(self, port, orderNo):
- raise NotImplementedError(u'设备未实现 `stop_by_order`')
-
- def isHaveFaultHandle(self):
- return False
-
- def faultHandle(self, **kw):
- raise NotImplementedError(u'设备未实现 `FaultHandle`')
- def format_upload_power(self, power):
- return power
-
- def force_stop_order(self,order, **kwargs):
- raise NotImplementedError(u'设备未实现 `force_stop_order`')
- def get_ports_info(self, **kw):
- raise NotImplementedError(u'设备未实现 `get_ports_info`')
- def switch_bill_as_service(self, onOrOff):
- Device.get_collection().update_one(
- {'devNo': self.device.devNo},
- {
- '$set':
- {
- 'devType.features.billAsService.on': onOrOff
- }
- })
- Device.invalid_device_cache(self.device.devNo)
- def custom_push_url(self, order, user, **kw):
- return concat_user_center_entry_url(agentId=user.productAgentId, redirect=concat_front_end_url(
- uri='/user/index.html#/user/consumeDetail?id={}'.format(str(order.id))))
- def bill_as_service_start_device(self, package, openId, attachParas):
- charge_unit = self.device.bill_as_service_feature.charge_unit
- price = package['price']
- elecCharge = charge_unit["elecCharge"]
- serviceCharge = charge_unit["serviceCharge"]
- unitPrice = float(elecCharge) + float(serviceCharge)
- actualTime = round(price / unitPrice, 2)
- package['time'] = actualTime
- return self.start_device(package, openId, attachParas)
- def prepare_package(self, packageId, attachParas, startType=StartDeviceType.ON_LIEN):
- """
- 预备套餐模型 普通设备直接由设备解释
- """
- isTemporary = 'isTemporary' in attachParas and attachParas['isTemporary'] is True
- package = self.device.package(packageId, isTemporary=isTemporary)
- return package
- def prepare_serviced_info(self, package, attach_paras): # type:(dict, dict)->dict
- rv = {'chargeIndex': attach_paras['chargeIndex']}
- return rv
- def get_service_fee_info(self):
- ruleList = []
- for packageId, rule in self.device['washConfig'].items():
- item = {
- 'id': packageId,
- 'name': rule['name'],
- 'coins': rule['coins'],
- 'price': rule.get('price', rule['coins']),
- 'time': rule.get('time', 20),
- 'description': rule.get('description', ''),
- 'imgList': rule.get('imgList', []),
- 'unit': rule.get('unit', u'分钟'),
- 'switch': rule.get('switch', True),
- }
- if 'sn' in rule:
- item['sn'] = rule['sn']
- ruleList.append(item)
- billAsService = self.device.bill_as_service_feature
- devData = {
- 'id': self.device.devNo,
- 'isManager': True,
- 'groupName': self.device.group['groupName'],
- 'groupNumber': self.device['groupNumber'],
- 'devNo': self.device.devNo,
- 'devTypeName': self.device.devTypeName,
- 'devTypeCode': self.device.devTypeCode
- }
- if "displaySwitchs" in self.device.my_obj.otherConf:
- displaySwitchs = self.device.my_obj.otherConf.get('displaySwitchs')
- else:
- displaySwitchs = {
- 'displayCoinsSwitch': True,
- 'displayTimeSwitch': True,
- 'displayPriceSwitch': True,
- "setPulseAble": False,
- "setBasePriceAble": False
- }
- ruleList = sorted(ruleList, key=lambda x: (x.get('sn'), x.get('id')))
- return {
- 'ruleList': ruleList,
- 'billAsService': billAsService,
- 'devData': devData,
- 'displaySwitchs': displaySwitchs,
- }
- def set_service_fee_info(self, payload):
- elecCharge = round(float(payload['billAsService'].get('elecCharge', 0.5)), 2)
- serviceCharge = round(float(payload['billAsService'].get('serviceCharge', 0.5)), 2)
- # 显示展示给用户信息部分
- if payload.get('displaySwitchs'):
- displaySwitchs = payload.get('displaySwitchs')
- else:
- displaySwitchs = {'displayCoinsSwitch': False,
- 'displayTimeSwitch': True,
- 'displayPriceSwitch': True,
- 'setPulseAble': False,
- 'setBasePriceAble': False}
- # 套餐部分
- packages = payload.get('packages')
- # 调整SN套餐顺序
- for i, item in enumerate(packages):
- item['sn'] = i
- # 套餐id 去重
- existIds = list(map(lambda _: _.get('id'), packages))
- washConfig = {}
- for rule in packages:
- if 'price' in rule:
- if not is_number(rule['price'] and is_number(rule.get('time', 0))):
- raise InvalidParameter(u'金币数目或者时间或者价格必须是数字')
- if RMB(rule['price']) >= self.device.owner.maxPackagePrice:
- raise InvalidParameter(u'套餐金额超限')
- if len(rule['name']) > 20:
- raise InvalidParameter(u'套餐名字只能取1-20位')
- if 'id' in rule:
- ruleId = rule['id']
- else:
- ruleId = list(set(range(1, 71)) - set([int(ruleId) for ruleId in washConfig.keys()]) - set(existIds))[0]
- washConfig[str(ruleId)] = {
- 'billingMethod': CONSUMETYPE.BILL_AS_SERVICE,
- 'name': rule['name'],
- 'coins': float(rule['price']),
- 'price': float(rule['price']),
- 'time': float(rule.get('time', 0)),
- 'description': rule.get('description', ''),
- 'imgList': rule.get('imgList', []),
- 'unit': rule.get('unit', u'分钟'),
- 'switch': rule.get('switch', True),
- 'sn': rule.get('sn')
- }
- self.device.update_device_obj(**{
- 'washConfig': washConfig,
- 'otherConf.displaySwitchs': displaySwitchs,
- 'devType.features.billAsService.elecCharge': elecCharge,
- 'devType.features.billAsService.serviceCharge': serviceCharge
- })
- dealer = self.device.owner
- dealer.defaultWashConfig[self.device.devTypeId] = self.device['washConfig'].values()
- dealer.save()
-
- def start_device_swap(self, portNo):
- # type:(ConsumeRecord)->dict
- raise NotImplementedError('cannot call `start_device_swap` of base class SmartBox')
- @property
- def support_device_package(self):
- return False
- def get_reg_model(self, dealer, devTypeId, isTemp=False, **kw):
- payload = {}
- if devTypeId in dealer.defaultWashConfig:
- payload = dealer.defaultWashConfig[devTypeId]
- return payload
- def reg_model(self, **kw):
- raise NotImplementedError('cannot call `reg_model` of base class SmartBox')
- def format_device_package(self, isTemp=False, **kw):
- def __generate_id(ids):
- i = 1
- while True:
- if str(i) in ids:
- i += 1
- else:
- return str(i)
- def __formart_ruleList():
- packageList = kw['serviceData']
- for item in packageList:
- item["sn"] = packageList.index(item)
- ids = [str(rule['id']) for rule in packageList if 'id' in rule]
- washConfig = {}
- for i, rule in enumerate(packageList):
- ruleId = str(rule.get('id', ''))
- if not ruleId:
- ruleId = __generate_id(ids)
- ids.append(ruleId)
- washConfig[ruleId] = {}
- if 'switch' in rule:
- washConfig[ruleId].update({'switch': rule['switch']})
- if 'billingMethod' in rule:
- washConfig[ruleId].update({'billingMethod': rule['billingMethod']})
- if 'price' in rule:
- washConfig[ruleId].update({'price': round(float(rule['price']), 2) or 0})
- if 'coins' in rule:
- washConfig[ruleId].update({'coins': round(float(rule['coins']), 2) or 0})
- if 'time' in rule:
- washConfig[ruleId].update({'time': rule['time'] or 0})
- if 'name' in rule:
- washConfig[ruleId].update({'name': rule['name'] or '套餐{}'.format(i + 1)})
- if 'unit' in rule:
- washConfig[ruleId].update({'unit': rule['unit']})
- if 'sn' in rule:
- washConfig[ruleId].update({'sn': rule['sn']})
- if 'autoStop' in rule:
- washConfig[ruleId]['autoStop'] = rule['autoStop']
- if 'autoRefund' in rule:
- washConfig[ruleId]['autoRefund'] = rule['autoRefund']
- if 'minAfterStartCoins' in rule:
- washConfig[ruleId]['minAfterStartCoins'] = rule['minAfterStartCoins']
- if 'minFee' in rule:
- washConfig[ruleId]['minFee'] = rule['minFee']
- if isTemp:
- pass
- # 检验部分
- if RMB(rule.get('price') or 0) > self.device.owner.maxPackagePrice:
- raise ServiceException(
- {'result': 0, 'description': '套餐( {} )金额超限'.format(rule['name']), 'payload': {}})
- return washConfig
- def __formart_displaySwitchs():
- return kw.get('displaySwitchs', {'displayCoinsSwitch': True,
- 'displayTimeSwitch': True,
- 'displayPriceSwitch': True,
- 'setPulseAble': False,
- 'setBasePriceAble': False})
- washConfig = __formart_ruleList()
- displaySwitchs = __formart_displaySwitchs()
- return washConfig, displaySwitchs
- def dealer_show_package(self, isTemp=False, **kw):
- def get_rule_list():
- if isTemp:
- config = self.device.get('tempWashConfig', {})
- else:
- config = self.device['washConfig']
- ruleList = []
- for packageId, rule in config.items():
- item = {
- 'id': packageId
- }
- if 'switch' in rule:
- item['switch'] = rule['switch']
- if 'name' in rule:
- item['name'] = rule['name']
- if 'billingMethod' in rule:
- item['billingMethod'] = rule['billingMethod']
- if 'coins' in rule:
- item['coins'] = rule['coins']
- if 'price' in rule:
- item['price'] = rule['price']
- if 'time' in rule:
- item['time'] = rule['time']
- if 'description' in rule:
- item['description'] = rule['description']
- if 'unit' in rule:
- item['unit'] = rule['unit']
- if 'imgList' in rule:
- item['imgList'] = rule['imgList']
- if 'sn' in rule:
- item['sn'] = rule['sn']
- if 'autoStop' in rule:
- item['autoStop'] = rule['autoStop']
- if 'minAfterStartCoins' in rule:
- item['minAfterStartCoins'] = rule['minAfterStartCoins']
- if 'minFee' in rule:
- item['minFee'] = rule['minFee']
- ruleList.append(item)
- return sorted(ruleList, key=lambda x: (x.get('sn'), x.get('id')))
- def get_display_switchs():
- if "displaySwitchs" in self.device["otherConf"]:
- displaySwitchs = self.device["otherConf"].get('displaySwitchs')
- else:
- displaySwitchs = {'displayCoinsSwitch': True,
- 'displayTimeSwitch': True,
- 'displayPriceSwitch': True,
- "setPulseAble": False,
- "setBasePriceAble": False}
- return displaySwitchs
- displaySwitchs = get_display_switchs()
- try:
- ruleList = get_rule_list()
- return {'ruleList': ruleList, 'displaySwitchs': displaySwitchs}
- except:
- return {'displaySwitchs': displaySwitchs, 'ruleList': []}
- def user_show_package(self, isTemp=False):
- group = self.device.group # type: GroupDict
- # 探测是否地址为免费活动组,默认为否
- is_free_service = group.is_free
- if isTemp:
- config = self.device.get('tempWashConfig', {})
- else:
- config = self.device['washConfig']
- if "displaySwitchs" in self.device.otherConf:
- displaySwitchs = self.device.otherConf.get('displaySwitchs')
- displaySwitchs = dict(filter(lambda x: "display" in x[0], displaySwitchs.items()))
- else:
- displaySwitchs = {
- 'displayCoinsSwitch': True,
- 'displayTimeSwitch': True,
- 'displayPriceSwitch': True
- }
- packages = []
- for packageId, rule in config.items():
- # 没有启用的套餐 直接掠过
- if not rule.get("switch", True):
- continue
- item = {
- 'id': packageId
- }
- if 'name' in rule:
- item['name'] = rule['name']
- if 'coins' in rule:
- item['coins'] = rule['coins']
- if 'price' in rule:
- item['price'] = rule['price']
- if 'time' in rule:
- item['time'] = rule['time']
- if 'description' in rule:
- item['description'] = rule['description']
- if 'unit' in rule:
- item['unit'] = rule['unit']
- if 'sn' in rule:
- item['sn'] = rule['sn']
- if 'minFee' in rule and rule['minFee'] and float(rule['minFee']) > 0:
- item.update({'minFee': rule.get('minFee')})
- if 'minAfterStartCoins' in rule and rule['minAfterStartCoins'] and float(rule['minAfterStartCoins']) > 0:
- item.update({'minAfterStartCoins': rule.get('minAfterStartCoins')})
- if is_free_service:
- item.update({'description': '当前处于免费时段'})
- item.update(displaySwitchs)
- packages.append(item)
- return sorted(packages, key=lambda x: (x.get('sn'), x.get('id')))
- def get_customize_score_unit(self):
- return None
- def do_heartbeat(self, value, ts):
- pass
-
-
- class OnlineSmartBox(SmartBox):
- def __init__(self, device):
- super(OnlineSmartBox, self).__init__(device)
- @testcase_point()
- def check_dev_status(self, attachParas = None):
- """
- 如果超过两个心跳周期没有报心跳,并且最后一次更新时间在2个小时内,需要从设备获取状态
- 否则以缓存状态为准。
- :param attachParas:
- :return:
- """
- if not self.device.need_fetch_online:
- raise ServiceException(
- {'result': 2, 'description': DeviceErrorCodeDesc.get(ErrorCode.DEVICE_CONN_FAIL)})
- if self.device.online == DeviceOnlineStatus.DEV_STATUS_ONLINE:
- retry = 3
- timeout = 12
- else:
- retry = 2
- timeout = 10
- operation_result = MessageSender.send(device = self.device, cmd = DeviceCmdCode.GET_DEVINFO, payload = {
- 'IMEI': self.device.devNo,
- 'fields': ['signal', 'pulse_open', 'board_volt', 'board_valid']
- }, timeout = timeout, retry = retry)
- if operation_result['rst'] != ErrorCode.DEVICE_SUCCESS:
- if operation_result['rst'] == ErrorCode.DEVICE_CONN_FAIL:
- raise ServiceException(
- {
- 'result': 2,
- 'description': DeviceErrorCodeDesc.get(ErrorCode.DEVICE_CONN_CHECK_FAIL)
- })
- else:
- raise ServiceException(
- {
- 'result': 2,
- 'description': u'检测设备状态失败({})'.format(operation_result['rst'])
- })
- else:
- if 'pulse_open' in operation_result and (not operation_result['pulse_open']):
- raise ServiceException(
- {
- 'result': 2,
- 'description': u'检测设备状态失败({})'.format(ErrorCode.PULSE_IS_CLOSE)
- })
- if 'board_valid' in operation_result and 'board_volt' in operation_result and operation_result[
- 'board_valid'] != 2:
- if operation_result['board_volt'] != operation_result['board_valid']:
- raise ServiceException(
- {
- 'result': 2,
- 'description': u'当前设备正在工作,请稍后再试'
- })
- def test(self, coins):
- now_time = datetime.datetime.now()
- return MessageSender.send(device = self.device, cmd = DeviceCmdCode.PAY_MONEY, payload = {
- 't': int(time.mktime(now_time.timetuple())),
- 'duration': coins * 60,
- 'app_pay': coins
- })
- @testcase_point()
- def start_device(self, package, openId, attachParas):
- pay_count = int(package['coins'])
- result = MessageSender.net_pay(self.device, pay_count, timeout = MQTT_TIMEOUT.START_DEVICE)
- if result['rst'] == ErrorCode.DEVICE_CONN_FAIL:
- raise DeviceNetworkTimeoutError()
- elif result['rst'] != ErrorCode.DEVICE_SUCCESS:
- logger.debug('OnlineSmartBox() failed to start, result was=%s' % (json.dumps(result),))
- raise ServiceException({'result': 2, 'description': DeviceErrorCodeDesc.get(result['rst'])})
- try:
- duration = self.get_duration(package)
- result['finishedTime'] = (int(time.time()) + duration)
- Device.update_dev_control_cache(self._device['devNo'],
- {
- 'status': Const.DEV_WORK_STATUS_WORKING,
- 'finishedTime': result['finishedTime']
- })
- except Exception as e:
- logger.exception('error = %s' % e)
- return result
- def get_total_coin(self):
- result = MessageSender.send(self.device, DeviceCmdCode.GET_DEVINFO,
- {'cmd': DeviceCmdCode.GET_DEVINFO, 'IMEI': self._device['devNo']})
- if result['rst'] != ErrorCode.DEVICE_SUCCESS:
- logger.debug('OnlineSmartBox() failed to get total coin, result was=%s' % (json.dumps(result),))
- description = u'当前设备信号弱没有响应,请您稍后重试。'
- raise ServiceException({'result': 2, 'description': description})
- if not result.has_key('total_coin'):
- raise ServiceException({'result': 2, 'description': u'当前设备暂时不支持获取总的硬币数目,待版本自动升级后,会支持'})
- return result['total_coin']
- def check_alarm(self, alarm):
- if alarm.faultCode == FAULT_CODE.OFFLINE:
- dev_info = MessageSender.send(device = self.device, cmd = DeviceCmdCode.GET_DEVINFO,
- payload = {'IMEI': self.device.devNo, 'fields': []},
- timeout = MQTT_TIMEOUT.SHORT)
- if dev_info['rst'] == 0:
- return u'设备状态检查在线,网络通畅,网络可能出现闪断'
- else:
- raise ServiceException({'result': 2, 'description': u'设备玩命也无法找到网络,设备可能不在线'})
- else:
- return u'无法检查该设备的告警状态,建议您用其他方式确认此告警是否正常'
- def async_update_portinfo_from_dev(self):
- return
- class MqttSmartBox(SmartBox):
- def check_dev_status(self, attachParas = None):
- """
- 如果超过两个心跳周期没有报心跳,并且最后一次更新时间在2个小时内,需要从设备获取状态
- 否则以缓存状态为准。
- :param attachParas:
- :return:
- """
- if not self.device.need_fetch_online:
- raise ServiceException(
- {'result': 2, 'description': DeviceErrorCodeDesc.get(ErrorCode.DEVICE_CONN_FAIL)})
- operation_result = MessageSender.send(device = self.device, cmd = DeviceCmdCode.GET_DEVINFO, payload = {
- 'IMEI': self.device.devNo,
- 'fields': ['signal']
- }, timeout = MQTT_TIMEOUT.TEST)
- if operation_result['rst'] != ErrorCode.DEVICE_SUCCESS:
- if operation_result['rst'] == ErrorCode.DEVICE_CONN_FAIL:
- raise ServiceException(
- {
- 'result': 2,
- 'description': DeviceErrorCodeDesc.get(ErrorCode.DEVICE_CONN_CHECK_FAIL)
- })
- else:
- raise ServiceException(
- {
- 'result': 2,
- 'description': u'检测设备状态失败({})'.format(operation_result['rst'])
- })
- def make_six_bytes_session_id():
- # 至少可以用到2050
- ts = long(time.time() * 100000)
- ts = ts + long(get_random_str(2, seq = string.digits))
- rv = fill_2_hexByte(hex(ts), 12)
- if rv[6:8] == 'AA':
- logger.debug('AA in session id.')
- return rv[0:6] + 'A9' + rv[8:]
- else:
- return rv
- def _record_error_times(devNo, portStr, box):
- """
- 记录 启动错误的 次数 超过次数报警处理 具体处理方式由协议函数实现
- :param devNo: 设备号
- :param portStr: 端口号
- :param box: SmartBox
- :return:
- """
- dev = Device.get_dev(devNo)
- errorTimes = dev.get("otherConf", {}).get("errorTimes", None)
- # 如果设备没有设置启动失败的次数,不做任何处理
- if not errorTimes:
- return
- times = Device.get_error_start_times(devNo, portStr)
- Device.set_error_start_times(devNo, portStr, times + 1)
- # 超过上限次数之后 缓存清除 触发响应操作
- if times + 1 >= errorTimes:
- box.handle_out_start_error(portStr)
- def _clear_error_times(devNo, portStr, box):
- """
- 启动成功之后 删除错误计数 并进行相关的处理
- :param devNo: 设备号
- :param portStr: 端口号
- :param box: SmartBox
- :return:
- """
- dev = Device.get_dev(devNo)
- errorTimes = dev.get("otherConf", {}).get("errorTimes", None)
- # 如果设备没有设置启动失败的次数,不做任何处理
- if not errorTimes:
- return
- times = Device.get_error_start_times(devNo, portStr)
- Device.delete_error_start_times(devNo, portStr)
- if times >= errorTimes:
- box.handle_clear_start_error(portStr)
- def start_error_timer(missMessages=None):
- """
- 装饰器 用来装饰start_device函数,记录失败次数 并且超过一定次数的时候做响应的处理
- :param missMessages: 忽略的消息列表 ex [u"设备端口已经被占用,请稍后再试"]
- :return:
- """
- if missMessages is None or not isinstance(missMessages, list):
- missMessages = list()
- def outFunc(func):
- @wraps(func)
- def inFunc(box, package, openId, attachParas):
- chargeIndex = attachParas.get("chargeIndex", 0)
- try:
- result = func(box, package, openId, attachParas)
- except ServiceException as e:
- message = e.result.get("description")
- if message not in missMessages:
- _record_error_times(box._device["devNo"], chargeIndex, box)
- raise e
- else:
- _clear_error_times(box._device["devNo"], chargeIndex, box)
- return result
- return inFunc
- return outFunc
- # 订单编号,需要根据云快充的协议生成:生成规则为 格式桩号(7bytes)+枪号(1byte)+年月日时分秒(6bytes)+自增序号
- #(2bytes);示例:32010600019236 01 200106180342 3060
- def make_cartcp_order_no(devNo, portIndex):
- increCount = 0
- try:
- increCount = caches['devmgr'].incr('increCount',1)
- except Exception ,e:
- caches['devmgr'].set('increCount',0)
-
- hexIndex = fill_2_hexByte(hex(int(portIndex)),2)
- hexIncre = fill_2_hexByte(hex(increCount), 4)
- nowTime = datetime.datetime.now()
- year = nowTime.year - 2000
- strTime = str(year) + nowTime.strftime('%m%d%H%M%S')
- orderNo = '%s%s%s%s' % (devNo,hexIndex,strTime,hexIncre)
- return orderNo
- def make_dianchuan_order_no(devNo):
- increCount = 0
- try:
- increCount = caches['devmgr'].incr('increCount',1)
- except Exception ,e:
- caches['devmgr'].set('increCount',0)
-
- hexIncre = fill_2_hexByte(hex(increCount), 4)
- nowTime = datetime.datetime.now()
- year = nowTime.year - 2000
- strTime = str(year) + nowTime.strftime('%m%d%H%M%S')
- orderNo = '%s%s%s' % (devNo,strTime,hexIncre)
- return orderNo
- class SendManager(object):
- def __init__(self, sender = MessageSender, visitor = "manager"):
- self._sender = sender
- self._rst = None
- self._tError = None
- self._sError = None
- self._cError = None
- add_exc_type = getattr(self, "add_{}_exc_type".format(visitor), self.add_manager_exc_type)
- add_exc_type()
- def __enter__(self):
- return self
- def __exit__(self, exc_type, exc_val, exc_tb):
- if self.rst == 0:
- return
- elif self.rst == -1:
- raise self._tError
- elif self.rst == 1:
- raise self._sError
- else:
- raise self._cError
- def add_client_exc_type(self):
- setattr(self, "_tError", ClientServiceTimeOutException())
- setattr(self, "_sError", ClientServiceSerialException())
- setattr(self, "_cError", ServiceException())
- def add_manager_exc_type(self):
- setattr(self, "_tError", ManagerServiceTimeOutException())
- setattr(self, "_sError", ManagerServiceSerialException())
- setattr(self, "_cError", ServiceException())
- def add_tester_exc_type(self):
- setattr(self, "_tError", TestTimeOutError())
- setattr(self, "_sError", TestSerialError())
- setattr(self, "_cError", TestError())
- def send(self, *args, **kwargs):
- return self._sender.send(*args, **kwargs)
- @property
- def rst(self):
- return self._rst
- @rst.setter
- def rst(self, result):
- self._rst = result.get("rst")
|